1 // NpgsqlTypes.NpgsqlTypesHelper.cs
4 // Francisco Jr. (fxjrlists@yahoo.com.br)
6 // Copyright (C) 2002 The Npgsql Development Team
7 // npgsql-general@gborg.postgresql.org
8 // http://gborg.postgresql.org/project/npgsql/projdisplay.php
10 // This library is free software; you can redistribute it and/or
11 // modify it under the terms of the GNU Lesser General Public
12 // License as published by the Free Software Foundation; either
13 // version 2.1 of the License, or (at your option) any later version.
15 // This library is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 // Lesser General Public License for more details.
20 // You should have received a copy of the GNU Lesser General Public
21 // License along with this library; if not, write to the Free Software
22 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 using System
.Collections
;
26 using System
.Globalization
;
30 using System
.Resources
;
37 /// This class contains helper methods for type conversion between
38 /// the .Net type system and postgresql.
40 internal abstract class NpgsqlTypesHelper
42 // Logging related values
43 private static readonly String CLASSNAME
= "NpgsqlTypesHelper";
44 private static ResourceManager resman
= new ResourceManager(typeof(NpgsqlTypesHelper
));
47 /// A cache of basic datatype mappings keyed by server version. This way we don't
48 /// have to load the basic type mappings for every connection.
50 private static Hashtable BackendTypeMappingCache
= new Hashtable();
51 private static NpgsqlNativeTypeMapping NativeTypeMapping
= null;
55 /// Find a NpgsqlNativeTypeInfo in the default types map that can handle objects
56 /// of the given NpgsqlDbType.
58 public static NpgsqlNativeTypeInfo
GetNativeTypeInfo(NpgsqlDbType NpgsqlDbType
)
60 NpgsqlEventLog
.LogMethodEnter(LogLevel
.Debug
, CLASSNAME
, "GetBackendTypeNameFromNpgsqlDbType");
62 VerifyDefaultTypesMap();
63 return NativeTypeMapping
[NpgsqlDbType
];
67 /// Find a NpgsqlNativeTypeInfo in the default types map that can handle objects
68 /// of the given DbType.
70 public static NpgsqlNativeTypeInfo
GetNativeTypeInfo(DbType DbType
)
72 NpgsqlEventLog
.LogMethodEnter(LogLevel
.Debug
, CLASSNAME
, "GetBackendTypeNameFromNpgsqlDbType");
74 VerifyDefaultTypesMap();
75 return NativeTypeMapping
[DbType
];
81 /// Find a NpgsqlNativeTypeInfo in the default types map that can handle objects
82 /// of the given System.Type.
84 public static NpgsqlNativeTypeInfo
GetNativeTypeInfo(Type Type
)
86 NpgsqlEventLog
.LogMethodEnter(LogLevel
.Debug
, CLASSNAME
, "GetBackendTypeNameFromNpgsqlDbType");
88 VerifyDefaultTypesMap();
89 return NativeTypeMapping
[Type
];
93 // Not sure what to do with this one. I don't believe we ever ask for a binary
94 // formatting, so this shouldn't even be used right now.
95 // At some point this will need to be merged into the type converter system somehow?
96 public static Object
ConvertBackendBytesToSystemType(NpgsqlBackendTypeInfo TypeInfo
, Byte
[] data
, Encoding encoding
, Int32 fieldValueSize
, Int32 typeModifier
)
98 NpgsqlEventLog
.LogMethodEnter(LogLevel
.Debug
, CLASSNAME
, "ConvertBackendBytesToStytemType");
101 // We are never guaranteed to know about every possible data type the server can send us.
102 // When we encounter an unknown type, we punt and return the data without modification.
103 if (TypeInfo
== null)
106 switch (TypeInfo
.NpgsqlDbType
)
108 case NpgsqlDbType
.Bytea
:
110 /*case NpgsqlDbType.Boolean:
111 return BitConverter.ToBoolean(data, 0);
112 case NpgsqlDbType.DateTime:
113 return DateTime.MinValue.AddTicks(IPAddress.NetworkToHostOrder(BitConverter.ToInt64(data, 0)));
115 case NpgsqlDbType.Int16:
116 return IPAddress.NetworkToHostOrder(BitConverter.ToInt16(data, 0));
117 case NpgsqlDbType.Int32:
118 return IPAddress.NetworkToHostOrder(BitConverter.ToInt32(data, 0));
119 case NpgsqlDbType.Int64:
120 return IPAddress.NetworkToHostOrder(BitConverter.ToInt64(data, 0));
121 case NpgsqlDbType.String:
122 case NpgsqlDbType.AnsiString:
123 case NpgsqlDbType.StringFixedLength:
124 return encoding.GetString(data, 0, fieldValueSize);*/
126 throw new InvalidCastException("Type not supported in binary format");
133 /// This method is responsible to convert the string received from the backend
134 /// to the corresponding NpgsqlType.
135 /// The given TypeInfo is called upon to do the conversion.
136 /// If no TypeInfo object is provided, no conversion is performed.
138 public static Object
ConvertBackendStringToSystemType(NpgsqlBackendTypeInfo TypeInfo
, String data
, Int16 typeSize
, Int32 typeModifier
)
140 NpgsqlEventLog
.LogMethodEnter(LogLevel
.Debug
, CLASSNAME
, "ConvertBackendStringToSystemType");
142 if (TypeInfo
!= null) {
143 return TypeInfo
.ConvertToNative(data
, typeSize
, typeModifier
);
150 /// Create the one and only native to backend type map.
151 /// This map is used when formatting native data
152 /// types to backend representations.
154 private static void VerifyDefaultTypesMap()
157 if (NativeTypeMapping
!= null) {
161 NativeTypeMapping
= new NpgsqlNativeTypeMapping();
164 // Conflicting types should have mapped first the non default mappings.
165 // For example, char, varchar and text map to DbType.String. As the most
166 // common is to use text with string, it has to be the last mapped, in order
167 // to type mapping has the last entry, in this case, text, as the map value
168 // for DbType.String.
170 NativeTypeMapping
.AddType("refcursor", NpgsqlDbType
.Refcursor
, DbType
.String
, true, null);
172 NativeTypeMapping
.AddType("char", NpgsqlDbType
.Char
, DbType
.String
, true, null);
174 NativeTypeMapping
.AddType("varchar", NpgsqlDbType
.Varchar
, DbType
.String
, true, null);
176 NativeTypeMapping
.AddType("text", NpgsqlDbType
.Text
, DbType
.String
, true, null);
178 NativeTypeMapping
.AddDbTypeAlias("text", DbType
.StringFixedLength
);
179 NativeTypeMapping
.AddDbTypeAlias("text", DbType
.AnsiString
);
180 NativeTypeMapping
.AddDbTypeAlias("text", DbType
.AnsiStringFixedLength
);
181 NativeTypeMapping
.AddTypeAlias("text", typeof(String
));
184 NativeTypeMapping
.AddType("bytea", NpgsqlDbType
.Bytea
, DbType
.Binary
, true,
185 new ConvertNativeToBackendHandler(BasicNativeToBackendTypeConverter
.ToBinary
));
187 NativeTypeMapping
.AddTypeAlias("bytea", typeof(Byte
[]));
189 NativeTypeMapping
.AddType("bit", NpgsqlDbType
.Bit
, DbType
.Boolean
, true,
190 new ConvertNativeToBackendHandler(BasicNativeToBackendTypeConverter
.ToBit
));
192 NativeTypeMapping
.AddType("bool", NpgsqlDbType
.Boolean
, DbType
.Boolean
, false,
193 new ConvertNativeToBackendHandler(BasicNativeToBackendTypeConverter
.ToBoolean
));
195 NativeTypeMapping
.AddTypeAlias("bool", typeof(Boolean
));
197 NativeTypeMapping
.AddType("int2", NpgsqlDbType
.Smallint
, DbType
.Int16
, false,
200 NativeTypeMapping
.AddTypeAlias("int2", typeof(Int16
));
202 NativeTypeMapping
.AddDbTypeAlias("int2", DbType
.Byte
);
204 NativeTypeMapping
.AddTypeAlias("int2", typeof(Byte
));
206 NativeTypeMapping
.AddType("int4", NpgsqlDbType
.Integer
, DbType
.Int32
, false,
209 NativeTypeMapping
.AddTypeAlias("int4", typeof(Int32
));
211 NativeTypeMapping
.AddType("int8", NpgsqlDbType
.Bigint
, DbType
.Int64
, false,
214 NativeTypeMapping
.AddTypeAlias("int8", typeof(Int64
));
216 NativeTypeMapping
.AddType("float4", NpgsqlDbType
.Real
, DbType
.Single
, true,
219 NativeTypeMapping
.AddTypeAlias("float4", typeof(Single
));
221 NativeTypeMapping
.AddType("float8", NpgsqlDbType
.Double
, DbType
.Double
, true,
224 NativeTypeMapping
.AddTypeAlias("float8", typeof(Double
));
226 NativeTypeMapping
.AddType("numeric", NpgsqlDbType
.Numeric
, DbType
.Decimal
, false,
229 NativeTypeMapping
.AddTypeAlias("numeric", typeof(Decimal
));
231 NativeTypeMapping
.AddType("currency", NpgsqlDbType
.Money
, DbType
.Currency
, true,
232 new ConvertNativeToBackendHandler(BasicNativeToBackendTypeConverter
.ToMoney
));
234 NativeTypeMapping
.AddType("date", NpgsqlDbType
.Date
, DbType
.Date
, true,
235 new ConvertNativeToBackendHandler(BasicNativeToBackendTypeConverter
.ToDate
));
237 NativeTypeMapping
.AddType("time", NpgsqlDbType
.Time
, DbType
.Time
, true,
238 new ConvertNativeToBackendHandler(BasicNativeToBackendTypeConverter
.ToTime
));
240 NativeTypeMapping
.AddType("timestamp", NpgsqlDbType
.Timestamp
, DbType
.DateTime
, true,
241 new ConvertNativeToBackendHandler(BasicNativeToBackendTypeConverter
.ToDateTime
));
243 NativeTypeMapping
.AddTypeAlias("timestamp", typeof(DateTime
));
245 NativeTypeMapping
.AddType("timestamptz", NpgsqlDbType
.TimestampTZ
, DbType
.DateTime
, true,
246 new ConvertNativeToBackendHandler(BasicNativeToBackendTypeConverter
.ToDateTime
));
248 NativeTypeMapping
.AddType("point", NpgsqlDbType
.Point
, DbType
.Object
, true,
249 new ConvertNativeToBackendHandler(ExtendedNativeToBackendTypeConverter
.ToPoint
));
251 NativeTypeMapping
.AddTypeAlias("point", typeof(NpgsqlPoint
));
253 NativeTypeMapping
.AddType("box", NpgsqlDbType
.Box
, DbType
.Object
, true,
254 new ConvertNativeToBackendHandler(ExtendedNativeToBackendTypeConverter
.ToBox
));
256 NativeTypeMapping
.AddTypeAlias("box", typeof(NpgsqlBox
));
258 NativeTypeMapping
.AddType("lseg", NpgsqlDbType
.LSeg
, DbType
.Object
, true,
259 new ConvertNativeToBackendHandler(ExtendedNativeToBackendTypeConverter
.ToLSeg
));
261 NativeTypeMapping
.AddTypeAlias("lseg", typeof(NpgsqlLSeg
));
263 NativeTypeMapping
.AddType("path", NpgsqlDbType
.Path
, DbType
.Object
, true,
264 new ConvertNativeToBackendHandler(ExtendedNativeToBackendTypeConverter
.ToPath
));
266 NativeTypeMapping
.AddTypeAlias("path", typeof(NpgsqlPath
));
268 NativeTypeMapping
.AddType("polygon", NpgsqlDbType
.Polygon
, DbType
.Object
, true,
269 new ConvertNativeToBackendHandler(ExtendedNativeToBackendTypeConverter
.ToPolygon
));
271 NativeTypeMapping
.AddTypeAlias("polygon", typeof(NpgsqlPolygon
));
273 NativeTypeMapping
.AddType("circle", NpgsqlDbType
.Circle
, DbType
.Object
, true,
274 new ConvertNativeToBackendHandler(ExtendedNativeToBackendTypeConverter
.ToCircle
));
276 NativeTypeMapping
.AddTypeAlias("circle", typeof(NpgsqlCircle
));
278 NativeTypeMapping
.AddType("inet", NpgsqlDbType
.Inet
, DbType
.Object
, true,
279 new ConvertNativeToBackendHandler(ExtendedNativeToBackendTypeConverter
.ToIPAddress
));
281 NativeTypeMapping
.AddTypeAlias("inet", typeof(IPAddress
));
282 NativeTypeMapping
.AddTypeAlias("inet", typeof(NpgsqlInet
));
287 /// This method creates (or retrieves from cache) a mapping between type and OID
288 /// of all natively supported postgresql data types.
289 /// This is needed as from one version to another, this mapping can be changed and
290 /// so we avoid hardcoding them.
292 /// <returns>NpgsqlTypeMapping containing all known data types. The mapping must be
293 /// cloned before it is modified because it is cached; changes made by one connection may
294 /// effect another connection.</returns>
295 public static NpgsqlBackendTypeMapping
CreateAndLoadInitialTypesMapping(NpgsqlConnector conn
)
297 NpgsqlEventLog
.LogMethodEnter(LogLevel
.Debug
, CLASSNAME
, "LoadTypesMapping");
299 // [TODO] Verify another way to get higher concurrency.
302 // Check the cache for an initial types map.
303 NpgsqlBackendTypeMapping oidToNameMapping
= (NpgsqlBackendTypeMapping
) BackendTypeMappingCache
[conn
.ServerVersion
];
305 if (oidToNameMapping
!= null)
307 return oidToNameMapping
;
310 // Not in cache, create a new one.
311 oidToNameMapping
= new NpgsqlBackendTypeMapping();
313 // Create a list of all natively supported postgresql data types.
314 NpgsqlBackendTypeInfo
[] TypeInfoList
= new NpgsqlBackendTypeInfo
[]
316 new NpgsqlBackendTypeInfo(0, "unknown", NpgsqlDbType
.Text
, DbType
.String
, typeof(String
),
319 new NpgsqlBackendTypeInfo(0, "refcursor", NpgsqlDbType
.Refcursor
, DbType
.String
, typeof(String
),
322 new NpgsqlBackendTypeInfo(0, "char", NpgsqlDbType
.Char
, DbType
.String
, typeof(String
),
325 new NpgsqlBackendTypeInfo(0, "bpchar", NpgsqlDbType
.Text
, DbType
.String
, typeof(String
),
328 new NpgsqlBackendTypeInfo(0, "varchar", NpgsqlDbType
.Varchar
, DbType
.String
, typeof(String
),
331 new NpgsqlBackendTypeInfo(0, "text", NpgsqlDbType
.Text
, DbType
.String
, typeof(String
),
334 new NpgsqlBackendTypeInfo(0, "name", NpgsqlDbType
.Text
, DbType
.String
, typeof(String
),
337 new NpgsqlBackendTypeInfo(0, "bytea", NpgsqlDbType
.Bytea
, DbType
.Binary
, typeof(Byte
[]),
338 new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter
.ToBinary
)),
340 new NpgsqlBackendTypeInfo(0, "bit", NpgsqlDbType
.Bit
, DbType
.Boolean
, typeof(Boolean
),
341 new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter
.ToBit
)),
343 new NpgsqlBackendTypeInfo(0, "bool", NpgsqlDbType
.Boolean
, DbType
.Boolean
, typeof(Boolean
),
344 new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter
.ToBoolean
)),
347 new NpgsqlBackendTypeInfo(0, "int2", NpgsqlDbType
.Smallint
, DbType
.Int16
, typeof(Int16
),
350 new NpgsqlBackendTypeInfo(0, "int4", NpgsqlDbType
.Integer
, DbType
.Int32
, typeof(Int32
),
353 new NpgsqlBackendTypeInfo(0, "int8", NpgsqlDbType
.Bigint
, DbType
.Int64
, typeof(Int64
),
356 new NpgsqlBackendTypeInfo(0, "oid", NpgsqlDbType
.Bigint
, DbType
.Int64
, typeof(Int64
),
360 new NpgsqlBackendTypeInfo(0, "float4", NpgsqlDbType
.Real
, DbType
.Single
, typeof(Single
),
363 new NpgsqlBackendTypeInfo(0, "float8", NpgsqlDbType
.Double
, DbType
.Double
, typeof(Double
),
366 new NpgsqlBackendTypeInfo(0, "numeric", NpgsqlDbType
.Numeric
, DbType
.Decimal
, typeof(Decimal
),
369 new NpgsqlBackendTypeInfo(0, "inet", NpgsqlDbType
.Inet
, DbType
.Object
, typeof(NpgsqlInet
), new ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter
.ToInet
)),
371 new NpgsqlBackendTypeInfo(0, "money", NpgsqlDbType
.Money
, DbType
.Decimal
, typeof(Decimal
),
372 new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter
.ToMoney
)),
375 new NpgsqlBackendTypeInfo(0, "date", NpgsqlDbType
.Date
, DbType
.Date
, typeof(DateTime
),
376 new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter
.ToDate
)),
378 new NpgsqlBackendTypeInfo(0, "time", NpgsqlDbType
.Time
, DbType
.Time
, typeof(DateTime
),
379 new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter
.ToTime
)),
381 new NpgsqlBackendTypeInfo(0, "timetz", NpgsqlDbType
.Time
, DbType
.Time
, typeof(DateTime
),
382 new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter
.ToTime
)),
384 new NpgsqlBackendTypeInfo(0, "timestamp", NpgsqlDbType
.Timestamp
, DbType
.DateTime
, typeof(DateTime
),
385 new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter
.ToDateTime
)),
387 new NpgsqlBackendTypeInfo(0, "timestamptz", NpgsqlDbType
.Timestamp
, DbType
.DateTime
, typeof(DateTime
),
388 new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter
.ToDateTime
)),
391 new NpgsqlBackendTypeInfo(0, "point", NpgsqlDbType
.Point
, DbType
.Object
, typeof(NpgsqlPoint
),
392 new ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter
.ToPoint
)),
394 new NpgsqlBackendTypeInfo(0, "lseg", NpgsqlDbType
.LSeg
, DbType
.Object
, typeof(NpgsqlLSeg
),
395 new ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter
.ToLSeg
)),
397 new NpgsqlBackendTypeInfo(0, "path", NpgsqlDbType
.Path
, DbType
.Object
, typeof(NpgsqlPath
),
398 new ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter
.ToPath
)),
400 new NpgsqlBackendTypeInfo(0, "box", NpgsqlDbType
.Box
, DbType
.Object
, typeof(NpgsqlBox
),
401 new ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter
.ToBox
)),
403 new NpgsqlBackendTypeInfo(0, "circle", NpgsqlDbType
.Circle
, DbType
.Object
, typeof(NpgsqlCircle
),
404 new ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter
.ToCircle
)),
406 new NpgsqlBackendTypeInfo(0, "polygon", NpgsqlDbType
.Polygon
, DbType
.Object
, typeof(NpgsqlPolygon
),
407 new ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter
.ToPolygon
)),
410 // Attempt to map each type info in the list to an OID on the backend and
411 // add each mapped type to the new type mapping object.
412 LoadTypesMappings(conn
, oidToNameMapping
, TypeInfoList
);
414 // Add this mapping to the per-server-version cache so we don't have to
415 // do these expensive queries on every connection startup.
416 BackendTypeMappingCache
.Add(conn
.ServerVersion
, oidToNameMapping
);
418 return oidToNameMapping
;
425 /// Attempt to map types by issuing a query against pg_type.
426 /// This function takes a list of NpgsqlTypeInfo and attempts to resolve the OID field
427 /// of each by querying pg_type. If the mapping is found, the type info object is
428 /// updated (OID) and added to the provided NpgsqlTypeMapping object.
430 /// <param name="conn">NpgsqlConnector to send query through.</param>
431 /// <param name="TypeMappings">Mapping object to add types too.</param>
432 /// <param name="TypeInfoList">List of types that need to have OID's mapped.</param>
433 public static void LoadTypesMappings(NpgsqlConnector conn
, NpgsqlBackendTypeMapping TypeMappings
, IList TypeInfoList
)
435 StringBuilder InList
= new StringBuilder();
436 Hashtable NameIndex
= new Hashtable();
438 // Build a clause for the SELECT statement.
439 // Build a name->typeinfo mapping so we can match the results of the query
440 /// with the list of type objects efficiently.
441 foreach (NpgsqlBackendTypeInfo TypeInfo
in TypeInfoList
) {
442 NameIndex
.Add(TypeInfo
.Name
, TypeInfo
);
443 InList
.AppendFormat("{0}'{1}'", ((InList
.Length
> 0) ? ", " : ""), TypeInfo
.Name
);
446 if (InList
.Length
== 0) {
450 NpgsqlCommand command
= new NpgsqlCommand("SELECT oid, typname FROM pg_type WHERE typname IN (" + InList
.ToString() + ")", conn
);
451 NpgsqlDataReader dr
= command
.ExecuteReader();
454 NpgsqlBackendTypeInfo TypeInfo
= (NpgsqlBackendTypeInfo
)NameIndex
[dr
[1].ToString()];
456 TypeInfo
._OID
= Convert
.ToInt32(dr
[0]);
458 TypeMappings
.AddType(TypeInfo
);
464 /// Delegate called to convert the given backend data to its native representation.
466 internal delegate Object
ConvertBackendToNativeHandler(NpgsqlBackendTypeInfo TypeInfo
, String BackendData
, Int16 TypeSize
, Int32 TypeModifier
);
468 /// Delegate called to convert the given native data to its backand representation.
470 internal delegate String
ConvertNativeToBackendHandler(NpgsqlNativeTypeInfo TypeInfo
, Object NativeData
);
473 /// Represents a backend data type.
474 /// This class can be called upon to convert a backend field representation to a native object.
476 internal class NpgsqlBackendTypeInfo
478 private ConvertBackendToNativeHandler _ConvertBackendToNative
;
481 private String _Name
;
482 private NpgsqlDbType _NpgsqlDbType
;
483 private DbType _DbType
;
487 /// Construct a new NpgsqlTypeInfo with the given attributes and conversion handlers.
489 /// <param name="OID">Type OID provided by the backend server.</param>
490 /// <param name="Name">Type name provided by the backend server.</param>
491 /// <param name="NpgsqlDbType">NpgsqlDbType</param>
492 /// <param name="Type">System type to convert fields of this type to.</param>
493 /// <param name="ConvertBackendToNative">Data conversion handler.</param>
494 public NpgsqlBackendTypeInfo(Int32 OID
, String Name
, NpgsqlDbType NpgsqlDbType
, DbType DbType
, Type Type
, ConvertBackendToNativeHandler ConvertBackendToNative
)
498 _NpgsqlDbType
= NpgsqlDbType
;
501 _ConvertBackendToNative
= ConvertBackendToNative
;
505 /// Type OID provided by the backend server.
513 /// Type name provided by the backend server.
516 { get { return _Name; }
}
521 public NpgsqlDbType NpgsqlDbType
522 { get { return _NpgsqlDbType; }
}
528 { get { return _DbType; }
}
531 /// System type to convert fields of this type to.
534 { get { return _Type; }
}
537 /// Perform a data conversion from a backend representation to
540 /// <param name="BackendData">Data sent from the backend.</param>
541 /// <param name="TypeModifier">Type modifier field sent from the backend.</param>
542 public Object
ConvertToNative(String BackendData
, Int16 TypeSize
, Int32 TypeModifier
)
544 if (_ConvertBackendToNative
!= null) {
545 return _ConvertBackendToNative(this, BackendData
, TypeSize
, TypeModifier
);
548 return Convert
.ChangeType(BackendData
, Type
, CultureInfo
.InvariantCulture
);
557 /// Represents a backend data type.
558 /// This class can be called upon to convert a native object to its backend field representation,
560 internal class NpgsqlNativeTypeInfo
562 private static NumberFormatInfo ni
;
564 private ConvertNativeToBackendHandler _ConvertNativeToBackend
;
566 private String _Name
;
567 private NpgsqlDbType _NpgsqlDbType
;
568 private DbType _DbType
;
569 private Boolean _Quote
;
570 private Boolean _UseSize
;
572 static NpgsqlNativeTypeInfo()
574 ni
= (NumberFormatInfo
) CultureInfo
.InvariantCulture
.NumberFormat
.Clone();
575 ni
.NumberDecimalDigits
= 15;
579 /// Construct a new NpgsqlTypeInfo with the given attributes and conversion handlers.
581 /// <param name="OID">Type OID provided by the backend server.</param>
582 /// <param name="Name">Type name provided by the backend server.</param>
583 /// <param name="NpgsqlDbType">NpgsqlDbType</param>
584 /// <param name="Type">System type to convert fields of this type to.</param>
585 /// <param name="ConvertBackendToNative">Data conversion handler.</param>
586 /// <param name="ConvertNativeToBackend">Data conversion handler.</param>
587 public NpgsqlNativeTypeInfo(String Name
, NpgsqlDbType NpgsqlDbType
, DbType DbType
, Boolean Quote
, ConvertNativeToBackendHandler ConvertNativeToBackend
)
590 _NpgsqlDbType
= NpgsqlDbType
;
593 _ConvertNativeToBackend
= ConvertNativeToBackend
;
596 // The only parameters types which use length currently supported are char and varchar. Check for them.
598 if ( (NpgsqlDbType
== NpgsqlDbType
.Char
)
599 || (NpgsqlDbType
== NpgsqlDbType
.Varchar
))
607 /// Type name provided by the backend server.
610 { get { return _Name; }
}
615 public NpgsqlDbType NpgsqlDbType
616 { get { return _NpgsqlDbType; }
}
622 { get { return _DbType; }
}
629 { get { return _Quote; }
}
632 /// Use parameter size information.
634 public Boolean UseSize
635 { get { return _UseSize; }
}
639 /// Perform a data conversion from a native object to
640 /// a backend representation.
641 /// DBNull and null values are handled differently depending if a plain query is used
644 /// <param name="NativeData">Native .NET object to be converted.</param>
645 /// <param name="ForExtendedQuery">Flag indicating if the conversion has to be done for
646 /// plain queries or extended queries</param>
647 public String
ConvertToBackend(Object NativeData
, Boolean ForExtendedQuery
)
649 if (ForExtendedQuery
)
650 return ConvertToBackendExtendedQuery(NativeData
);
652 return ConvertToBackendPlainQuery(NativeData
);
656 private String
ConvertToBackendPlainQuery(Object NativeData
)
658 if ((NativeData
== DBNull
.Value
) || (NativeData
== null))
659 return "NULL"; // Plain queries exptects null values as string NULL.
661 if (_ConvertNativeToBackend
!= null)
662 return (this.Quote
? QuoteString(_ConvertNativeToBackend(this, NativeData
)) : _ConvertNativeToBackend(this, NativeData
));
667 if (NativeData
is System
.Enum
)
669 // Do a special handling of Enum values.
670 // Translate enum value to its underlying type.
671 return QuoteString((String
)Convert
.ChangeType(Enum
.Format(NativeData
.GetType(), NativeData
, "d"), typeof(String
), CultureInfo
.InvariantCulture
));
673 else if (NativeData
is IFormattable
)
675 return (this.Quote
? QuoteString(((IFormattable
) NativeData
).ToString(null, ni
).Replace("'", "''").Replace("\\", "\\\\")) :
676 ((IFormattable
) NativeData
).ToString(null, ni
).Replace("'", "''").Replace("\\", "\\\\"));
680 // Do special handling of strings when in simple query. Escape quotes and backslashes.
681 return (this.Quote
? QuoteString(NativeData
.ToString().Replace("'", "''").Replace("\\", "\\\\").Replace("\0", "\\0")) :
682 NativeData
.ToString().Replace("'", "''").Replace("\\", "\\\\").Replace("\0", "\\0"));
688 private String
ConvertToBackendExtendedQuery(Object NativeData
)
690 if ((NativeData
== DBNull
.Value
) || (NativeData
== null))
691 return null; // Extended query expects null values be represented as null.
693 if (_ConvertNativeToBackend
!= null)
694 return _ConvertNativeToBackend(this, NativeData
);
697 if (NativeData
is System
.Enum
)
699 // Do a special handling of Enum values.
700 // Translate enum value to its underlying type.
701 return (String
)Convert
.ChangeType(Enum
.Format(NativeData
.GetType(), NativeData
, "d"), typeof(String
), CultureInfo
.InvariantCulture
);
703 else if (NativeData
is IFormattable
)
705 return ((IFormattable
)NativeData
).ToString(null, ni
);
709 return NativeData
.ToString();
715 private static String
QuoteString(String S
)
717 return String
.Format("E'{0}'", S
);
723 /// Provide mapping between type OID, type name, and a NpgsqlBackendTypeInfo object that represents it.
725 internal class NpgsqlBackendTypeMapping
727 private Hashtable OIDIndex
;
728 private Hashtable NameIndex
;
731 /// Construct an empty mapping.
733 public NpgsqlBackendTypeMapping()
735 OIDIndex
= new Hashtable();
736 NameIndex
= new Hashtable();
742 private NpgsqlBackendTypeMapping(NpgsqlBackendTypeMapping Other
)
744 OIDIndex
= (Hashtable
)Other
.OIDIndex
.Clone();
745 NameIndex
= (Hashtable
)Other
.NameIndex
.Clone();
749 /// Add the given NpgsqlBackendTypeInfo to this mapping.
751 public void AddType(NpgsqlBackendTypeInfo T
)
753 if (OIDIndex
.Contains(T
.OID
)) {
754 throw new Exception("Type already mapped");
758 NameIndex
[T
.Name
] = T
;
762 /// Add a new NpgsqlBackendTypeInfo with the given attributes and conversion handlers to this mapping.
764 /// <param name="OID">Type OID provided by the backend server.</param>
765 /// <param name="Name">Type name provided by the backend server.</param>
766 /// <param name="NpgsqlDbType">NpgsqlDbType</param>
767 /// <param name="Type">System type to convert fields of this type to.</param>
768 /// <param name="ConvertBackendToNative">Data conversion handler.</param>
769 public void AddType(Int32 OID
, String Name
, NpgsqlDbType NpgsqlDbType
, DbType DbType
, Type Type
,
770 ConvertBackendToNativeHandler BackendConvert
)
772 AddType(new NpgsqlBackendTypeInfo(OID
, Name
, NpgsqlDbType
, DbType
, Type
, BackendConvert
));
776 /// Get the number of type infos held.
779 { get { return NameIndex.Count; }
}
782 /// Retrieve the NpgsqlBackendTypeInfo with the given backend type OID, or null if none found.
784 public NpgsqlBackendTypeInfo
this [Int32 OID
]
788 return (NpgsqlBackendTypeInfo
)OIDIndex
[OID
];
793 /// Retrieve the NpgsqlBackendTypeInfo with the given backend type name, or null if none found.
795 public NpgsqlBackendTypeInfo
this [String Name
]
799 return (NpgsqlBackendTypeInfo
)NameIndex
[Name
];
804 /// Make a shallow copy of this type mapping.
806 public NpgsqlBackendTypeMapping
Clone()
808 return new NpgsqlBackendTypeMapping(this);
812 /// Determine if a NpgsqlBackendTypeInfo with the given backend type OID exists in this mapping.
814 public Boolean
ContainsOID(Int32 OID
)
816 return OIDIndex
.ContainsKey(OID
);
820 /// Determine if a NpgsqlBackendTypeInfo with the given backend type name exists in this mapping.
822 public Boolean
ContainsName(String Name
)
824 return NameIndex
.ContainsKey(Name
);
831 /// Provide mapping between type Type, NpgsqlDbType and a NpgsqlNativeTypeInfo object that represents it.
833 internal class NpgsqlNativeTypeMapping
835 private Hashtable NameIndex
;
836 private Hashtable NpgsqlDbTypeIndex
;
837 private Hashtable DbTypeIndex
;
838 private Hashtable TypeIndex
;
841 /// Construct an empty mapping.
843 public NpgsqlNativeTypeMapping()
845 NameIndex
= new Hashtable();
846 NpgsqlDbTypeIndex
= new Hashtable();
847 DbTypeIndex
= new Hashtable();
848 TypeIndex
= new Hashtable();
852 /// Add the given NpgsqlNativeTypeInfo to this mapping.
854 public void AddType(NpgsqlNativeTypeInfo T
)
856 if (NameIndex
.Contains(T
.Name
)) {
857 throw new Exception("Type already mapped");
860 NameIndex
[T
.Name
] = T
;
861 NpgsqlDbTypeIndex
[T
.NpgsqlDbType
] = T
;
862 DbTypeIndex
[T
.DbType
] = T
;
866 /// Add a new NpgsqlNativeTypeInfo with the given attributes and conversion handlers to this mapping.
868 /// <param name="OID">Type OID provided by the backend server.</param>
869 /// <param name="Name">Type name provided by the backend server.</param>
870 /// <param name="NpgsqlDbType">NpgsqlDbType</param>
871 /// <param name="ConvertBackendToNative">Data conversion handler.</param>
872 /// <param name="ConvertNativeToBackend">Data conversion handler.</param>
873 public void AddType(String Name
, NpgsqlDbType NpgsqlDbType
, DbType DbType
, Boolean Quote
,
874 ConvertNativeToBackendHandler NativeConvert
)
876 AddType(new NpgsqlNativeTypeInfo(Name
, NpgsqlDbType
, DbType
, Quote
, NativeConvert
));
879 public void AddNpgsqlDbTypeAlias(String Name
, NpgsqlDbType NpgsqlDbType
)
881 if (NpgsqlDbTypeIndex
.Contains(NpgsqlDbType
)) {
882 throw new Exception("NpgsqlDbType already aliased");
885 NpgsqlDbTypeIndex
[NpgsqlDbType
] = NameIndex
[Name
];
888 public void AddDbTypeAlias(String Name
, DbType DbType
)
890 if (DbTypeIndex
.Contains(DbType
)) {
891 throw new Exception("NpgsqlDbType already aliased");
894 DbTypeIndex
[DbType
] = NameIndex
[Name
];
897 public void AddTypeAlias(String Name
, Type Type
)
899 if (TypeIndex
.Contains(Type
)) {
900 throw new Exception("Type already aliased");
903 TypeIndex
[Type
] = NameIndex
[Name
];
907 /// Get the number of type infos held.
910 { get { return NameIndex.Count; }
}
913 /// Retrieve the NpgsqlNativeTypeInfo with the given backend type name, or null if none found.
915 public NpgsqlNativeTypeInfo
this [String Name
]
919 return (NpgsqlNativeTypeInfo
)NameIndex
[Name
];
924 /// Retrieve the NpgsqlNativeTypeInfo with the given NpgsqlDbType, or null if none found.
926 public NpgsqlNativeTypeInfo
this [NpgsqlDbType NpgsqlDbType
]
930 return (NpgsqlNativeTypeInfo
)NpgsqlDbTypeIndex
[NpgsqlDbType
];
935 /// Retrieve the NpgsqlNativeTypeInfo with the given DbType, or null if none found.
937 public NpgsqlNativeTypeInfo
this [DbType DbType
]
941 return (NpgsqlNativeTypeInfo
)DbTypeIndex
[DbType
];
948 /// Retrieve the NpgsqlNativeTypeInfo with the given Type, or null if none found.
950 public NpgsqlNativeTypeInfo
this [Type Type
]
954 return (NpgsqlNativeTypeInfo
)TypeIndex
[Type
];
959 /// Determine if a NpgsqlNativeTypeInfo with the given backend type name exists in this mapping.
961 public Boolean
ContainsName(String Name
)
963 return NameIndex
.ContainsKey(Name
);
967 /// Determine if a NpgsqlNativeTypeInfo with the given NpgsqlDbType exists in this mapping.
969 public Boolean
ContainsNpgsqlDbType(NpgsqlDbType NpgsqlDbType
)
971 return NpgsqlDbTypeIndex
.ContainsKey(NpgsqlDbType
);
975 /// Determine if a NpgsqlNativeTypeInfo with the given Type name exists in this mapping.
977 public Boolean
ContainsType(Type Type
)
979 return TypeIndex
.ContainsKey(Type
);