X Tutup
// NpgsqlTypes.NpgsqlTypesHelper.cs // // Author: // Francisco Jr. (fxjrlists@yahoo.com.br) // // Copyright (C) 2002 The Npgsql Development Team // npgsql-general@gborg.postgresql.org // http://gborg.postgresql.org/project/npgsql/projdisplay.php // // Permission to use, copy, modify, and distribute this software and its // documentation for any purpose, without fee, and without a written // agreement is hereby granted, provided that the above copyright notice // and this paragraph and the following two paragraphs appear in all copies. // // IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY // FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, // INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS // DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF // THE POSSIBILITY OF SUCH DAMAGE. // // THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY // AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS // ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS // TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. using System; using System.Collections.Generic; using System.Data; using System.Globalization; using System.Net; using System.Net.NetworkInformation; using System.Reflection; using System.Resources; using System.Text; using Npgsql; namespace NpgsqlTypes { /// /// This class contains helper methods for type conversion between /// the .Net type system and postgresql. /// internal static class NpgsqlTypesHelper { // Logging related values private static readonly String CLASSNAME = MethodBase.GetCurrentMethod().DeclaringType.Name; private static readonly ResourceManager resman = new ResourceManager(MethodBase.GetCurrentMethod().DeclaringType); // This is used by the test suite to test both text and binary encodings on version 3 connections. // See NpgsqlTests.BaseClassTests.TestFixtureSetup() and InitBinaryBackendSuppression(). // If this field is changed or removed, some tests will become partially non-functional, and an error will be issued. internal static bool SuppressBinaryBackendEncoding = false; private struct MappingKey : IEquatable { public readonly Version Version; public readonly bool UseExtendedTypes; public MappingKey(NpgsqlConnector conn) { Version = conn.ServerVersion; UseExtendedTypes = conn.UseExtendedTypes; } public bool Equals(MappingKey other) { return UseExtendedTypes.Equals(other.UseExtendedTypes) && Version.Equals(other.Version); } public override bool Equals(object obj) { //Note that Dictionary will call IEquatable.Equals() when possible. //This is included for completeness (that and second-guessing Mono while coding on .NET!). return obj != null && obj is MappingKey && Equals((MappingKey) obj); } public override int GetHashCode() { return UseExtendedTypes ? ~Version.GetHashCode() : Version.GetHashCode(); } } /// /// A cache of basic datatype mappings keyed by server version. This way we don't /// have to load the basic type mappings for every connection. /// private static readonly Dictionary BackendTypeMappingCache = new Dictionary(); private static readonly NpgsqlNativeTypeMapping NativeTypeMapping = PrepareDefaultTypesMap(); private static readonly Version Npgsql207 = new Version("2.0.7"); private static readonly Dictionary DefaultBackendInfoMapping = PrepareDefaultBackendInfoMapping(); private static Dictionary PrepareDefaultBackendInfoMapping() { Dictionary NameIndex = new Dictionary(); foreach (NpgsqlBackendTypeInfo TypeInfo in TypeInfoList(false, new Version("10.0.0.0"))) { NameIndex.Add(TypeInfo.Name, TypeInfo); //do the same for the equivalent array type. NameIndex.Add("_" + TypeInfo.Name, ArrayTypeInfo(TypeInfo)); } return NameIndex; } /// /// Find a NpgsqlNativeTypeInfo in the default types map that can handle objects /// of the given NpgsqlDbType. /// public static bool TryGetBackendTypeInfo(String BackendTypeName, out NpgsqlBackendTypeInfo TypeInfo) { return DefaultBackendInfoMapping.TryGetValue(BackendTypeName, out TypeInfo); } /// /// Find a NpgsqlNativeTypeInfo in the default types map that can handle objects /// of the given NpgsqlDbType. /// public static bool TryGetNativeTypeInfo(NpgsqlDbType dbType, out NpgsqlNativeTypeInfo typeInfo) { return NativeTypeMapping.TryGetValue(dbType, out typeInfo); } /// /// Find a NpgsqlNativeTypeInfo in the default types map that can handle objects /// of the given DbType. /// public static bool TryGetNativeTypeInfo(DbType dbType, out NpgsqlNativeTypeInfo typeInfo) { return NativeTypeMapping.TryGetValue(dbType, out typeInfo); } public static NpgsqlNativeTypeInfo GetNativeTypeInfo(DbType DbType) { NpgsqlNativeTypeInfo ret = null; return TryGetNativeTypeInfo(DbType, out ret) ? ret : null; } private static bool TestTypedEnumerator(Type type, out Type typeOut) { if (type.IsArray) { typeOut = type.GetElementType(); return true; } //We can only work out the element type for IEnumerable not for IEnumerable //so we are looking for IEnumerable for any value of T. //So we want to find an interface type where GetGenericTypeDefinition == typeof(IEnumerable<>); //And we can only safely call GetGenericTypeDefinition() if IsGenericType is true, but if it's false //then the interface clearly isn't an IEnumerable. foreach (Type iface in type.GetInterfaces()) { if (iface.IsGenericType && iface.GetGenericTypeDefinition().Equals(typeof (IEnumerable<>))) { typeOut = iface.GetGenericArguments()[0]; return true; } } typeOut = null; return false; } /// /// Find a NpgsqlNativeTypeInfo in the default types map that can handle objects /// of the given System.Type. /// public static bool TryGetNativeTypeInfo(Type type, out NpgsqlNativeTypeInfo typeInfo) { if (NativeTypeMapping.TryGetValue(type, out typeInfo)) { return true; } // At this point there is no direct mapping, so we see if we have an array or IEnumerable. // Note that we checked for a direct mapping first, so if there is a direct mapping of a class // which implements IEnumerable we will use that (currently this is only string, which // implements IEnumerable. Type elementType = null; NpgsqlNativeTypeInfo elementTypeInfo = null; if (TestTypedEnumerator(type, out elementType) && TryGetNativeTypeInfo(elementType, out elementTypeInfo)) { typeInfo = NpgsqlNativeTypeInfo.ArrayOf(elementTypeInfo); return true; } return false; } public static NpgsqlNativeTypeInfo GetNativeTypeInfo(Type Type) { NpgsqlNativeTypeInfo ret = null; return TryGetNativeTypeInfo(Type, out ret) ? ret : null; } public static bool DefinedType(Type type) { return NativeTypeMapping.ContainsType(type); } public static bool DefinedType(object item) { return DefinedType(item.GetType()); } // CHECKME // Not sure what to do with this one. I don't believe we ever ask for a binary // formatting, so this shouldn't even be used right now. // At some point this will need to be merged into the type converter system somehow? public static Object ConvertBackendBytesToSystemType(NpgsqlBackendTypeInfo TypeInfo, Byte[] data, Int32 fieldValueSize, Int32 typeModifier) { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ConvertBackendBytesToStytemType"); if (TypeInfo != null) { return TypeInfo.ConvertToNative(data, fieldValueSize, typeModifier); } else { return data; } } /// /// This method is responsible to convert the string received from the backend /// to the corresponding NpgsqlType. /// The given TypeInfo is called upon to do the conversion. /// If no TypeInfo object is provided, no conversion is performed. /// public static Object ConvertBackendStringToSystemType(NpgsqlBackendTypeInfo TypeInfo, String data, Int16 typeSize, Int32 typeModifier) { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ConvertBackendStringToSystemType"); if (TypeInfo != null) { return TypeInfo.ConvertToNative(data, typeSize, typeModifier); } else { return data; } } /// /// Create the one and only native to backend type map. /// This map is used when formatting native data /// types to backend representations. /// private static NpgsqlNativeTypeMapping PrepareDefaultTypesMap() { NpgsqlNativeTypeMapping nativeTypeMapping = new NpgsqlNativeTypeMapping(); nativeTypeMapping.AddType("name", NpgsqlDbType.Name, DbType.String, true, null); nativeTypeMapping.AddType("oidvector", NpgsqlDbType.Oidvector, DbType.String, true, null); // Conflicting types should have mapped first the non default mappings. // For example, char, varchar and text map to DbType.String. As the most // common is to use text with string, it has to be the last mapped, in order // to type mapping has the last entry, in this case, text, as the map value // for DbType.String. nativeTypeMapping.AddType("refcursor", NpgsqlDbType.Refcursor, DbType.String, true, null); nativeTypeMapping.AddType("char", NpgsqlDbType.Char, DbType.String, true, null); nativeTypeMapping.AddTypeAlias("char", typeof (Char)); nativeTypeMapping.AddType("varchar", NpgsqlDbType.Varchar, DbType.String, true, null); nativeTypeMapping.AddType("text", NpgsqlDbType.Text, DbType.String, true, null); nativeTypeMapping.AddDbTypeAlias("text", DbType.StringFixedLength); nativeTypeMapping.AddDbTypeAlias("text", DbType.AnsiString); nativeTypeMapping.AddDbTypeAlias("text", DbType.AnsiStringFixedLength); nativeTypeMapping.AddTypeAlias("text", typeof (String)); nativeTypeMapping.AddType("bytea", NpgsqlDbType.Bytea, DbType.Binary, true, new ConvertNativeToBackendTextHandler(BasicNativeToBackendTypeConverter.ByteArrayToByteaText), new ConvertNativeToBackendBinaryHandler(BasicNativeToBackendTypeConverter.ByteArrayToByteaBinary)); nativeTypeMapping.AddTypeAlias("bytea", typeof (Byte[])); nativeTypeMapping.AddType("bit", NpgsqlDbType.Bit, DbType.Object, false, new ConvertNativeToBackendTextHandler(BasicNativeToBackendTypeConverter.ToBit)); nativeTypeMapping.AddTypeAlias("bit", typeof(BitString)); nativeTypeMapping.AddType("bool", NpgsqlDbType.Boolean, DbType.Boolean, false, new ConvertNativeToBackendTextHandler(BasicNativeToBackendTypeConverter.BooleanToBooleanText), new ConvertNativeToBackendBinaryHandler(BasicNativeToBackendTypeConverter.BooleanToBooleanBinary)); nativeTypeMapping.AddTypeAlias("bool", typeof (Boolean)); nativeTypeMapping.AddType("int2", NpgsqlDbType.Smallint, DbType.Int16, false, BasicNativeToBackendTypeConverter.ToBasicType, BasicNativeToBackendTypeConverter.Int16ToInt2Binary); nativeTypeMapping.AddTypeAlias("int2", typeof (UInt16)); nativeTypeMapping.AddTypeAlias("int2", typeof (Int16)); nativeTypeMapping.AddDbTypeAlias("int2", DbType.Byte); nativeTypeMapping.AddTypeAlias("int2", typeof (Byte)); nativeTypeMapping.AddType("int4", NpgsqlDbType.Integer, DbType.Int32, false, BasicNativeToBackendTypeConverter.ToBasicType, BasicNativeToBackendTypeConverter.Int32ToInt4Binary); nativeTypeMapping.AddTypeAlias("int4", typeof (Int32)); nativeTypeMapping.AddType("int8", NpgsqlDbType.Bigint, DbType.Int64, false, BasicNativeToBackendTypeConverter.ToBasicType, BasicNativeToBackendTypeConverter.Int64ToInt8Binary); nativeTypeMapping.AddTypeAlias("int8", typeof (Int64)); nativeTypeMapping.AddType("float4", NpgsqlDbType.Real, DbType.Single, true, new ConvertNativeToBackendTextHandler(BasicNativeToBackendTypeConverter.ToSingleDouble)); nativeTypeMapping.AddTypeAlias("float4", typeof (Single)); //nativeTypeMapping.AddType("float8", NpgsqlDbType.Double, DbType.Double, true, BasicNativeToBackendTypeConverter.ToBasicType); nativeTypeMapping.AddType("float8", NpgsqlDbType.Double, DbType.Double, true, new ConvertNativeToBackendTextHandler(BasicNativeToBackendTypeConverter.ToSingleDouble)); nativeTypeMapping.AddTypeAlias("float8", typeof (Double)); nativeTypeMapping.AddType("numeric", NpgsqlDbType.Numeric, DbType.Decimal, true, BasicNativeToBackendTypeConverter.ToBasicType); nativeTypeMapping.AddTypeAlias("numeric", typeof (Decimal)); nativeTypeMapping.AddType("money", NpgsqlDbType.Money, DbType.Currency, true, new ConvertNativeToBackendTextHandler(BasicNativeToBackendTypeConverter.ToMoney)); nativeTypeMapping.AddType("date", NpgsqlDbType.Date, DbType.Date, true, new ConvertNativeToBackendTextHandler(BasicNativeToBackendTypeConverter.ToDate)); nativeTypeMapping.AddTypeAlias("date", typeof (NpgsqlDate)); nativeTypeMapping.AddType("timetz", NpgsqlDbType.TimeTZ, DbType.Time, true, new ConvertNativeToBackendTextHandler(ExtendedNativeToBackendTypeConverter.ToTimeTZ)); nativeTypeMapping.AddTypeAlias("timetz", typeof (NpgsqlTimeTZ)); nativeTypeMapping.AddType("time", NpgsqlDbType.Time, DbType.Time, true, new ConvertNativeToBackendTextHandler(BasicNativeToBackendTypeConverter.ToTime)); nativeTypeMapping.AddTypeAlias("time", typeof (NpgsqlTime)); nativeTypeMapping.AddType("timestamptz", NpgsqlDbType.TimestampTZ, DbType.DateTime, true, new ConvertNativeToBackendTextHandler(ExtendedNativeToBackendTypeConverter.ToTimeStamp)); nativeTypeMapping.AddTypeAlias("timestamptz", typeof(NpgsqlTimeStampTZ)); nativeTypeMapping.AddDbTypeAlias("timestamptz", DbType.DateTimeOffset); nativeTypeMapping.AddTypeAlias("timestamptz", typeof(DateTimeOffset)); nativeTypeMapping.AddType("abstime", NpgsqlDbType.Abstime, DbType.DateTime, true, new ConvertNativeToBackendTextHandler(ExtendedNativeToBackendTypeConverter.ToTimeStamp)); nativeTypeMapping.AddType("timestamp", NpgsqlDbType.Timestamp, DbType.DateTime, true, new ConvertNativeToBackendTextHandler(ExtendedNativeToBackendTypeConverter.ToTimeStamp)); nativeTypeMapping.AddTypeAlias("timestamp", typeof (DateTime)); nativeTypeMapping.AddTypeAlias("timestamp", typeof (NpgsqlTimeStamp)); nativeTypeMapping.AddType("point", NpgsqlDbType.Point, DbType.Object, true, new ConvertNativeToBackendTextHandler(ExtendedNativeToBackendTypeConverter.ToPoint)); nativeTypeMapping.AddTypeAlias("point", typeof (NpgsqlPoint)); nativeTypeMapping.AddType("box", NpgsqlDbType.Box, DbType.Object, true, new ConvertNativeToBackendTextHandler(ExtendedNativeToBackendTypeConverter.ToBox)); nativeTypeMapping.AddTypeAlias("box", typeof (NpgsqlBox)); nativeTypeMapping.AddType("lseg", NpgsqlDbType.LSeg, DbType.Object, true, new ConvertNativeToBackendTextHandler(ExtendedNativeToBackendTypeConverter.ToLSeg)); nativeTypeMapping.AddTypeAlias("lseg", typeof (NpgsqlLSeg)); nativeTypeMapping.AddType("path", NpgsqlDbType.Path, DbType.Object, true, new ConvertNativeToBackendTextHandler(ExtendedNativeToBackendTypeConverter.ToPath)); nativeTypeMapping.AddTypeAlias("path", typeof (NpgsqlPath)); nativeTypeMapping.AddType("polygon", NpgsqlDbType.Polygon, DbType.Object, true, new ConvertNativeToBackendTextHandler(ExtendedNativeToBackendTypeConverter.ToPolygon)); nativeTypeMapping.AddTypeAlias("polygon", typeof (NpgsqlPolygon)); nativeTypeMapping.AddType("circle", NpgsqlDbType.Circle, DbType.Object, true, new ConvertNativeToBackendTextHandler(ExtendedNativeToBackendTypeConverter.ToCircle)); nativeTypeMapping.AddTypeAlias("circle", typeof (NpgsqlCircle)); nativeTypeMapping.AddType("inet", NpgsqlDbType.Inet, DbType.Object, true, new ConvertNativeToBackendTextHandler(ExtendedNativeToBackendTypeConverter.ToIPAddress)); nativeTypeMapping.AddTypeAlias("inet", typeof (IPAddress)); nativeTypeMapping.AddTypeAlias("inet", typeof (NpgsqlInet)); nativeTypeMapping.AddType("macaddr", NpgsqlDbType.MacAddr, DbType.Object, true, new ConvertNativeToBackendTextHandler(ExtendedNativeToBackendTypeConverter.ToMacAddress)); nativeTypeMapping.AddTypeAlias("macaddr", typeof(PhysicalAddress)); nativeTypeMapping.AddTypeAlias("macaddr", typeof(NpgsqlMacAddress)); nativeTypeMapping.AddType("uuid", NpgsqlDbType.Uuid, DbType.Guid, true, null); nativeTypeMapping.AddTypeAlias("uuid", typeof (Guid)); nativeTypeMapping.AddType("xml", NpgsqlDbType.Xml, DbType.Xml, true, null); nativeTypeMapping.AddType("interval", NpgsqlDbType.Interval, DbType.Object, true, new ConvertNativeToBackendTextHandler(ExtendedNativeToBackendTypeConverter.ToInterval)); nativeTypeMapping.AddTypeAlias("interval", typeof (NpgsqlInterval)); nativeTypeMapping.AddTypeAlias("interval", typeof (TimeSpan)); nativeTypeMapping.AddDbTypeAlias("text", DbType.Object); return nativeTypeMapping; } private static IEnumerable TypeInfoList(bool useExtendedTypes, Version compat) { yield return new NpgsqlBackendTypeInfo(0, "oidvector", NpgsqlDbType.Text, DbType.String, typeof (String), null); yield return new NpgsqlBackendTypeInfo(0, "unknown", NpgsqlDbType.Text, DbType.String, typeof (String), null); yield return new NpgsqlBackendTypeInfo(0, "refcursor", NpgsqlDbType.Refcursor, DbType.String, typeof (String), null); yield return new NpgsqlBackendTypeInfo(0, "char", NpgsqlDbType.Char, DbType.String, typeof (String), null); yield return new NpgsqlBackendTypeInfo(0, "bpchar", NpgsqlDbType.Text, DbType.String, typeof (String), null); yield return new NpgsqlBackendTypeInfo(0, "varchar", NpgsqlDbType.Varchar, DbType.String, typeof (String), null); yield return new NpgsqlBackendTypeInfo(0, "text", NpgsqlDbType.Text, DbType.String, typeof (String), null); yield return new NpgsqlBackendTypeInfo(0, "name", NpgsqlDbType.Name, DbType.String, typeof (String), null); yield return new NpgsqlBackendTypeInfo(0, "bytea", NpgsqlDbType.Bytea, DbType.Binary, typeof (Byte[]), new ConvertBackendTextToNativeHandler(BasicBackendToNativeTypeConverter.ByteaTextToByteArray), new ConvertBackendBinaryToNativeHandler(BasicBackendToNativeTypeConverter.ByteaBinaryToByteArray)); yield return new NpgsqlBackendTypeInfo(0, "bit", NpgsqlDbType.Bit, DbType.Object, typeof (BitString), new ConvertBackendTextToNativeHandler(BasicBackendToNativeTypeConverter.ToBit)); yield return new NpgsqlBackendTypeInfo(0, "bool", NpgsqlDbType.Boolean, DbType.Boolean, typeof (Boolean), new ConvertBackendTextToNativeHandler(BasicBackendToNativeTypeConverter.BooleanTextToBoolean), new ConvertBackendBinaryToNativeHandler(BasicBackendToNativeTypeConverter.BooleanBinaryToBoolean)); yield return new NpgsqlBackendTypeInfo(0, "int2", NpgsqlDbType.Smallint, DbType.Int16, typeof (Int16), null, new ConvertBackendBinaryToNativeHandler(BasicBackendToNativeTypeConverter.IntBinaryToInt)); yield return new NpgsqlBackendTypeInfo(0, "int4", NpgsqlDbType.Integer, DbType.Int32, typeof (Int32), null, new ConvertBackendBinaryToNativeHandler(BasicBackendToNativeTypeConverter.IntBinaryToInt)); yield return new NpgsqlBackendTypeInfo(0, "int8", NpgsqlDbType.Bigint, DbType.Int64, typeof (Int64), null, new ConvertBackendBinaryToNativeHandler(BasicBackendToNativeTypeConverter.IntBinaryToInt)); yield return new NpgsqlBackendTypeInfo(0, "oid", NpgsqlDbType.Bigint, DbType.Int64, typeof (Int64), null); yield return new NpgsqlBackendTypeInfo(0, "float4", NpgsqlDbType.Real, DbType.Single, typeof (Single), null); yield return new NpgsqlBackendTypeInfo(0, "float8", NpgsqlDbType.Double, DbType.Double, typeof (Double), null); yield return new NpgsqlBackendTypeInfo(0, "numeric", NpgsqlDbType.Numeric, DbType.Decimal, typeof (Decimal), null); yield return new NpgsqlBackendTypeInfo(0, "inet", NpgsqlDbType.Inet, DbType.Object, typeof (NpgsqlInet), new ConvertBackendTextToNativeHandler(ExtendedBackendToNativeTypeConverter.ToInet), typeof(IPAddress), ipaddress => (IPAddress)(NpgsqlInet)ipaddress, npgsqlinet => (npgsqlinet is IPAddress ? (NpgsqlInet)(IPAddress) npgsqlinet : npgsqlinet)); yield return new NpgsqlBackendTypeInfo(0, "macaddr", NpgsqlDbType.MacAddr, DbType.Object, typeof(NpgsqlMacAddress), new ConvertBackendTextToNativeHandler(ExtendedBackendToNativeTypeConverter.ToMacAddress), typeof(PhysicalAddress), macAddress => (PhysicalAddress)(NpgsqlMacAddress)macAddress, npgsqlmacaddr => (npgsqlmacaddr is PhysicalAddress ? (NpgsqlMacAddress)(PhysicalAddress)npgsqlmacaddr : npgsqlmacaddr)); yield return new NpgsqlBackendTypeInfo(0, "money", NpgsqlDbType.Money, DbType.Currency, typeof (Decimal), new ConvertBackendTextToNativeHandler(BasicBackendToNativeTypeConverter.ToMoney)); yield return new NpgsqlBackendTypeInfo(0, "point", NpgsqlDbType.Point, DbType.Object, typeof (NpgsqlPoint), new ConvertBackendTextToNativeHandler(ExtendedBackendToNativeTypeConverter.ToPoint)); yield return new NpgsqlBackendTypeInfo(0, "lseg", NpgsqlDbType.LSeg, DbType.Object, typeof (NpgsqlLSeg), new ConvertBackendTextToNativeHandler(ExtendedBackendToNativeTypeConverter.ToLSeg)); yield return new NpgsqlBackendTypeInfo(0, "path", NpgsqlDbType.Path, DbType.Object, typeof (NpgsqlPath), new ConvertBackendTextToNativeHandler(ExtendedBackendToNativeTypeConverter.ToPath)); yield return new NpgsqlBackendTypeInfo(0, "box", NpgsqlDbType.Box, DbType.Object, typeof (NpgsqlBox), new ConvertBackendTextToNativeHandler(ExtendedBackendToNativeTypeConverter.ToBox)); yield return new NpgsqlBackendTypeInfo(0, "circle", NpgsqlDbType.Circle, DbType.Object, typeof (NpgsqlCircle), new ConvertBackendTextToNativeHandler(ExtendedBackendToNativeTypeConverter.ToCircle)); yield return new NpgsqlBackendTypeInfo(0, "polygon", NpgsqlDbType.Polygon, DbType.Object, typeof (NpgsqlPolygon), new ConvertBackendTextToNativeHandler(ExtendedBackendToNativeTypeConverter.ToPolygon)); yield return new NpgsqlBackendTypeInfo(0, "uuid", NpgsqlDbType.Uuid, DbType.Guid, typeof (Guid), new ConvertBackendTextToNativeHandler(ExtendedBackendToNativeTypeConverter.ToGuid)); yield return new NpgsqlBackendTypeInfo(0, "xml", NpgsqlDbType.Xml, DbType.Xml, typeof (String), null); if (useExtendedTypes) { yield return new NpgsqlBackendTypeInfo(0, "interval", NpgsqlDbType.Interval, DbType.Object, typeof(NpgsqlInterval), new ConvertBackendTextToNativeHandler(ExtendedBackendToNativeTypeConverter.ToInterval)); yield return new NpgsqlBackendTypeInfo(0, "date", NpgsqlDbType.Date, DbType.Date, typeof(NpgsqlDate), new ConvertBackendTextToNativeHandler(ExtendedBackendToNativeTypeConverter.ToDate)); yield return new NpgsqlBackendTypeInfo(0, "time", NpgsqlDbType.Time, DbType.Time, typeof(NpgsqlTime), new ConvertBackendTextToNativeHandler(ExtendedBackendToNativeTypeConverter.ToTime)); yield return new NpgsqlBackendTypeInfo(0, "timetz", NpgsqlDbType.TimeTZ, DbType.Time, typeof(NpgsqlTimeTZ), new ConvertBackendTextToNativeHandler(ExtendedBackendToNativeTypeConverter.ToTimeTZ)); yield return new NpgsqlBackendTypeInfo(0, "timestamp", NpgsqlDbType.Timestamp, DbType.DateTime, typeof(NpgsqlTimeStamp), new ConvertBackendTextToNativeHandler(ExtendedBackendToNativeTypeConverter.ToTimeStamp)); yield return new NpgsqlBackendTypeInfo(0, "abstime", NpgsqlDbType.Abstime , DbType.DateTime, typeof(NpgsqlTimeStampTZ), new ConvertBackendTextToNativeHandler(ExtendedBackendToNativeTypeConverter.ToTimeStampTZ)); yield return new NpgsqlBackendTypeInfo(0, "timestamptz", NpgsqlDbType.TimestampTZ, DbType.DateTime, typeof(NpgsqlTimeStampTZ), new ConvertBackendTextToNativeHandler(ExtendedBackendToNativeTypeConverter.ToTimeStampTZ)); } else { if (compat <= Npgsql207) { // In 2.0.7 and earlier, intervals were returned as the native type. // later versions return a CLR type and rely on provider specific api for NpgsqlInterval yield return new NpgsqlBackendTypeInfo(0, "interval", NpgsqlDbType.Interval, DbType.Object, typeof(NpgsqlInterval), new ConvertBackendTextToNativeHandler(ExtendedBackendToNativeTypeConverter.ToInterval)); } else { yield return new NpgsqlBackendTypeInfo(0, "interval", NpgsqlDbType.Interval, DbType.Object, typeof(NpgsqlInterval), new ConvertBackendTextToNativeHandler(ExtendedBackendToNativeTypeConverter.ToInterval), typeof(TimeSpan), interval => (TimeSpan)(NpgsqlInterval)interval, intervalNpgsql => (intervalNpgsql is TimeSpan ? (NpgsqlInterval)(TimeSpan) intervalNpgsql : intervalNpgsql)); } yield return new NpgsqlBackendTypeInfo(0, "date", NpgsqlDbType.Date, DbType.Date, typeof (NpgsqlDate), new ConvertBackendTextToNativeHandler(ExtendedBackendToNativeTypeConverter.ToDate), typeof(DateTime), date => (DateTime)(NpgsqlDate)date, npgsqlDate => (npgsqlDate is DateTime ? (NpgsqlDate)(DateTime) npgsqlDate : npgsqlDate)); yield return new NpgsqlBackendTypeInfo(0, "time", NpgsqlDbType.Time, DbType.Time, typeof (NpgsqlTime), new ConvertBackendTextToNativeHandler(ExtendedBackendToNativeTypeConverter.ToTime), typeof(DateTime), time => time is DateTime ? time : (DateTime)(NpgsqlTime)time, npgsqlTime => (npgsqlTime is TimeSpan ? (NpgsqlTime)(TimeSpan) npgsqlTime : npgsqlTime)); yield return new NpgsqlBackendTypeInfo(0, "timetz", NpgsqlDbType.TimeTZ, DbType.Time, typeof (NpgsqlTimeTZ), new ConvertBackendTextToNativeHandler(ExtendedBackendToNativeTypeConverter.ToTimeTZ), typeof(DateTime), timetz => (DateTime)(NpgsqlTimeTZ)timetz, npgsqlTimetz => (npgsqlTimetz is TimeSpan ? (NpgsqlTimeTZ)(TimeSpan) npgsqlTimetz : npgsqlTimetz)); yield return new NpgsqlBackendTypeInfo(0, "timestamp", NpgsqlDbType.Timestamp, DbType.DateTime, typeof (NpgsqlTimeStamp), new ConvertBackendTextToNativeHandler(ExtendedBackendToNativeTypeConverter.ToTimeStamp), typeof(DateTime), timestamp => (DateTime)(NpgsqlTimeStamp)timestamp, npgsqlTimestamp => (npgsqlTimestamp is DateTime ? (NpgsqlTimeStamp)(DateTime) npgsqlTimestamp : npgsqlTimestamp)); yield return new NpgsqlBackendTypeInfo(0, "abstime", NpgsqlDbType.Abstime, DbType.DateTime, typeof(NpgsqlTimeStampTZ), new ConvertBackendTextToNativeHandler(ExtendedBackendToNativeTypeConverter.ToTimeStampTZ), typeof(DateTime), timestamp => (DateTime)(NpgsqlTimeStampTZ)timestamp, npgsqlTimestampTZ => (npgsqlTimestampTZ is DateTime ? (NpgsqlTimeStampTZ)(DateTime) npgsqlTimestampTZ : npgsqlTimestampTZ)); yield return new NpgsqlBackendTypeInfo(0, "timestamptz", NpgsqlDbType.TimestampTZ, DbType.DateTime, typeof (NpgsqlTimeStampTZ), new ConvertBackendTextToNativeHandler(ExtendedBackendToNativeTypeConverter.ToTimeStampTZ), typeof(DateTime), timestamptz => ((DateTime)(NpgsqlTimeStampTZ)timestamptz).ToLocalTime(), npgsqlTimestampTZ => (npgsqlTimestampTZ is DateTime ? (NpgsqlTimeStampTZ)(DateTime)npgsqlTimestampTZ : npgsqlTimestampTZ is DateTimeOffset ? (NpgsqlTimeStampTZ)(DateTimeOffset)npgsqlTimestampTZ : npgsqlTimestampTZ)); } } /// /// This method creates (or retrieves from cache) a mapping between type and OID /// of all natively supported postgresql data types. /// This is needed as from one version to another, this mapping can be changed and /// so we avoid hardcoding them. /// /// NpgsqlTypeMapping containing all known data types. The mapping must be /// cloned before it is modified because it is cached; changes made by one connection may /// effect another connection. /// public static NpgsqlBackendTypeMapping CreateAndLoadInitialTypesMapping(NpgsqlConnector conn) { NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "LoadTypesMapping"); MappingKey key = new MappingKey(conn); // Check the cache for an initial types map. NpgsqlBackendTypeMapping oidToNameMapping = null; if(BackendTypeMappingCache.TryGetValue(key, out oidToNameMapping)) return oidToNameMapping; // Not in cache, create a new one. oidToNameMapping = new NpgsqlBackendTypeMapping(); // Create a list of all natively supported postgresql data types. // Attempt to map each type info in the list to an OID on the backend and // add each mapped type to the new type mapping object. LoadTypesMappings(conn, oidToNameMapping, TypeInfoList(conn.UseExtendedTypes, conn.CompatVersion)); //We hold the lock for the least time possible on the least scope possible. //We must lock on BackendTypeMappingCache because it will be updated by this operation, //and we must not just add to it, but also check that another thread hasn't updated it //in the meantime. Strictly just doing : //return BackendTypeMappingCache[key] = oidToNameMapping; //as the only call within the locked section should be safe and correct, but we'll assume //there's some subtle problem with temporarily having two copies of the same mapping and //ensure only one is called. //It is of course wasteful that multiple threads could be creating mappings when only one //will be used, but we aim for better overall concurrency at the risk of causing some //threads the extra work. NpgsqlBackendTypeMapping mappingCheck = null; //First check without acquiring the lock; don't lock if we don't have to. if(BackendTypeMappingCache.TryGetValue(key, out mappingCheck))//Another thread built the mapping in the meantime. return mappingCheck; lock(BackendTypeMappingCache) { //Final check. We have the lock now so if this fails it'll continue to fail. if(BackendTypeMappingCache.TryGetValue(key, out mappingCheck))//Another thread built the mapping in the meantime. return mappingCheck; // Add this mapping to the per-server-version cache so we don't have to // do these expensive queries on every connection startup. BackendTypeMappingCache.Add(key, oidToNameMapping); } return oidToNameMapping; } //Take a NpgsqlBackendTypeInfo for a type and return the NpgsqlBackendTypeInfo for //an array of that type. private static NpgsqlBackendTypeInfo ArrayTypeInfo(NpgsqlBackendTypeInfo elementInfo) { return new NpgsqlBackendTypeInfo(0, "_" + elementInfo.Name, NpgsqlDbType.Array | elementInfo.NpgsqlDbType, DbType.Object, elementInfo.Type.MakeArrayType(), new ConvertBackendTextToNativeHandler( new ArrayBackendToNativeTypeConverter(elementInfo).ToArray)); } /// /// Attempt to map types by issuing a query against pg_type. /// This function takes a list of NpgsqlTypeInfo and attempts to resolve the OID field /// of each by querying pg_type. If the mapping is found, the type info object is /// updated (OID) and added to the provided NpgsqlTypeMapping object. /// /// NpgsqlConnector to send query through. /// Mapping object to add types too. /// List of types that need to have OID's mapped. public static void LoadTypesMappings(NpgsqlConnector conn, NpgsqlBackendTypeMapping TypeMappings, IEnumerable TypeInfoList) { StringBuilder InList = new StringBuilder(); Dictionary NameIndex = new Dictionary(); // Build a clause for the SELECT statement. // Build a name->typeinfo mapping so we can match the results of the query // with the list of type objects efficiently. foreach (NpgsqlBackendTypeInfo TypeInfo in TypeInfoList) { NameIndex.Add(TypeInfo.Name, TypeInfo); InList.AppendFormat("{0}'{1}'", ((InList.Length > 0) ? ", " : ""), TypeInfo.Name); //do the same for the equivalent array type. NameIndex.Add("_" + TypeInfo.Name, ArrayTypeInfo(TypeInfo)); InList.Append(", '_").Append(TypeInfo.Name).Append('\''); } if (InList.Length == 0) { return; } using ( NpgsqlCommand command = new NpgsqlCommand(string.Format("SELECT typname, oid FROM pg_type WHERE typname IN ({0})", InList), conn)) { using (NpgsqlDataReader dr = command.GetReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult)) { while (dr.Read()) { NpgsqlBackendTypeInfo TypeInfo = NameIndex[dr[0].ToString()]; TypeInfo._OID = Convert.ToInt32(dr[1]); TypeMappings.AddType(TypeInfo); } } } } } /// /// Delegate called to convert the given backend text data to its native representation. /// internal delegate Object ConvertBackendTextToNativeHandler( NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier); /// /// Delegate called to convert the given backend binary data to its native representation. /// internal delegate Object ConvertBackendBinaryToNativeHandler( NpgsqlBackendTypeInfo TypeInfo, byte[] BackendData, Int32 fieldValueSize, Int32 TypeModifier); /// /// Delegate called to convert the given native data to its backand representation. /// internal delegate String ConvertNativeToBackendTextHandler(NpgsqlNativeTypeInfo TypeInfo, Object NativeData, Boolean forExtendedQuery, NativeToBackendTypeConverterOptions options); internal delegate byte[] ConvertNativeToBackendBinaryHandler(NpgsqlNativeTypeInfo TypeInfo, Object NativeData, NativeToBackendTypeConverterOptions options); internal delegate object ConvertProviderTypeToFrameworkTypeHander(object value); internal delegate object ConvertFrameworkTypeToProviderTypeHander(object value); /// /// Represents a backend data type. /// This class can be called upon to convert a backend field representation to a native object. /// internal class NpgsqlBackendTypeInfo { private readonly ConvertBackendTextToNativeHandler _ConvertBackendTextToNative; private readonly ConvertBackendBinaryToNativeHandler _ConvertBackendBinaryToNative; private readonly ConvertProviderTypeToFrameworkTypeHander _convertProviderToFramework; private readonly ConvertFrameworkTypeToProviderTypeHander _convertFrameworkToProvider; internal Int32 _OID; private readonly String _Name; private readonly NpgsqlDbType _NpgsqlDbType; private readonly DbType _DbType; private readonly Type _Type; private readonly Type _frameworkType; /// /// Construct a new NpgsqlTypeInfo with the given attributes and conversion handlers. /// /// Type OID provided by the backend server. /// Type name provided by the backend server. /// NpgsqlDbType /// DbType /// System type to convert fields of this type to. /// Data conversion handler for text encoding. /// Data conversion handler for binary data. public NpgsqlBackendTypeInfo(Int32 OID, String Name, NpgsqlDbType NpgsqlDbType, DbType DbType, Type Type, ConvertBackendTextToNativeHandler ConvertBackendTextToNative = null, ConvertBackendBinaryToNativeHandler ConvertBackendBinaryToNative = null) { if (Type == null) { throw new ArgumentNullException("Type"); } _OID = OID; _Name = Name; _NpgsqlDbType = NpgsqlDbType; _DbType = DbType; _Type = Type; _frameworkType = Type; _ConvertBackendTextToNative = ConvertBackendTextToNative; _ConvertBackendBinaryToNative = ConvertBackendBinaryToNative; } public NpgsqlBackendTypeInfo(Int32 OID, String Name, NpgsqlDbType NpgsqlDbType, DbType DbType, Type Type, ConvertBackendTextToNativeHandler ConvertBackendTextToNative, ConvertBackendBinaryToNativeHandler ConvertBackendBinaryToNative, Type frameworkType, ConvertProviderTypeToFrameworkTypeHander convertProviderToFramework, ConvertFrameworkTypeToProviderTypeHander convertFrameworkToProvider) : this(OID, Name, NpgsqlDbType, DbType, Type, ConvertBackendTextToNative, ConvertBackendBinaryToNative) { _frameworkType = frameworkType; _convertProviderToFramework = convertProviderToFramework; _convertFrameworkToProvider = convertFrameworkToProvider; } public NpgsqlBackendTypeInfo(Int32 OID, String Name, NpgsqlDbType NpgsqlDbType, DbType DbType, Type Type, ConvertBackendTextToNativeHandler ConvertBackendTextToNative, Type frameworkType, ConvertProviderTypeToFrameworkTypeHander convertProviderToFramework, ConvertFrameworkTypeToProviderTypeHander convertFrameworkToProvider) : this(OID, Name, NpgsqlDbType, DbType, Type, ConvertBackendTextToNative, null) { _frameworkType = frameworkType; _convertProviderToFramework = convertProviderToFramework; _convertFrameworkToProvider = convertFrameworkToProvider; } /// /// Type OID provided by the backend server. /// public Int32 OID { get { return _OID; } } /// /// Type name provided by the backend server. /// public String Name { get { return _Name; } } /// /// NpgsqlDbType. /// public NpgsqlDbType NpgsqlDbType { get { return _NpgsqlDbType; } } /// /// NpgsqlDbType. /// public DbType DbType { get { return _DbType; } } /// /// Provider type to convert fields of this type to. /// public Type Type { get { return _Type; } } /// /// System type to convert fields of this type to. /// public Type FrameworkType { get { return _frameworkType; } } /// /// Reports whether a backend binary to native decoder is available for this type. /// public bool SupportsBinaryBackendData { get { return (! NpgsqlTypesHelper.SuppressBinaryBackendEncoding && _ConvertBackendBinaryToNative != null); } } /// /// Perform a data conversion from a backend representation to /// a native object. /// /// Data sent from the backend. /// fieldValueSize /// Type modifier field sent from the backend. public Object ConvertToNative(Byte[] BackendData, Int32 fieldValueSize, Int32 TypeModifier) { if (! NpgsqlTypesHelper.SuppressBinaryBackendEncoding && _ConvertBackendBinaryToNative != null) { return _ConvertBackendBinaryToNative(this, BackendData, fieldValueSize, TypeModifier); } else { return BackendData; } } /// /// Perform a data conversion from a backend representation to /// a native object. /// /// Data sent from the backend. /// TypeSize /// Type modifier field sent from the backend. public Object ConvertToNative(string BackendData, Int16 TypeSize, Int32 TypeModifier) { if (_ConvertBackendTextToNative != null) { return _ConvertBackendTextToNative(this, BackendData, TypeSize, TypeModifier); } else { try { return Convert.ChangeType(BackendData, Type, CultureInfo.InvariantCulture); } catch { return BackendData; } } } internal object ConvertToFrameworkType(object providerValue) { if (providerValue == DBNull.Value) { return providerValue; } else if (_convertProviderToFramework != null) { return _convertProviderToFramework(providerValue); } else if (Type != FrameworkType) { try { return Convert.ChangeType(providerValue, FrameworkType, CultureInfo.InvariantCulture); } catch { return providerValue; } } return providerValue; } internal object ConvertToProviderType(object frameworkValue) { if (frameworkValue == DBNull.Value) { return frameworkValue; } else if (_convertFrameworkToProvider!= null) { return _convertFrameworkToProvider(frameworkValue); } return frameworkValue; } } /// /// Represents a backend data type. /// This class can be called upon to convert a native object to its backend field representation, /// internal class NpgsqlNativeTypeInfo { private static readonly NumberFormatInfo ni; private readonly ConvertNativeToBackendTextHandler _ConvertNativeToBackendText; private readonly ConvertNativeToBackendBinaryHandler _ConvertNativeToBackendBinary; private readonly String _Name; private readonly string _CastName; private readonly NpgsqlDbType _NpgsqlDbType; private readonly DbType _DbType; private readonly Boolean _Quote; private readonly Boolean _UseSize; private Boolean _IsArray = false; /// /// Returns an NpgsqlNativeTypeInfo for an array where the elements are of the type /// described by the NpgsqlNativeTypeInfo supplied. /// public static NpgsqlNativeTypeInfo ArrayOf(NpgsqlNativeTypeInfo elementType) { if (elementType._IsArray) //we've an array of arrays. It's the inner most elements whose type we care about, so the type we have is fine. { return elementType; } NpgsqlNativeTypeInfo copy = new NpgsqlNativeTypeInfo("_" + elementType.Name, NpgsqlDbType.Array | elementType.NpgsqlDbType, elementType.DbType, false, new ConvertNativeToBackendTextHandler( new ArrayNativeToBackendTypeConverter(elementType).FromArray)); copy._IsArray = true; return copy; } static NpgsqlNativeTypeInfo() { ni = (NumberFormatInfo) CultureInfo.InvariantCulture.NumberFormat.Clone(); ni.NumberDecimalDigits = 15; } internal static NumberFormatInfo NumberFormat { get { return ni; } } /// /// Construct a new NpgsqlTypeInfo with the given attributes and conversion handlers. /// /// Type name provided by the backend server. /// DbType /// Quote /// NpgsqlDbType /// Data conversion handler for text backend encoding. /// Data conversion handler for binary backend encoding (for extended queries). public NpgsqlNativeTypeInfo(String Name, NpgsqlDbType NpgsqlDbType, DbType DbType, Boolean Quote, ConvertNativeToBackendTextHandler ConvertNativeToBackendText = null, ConvertNativeToBackendBinaryHandler ConvertNativeToBackendBinary = null) { _Name = Name; _CastName = Name.StartsWith("_") ? Name.Substring(1) + "[]" : Name; _NpgsqlDbType = NpgsqlDbType; _DbType = DbType; _Quote = Quote; _ConvertNativeToBackendText = ConvertNativeToBackendText; _ConvertNativeToBackendBinary = ConvertNativeToBackendBinary; // The only parameters types which use length currently supported are char and varchar. Check for them. if ((NpgsqlDbType == NpgsqlDbType.Char) || (NpgsqlDbType == NpgsqlDbType.Varchar)) { _UseSize = true; } else { _UseSize = false; } } /// /// Type name provided by the backend server. /// public String Name { get { return _Name; } } public string CastName { get { return _CastName; } } public bool IsArray { get { return _IsArray; } } /// /// NpgsqlDbType. /// public NpgsqlDbType NpgsqlDbType { get { return _NpgsqlDbType; } } /// /// DbType. /// public DbType DbType { get { return _DbType; } } /// /// Apply quoting. /// public Boolean Quote { get { return _Quote; } } /// /// Use parameter size information. /// public Boolean UseSize { get { return _UseSize; } } /// /// Perform a data conversion from a native object to /// a backend representation. /// DBNull and null values are handled differently depending if a plain query is used /// When /// /// Native .NET object to be converted. /// Options to guide serialization. /// Connection specific options. public object ConvertToBackend(Object NativeData, Boolean forExtendedQuery, NativeToBackendTypeConverterOptions options) { if (forExtendedQuery) { return ConvertToBackendExtendedQuery(NativeData, options); } else { return ConvertToBackendPlainQuery(NativeData, options); } } private String ConvertToBackendPlainQuery(Object NativeData, NativeToBackendTypeConverterOptions options) { if ((NativeData == DBNull.Value) || (NativeData == null)) { return "NULL"; // Plain queries exptects null values as string NULL. } string backendSerialization; if (_ConvertNativeToBackendText != null) { // This route escapes output strings as needed. backendSerialization = _ConvertNativeToBackendText(this, NativeData, false, options); if (Quote) { backendSerialization = QuoteString(backendSerialization, ! options.UseConformantStrings && options.Supports_E_StringPrefix); } } else if (NativeData is Enum) { // Do a special handling of Enum values. // Translate enum value to its underlying type. backendSerialization = (String)Convert.ChangeType(Enum.Format(NativeData.GetType(), NativeData, "d"), typeof(String), CultureInfo.InvariantCulture); // Wrap the string in quotes. No 'E' is needed here. backendSerialization = QuoteString(backendSerialization, false); } else { bool escaped = false; if (NativeData is IFormattable) { backendSerialization = ((IFormattable)NativeData).ToString(null, ni); } // Do special handling of strings when in simple query. Escape quotes and possibly backslashes, depending on options. backendSerialization = EscapeString(NativeData.ToString(), options.UseConformantStrings, out escaped); if (Quote) { // Wrap the string in quotes and possibly prepend with 'E', depending on options and escaping. backendSerialization = QuoteString(backendSerialization, escaped && options.Supports_E_StringPrefix); } } return backendSerialization; } private object ConvertToBackendExtendedQuery(Object NativeData, NativeToBackendTypeConverterOptions options) { if ((NativeData == DBNull.Value) || (NativeData == null)) { return null; // Extended query expects null values be represented as null. } if (! NpgsqlTypesHelper.SuppressBinaryBackendEncoding && _ConvertNativeToBackendBinary != null) { return _ConvertNativeToBackendBinary(this, NativeData, options); } else if (_ConvertNativeToBackendText != null) { return _ConvertNativeToBackendText(this, NativeData, true, options); } else { if (NativeData is Enum) { // Do a special handling of Enum values. // Translate enum value to its underlying type. return (String) Convert.ChangeType(Enum.Format(NativeData.GetType(), NativeData, "d"), typeof (String), CultureInfo.InvariantCulture); } else if (NativeData is IFormattable) { return ((IFormattable) NativeData).ToString(null, ni); } return NativeData.ToString(); } } internal static string EscapeString(string src, bool StandardsConformingStrings, out bool backslashEscaped) { StringBuilder ret = new StringBuilder(); backslashEscaped = false; foreach (Char ch in src) { switch (ch) { case '\'': ret.AppendFormat("{0}{0}", ch); break; case '\\': if (! StandardsConformingStrings) { backslashEscaped = true; ret.AppendFormat("{0}{0}", ch); } else { ret.Append(ch); } break; default: ret.Append(ch); break; } } return ret.ToString(); } internal static String QuoteString(String S, bool use_E_Prefix) { return String.Format("{0}'{1}'", use_E_Prefix ? "E" : "", S); } } /// /// Provide mapping between type OID, type name, and a NpgsqlBackendTypeInfo object that represents it. /// internal class NpgsqlBackendTypeMapping { private readonly Dictionary OIDIndex; private readonly Dictionary NameIndex; /// /// Construct an empty mapping. /// public NpgsqlBackendTypeMapping() { OIDIndex = new Dictionary(); NameIndex = new Dictionary(); } /// /// Copy constuctor. /// private NpgsqlBackendTypeMapping(NpgsqlBackendTypeMapping Other) { OIDIndex = new Dictionary(Other.OIDIndex); NameIndex = new Dictionary(Other.NameIndex); } /// /// Add the given NpgsqlBackendTypeInfo to this mapping. /// public void AddType(NpgsqlBackendTypeInfo T) { if (OIDIndex.ContainsKey(T.OID)) { throw new Exception("Type already mapped"); } OIDIndex[T.OID] = T; NameIndex[T.Name] = T; } /// /// Add a new NpgsqlBackendTypeInfo with the given attributes and conversion handlers to this mapping. /// /// Type OID provided by the backend server. /// Type name provided by the backend server. /// NpgsqlDbType /// DbType /// System type to convert fields of this type to. /// Data conversion handler for text encoding. /// Data conversion handler for binary data. public void AddType(Int32 OID, String Name, NpgsqlDbType NpgsqlDbType, DbType DbType, Type Type, ConvertBackendTextToNativeHandler BackendTextConvert = null, ConvertBackendBinaryToNativeHandler BackendBinaryConvert = null) { AddType(new NpgsqlBackendTypeInfo(OID, Name, NpgsqlDbType, DbType, Type, BackendTextConvert = null, BackendBinaryConvert = null)); } /// /// Get the number of type infos held. /// public Int32 Count { get { return NameIndex.Count; } } public bool TryGetValue(int oid, out NpgsqlBackendTypeInfo value) { return OIDIndex.TryGetValue(oid, out value); } /// /// Retrieve the NpgsqlBackendTypeInfo with the given backend type OID, or null if none found. /// public NpgsqlBackendTypeInfo this[Int32 OID] { get { NpgsqlBackendTypeInfo ret = null; return TryGetValue(OID, out ret) ? ret : null; } } /// /// Retrieve the NpgsqlBackendTypeInfo with the given backend type name, or null if none found. /// public NpgsqlBackendTypeInfo this[String Name] { get { NpgsqlBackendTypeInfo ret = null; return NameIndex.TryGetValue(Name, out ret) ? ret : null; } } /// /// Make a shallow copy of this type mapping. /// public NpgsqlBackendTypeMapping Clone() { return new NpgsqlBackendTypeMapping(this); } /// /// Determine if a NpgsqlBackendTypeInfo with the given backend type OID exists in this mapping. /// public Boolean ContainsOID(Int32 OID) { return OIDIndex.ContainsKey(OID); } /// /// Determine if a NpgsqlBackendTypeInfo with the given backend type name exists in this mapping. /// public Boolean ContainsName(String Name) { return NameIndex.ContainsKey(Name); } } /// /// Provide mapping between type Type, NpgsqlDbType and a NpgsqlNativeTypeInfo object that represents it. /// internal class NpgsqlNativeTypeMapping { private readonly Dictionary NameIndex = new Dictionary(); private readonly Dictionary NpgsqlDbTypeIndex = new Dictionary(); private readonly Dictionary DbTypeIndex = new Dictionary(); private readonly Dictionary TypeIndex = new Dictionary(); /// /// Add the given NpgsqlNativeTypeInfo to this mapping. /// public void AddType(NpgsqlNativeTypeInfo T) { if (NameIndex.ContainsKey(T.Name)) { throw new Exception("Type already mapped"); } NameIndex[T.Name] = T; NpgsqlDbTypeIndex[T.NpgsqlDbType] = T; DbTypeIndex[T.DbType] = T; if (!T.IsArray) { NpgsqlNativeTypeInfo arrayType = NpgsqlNativeTypeInfo.ArrayOf(T); NameIndex[arrayType.Name] = arrayType; NameIndex[arrayType.CastName] = arrayType; NpgsqlDbTypeIndex[arrayType.NpgsqlDbType] = arrayType; } } /// /// Add a new NpgsqlNativeTypeInfo with the given attributes and conversion handlers to this mapping. /// /// Type name provided by the backend server. /// NpgsqlDbType /// DbType /// Quote /// Data conversion handler for text backend encoding. /// Data conversion handler for binary backend encoding (for extended query). public void AddType(String Name, NpgsqlDbType NpgsqlDbType, DbType DbType, Boolean Quote, ConvertNativeToBackendTextHandler NativeTextConvert = null, ConvertNativeToBackendBinaryHandler NativeBinaryConvert = null) { AddType(new NpgsqlNativeTypeInfo(Name, NpgsqlDbType, DbType, Quote, NativeTextConvert, NativeBinaryConvert)); } public void AddNpgsqlDbTypeAlias(String Name, NpgsqlDbType NpgsqlDbType) { if (NpgsqlDbTypeIndex.ContainsKey(NpgsqlDbType)) { throw new Exception("NpgsqlDbType already aliased"); } NpgsqlDbTypeIndex[NpgsqlDbType] = NameIndex[Name]; } public void AddDbTypeAlias(String Name, DbType DbType) { /*if (DbTypeIndex.ContainsKey(DbType)) { throw new Exception("DbType already aliased"); }*/ DbTypeIndex[DbType] = NameIndex[Name]; } public void AddTypeAlias(String Name, Type Type) { if (TypeIndex.ContainsKey(Type)) { throw new Exception("Type already aliased"); } TypeIndex[Type] = NameIndex[Name]; } /// /// Get the number of type infos held. /// public Int32 Count { get { return NameIndex.Count; } } public bool TryGetValue(string name, out NpgsqlNativeTypeInfo typeInfo) { return NameIndex.TryGetValue(name, out typeInfo); } /// /// Retrieve the NpgsqlNativeTypeInfo with the given NpgsqlDbType. /// public bool TryGetValue(NpgsqlDbType dbType, out NpgsqlNativeTypeInfo typeInfo) { return NpgsqlDbTypeIndex.TryGetValue(dbType, out typeInfo); } /// /// Retrieve the NpgsqlNativeTypeInfo with the given DbType. /// public bool TryGetValue(DbType dbType, out NpgsqlNativeTypeInfo typeInfo) { return DbTypeIndex.TryGetValue(dbType, out typeInfo); } /// /// Retrieve the NpgsqlNativeTypeInfo with the given Type. /// public bool TryGetValue(Type type, out NpgsqlNativeTypeInfo typeInfo) { return TypeIndex.TryGetValue(type, out typeInfo); } /// /// Determine if a NpgsqlNativeTypeInfo with the given backend type name exists in this mapping. /// public Boolean ContainsName(String Name) { return NameIndex.ContainsKey(Name); } /// /// Determine if a NpgsqlNativeTypeInfo with the given NpgsqlDbType exists in this mapping. /// public Boolean ContainsNpgsqlDbType(NpgsqlDbType NpgsqlDbType) { return NpgsqlDbTypeIndex.ContainsKey(NpgsqlDbType); } /// /// Determine if a NpgsqlNativeTypeInfo with the given Type name exists in this mapping. /// public Boolean ContainsType(Type Type) { return TypeIndex.ContainsKey(Type); } } internal static class ExpectedTypeConverter { internal static object ChangeType(object value, Type expectedType) { if (value == null) return null; Type currentType = value.GetType(); if (value is DBNull || currentType == expectedType) return value; #if NET35 if (expectedType == typeof(DateTimeOffset)) { if (currentType == typeof(NpgsqlDate)) { return new DateTimeOffset((DateTime)(NpgsqlDate)value); } else if (currentType == typeof(NpgsqlTime)) { return new DateTimeOffset((DateTime)(NpgsqlTime)value); } else if (currentType == typeof(NpgsqlTimeTZ)) { NpgsqlTimeTZ timetz = (NpgsqlTimeTZ)value; return new DateTimeOffset(timetz.Ticks, new TimeSpan(timetz.TimeZone.Hours, timetz.TimeZone.Minutes, timetz.TimeZone.Seconds)); } else if (currentType == typeof(NpgsqlTimeStamp)) { return new DateTimeOffset((DateTime)(NpgsqlTimeStamp)value); } else if (currentType == typeof(NpgsqlTimeStampTZ)) { NpgsqlTimeStampTZ timestamptz = (NpgsqlTimeStampTZ)value; return new DateTimeOffset(timestamptz.Ticks, new TimeSpan(timestamptz.TimeZone.Hours, timestamptz.TimeZone.Minutes, timestamptz.TimeZone.Seconds)); } else if (currentType == typeof(NpgsqlInterval)) { return new DateTimeOffset(((TimeSpan)(NpgsqlInterval)value).Ticks, TimeSpan.FromSeconds(0)); } else if (currentType == typeof(DateTime)) { return new DateTimeOffset((DateTime)value); } else if (currentType == typeof(TimeSpan)) { return new DateTimeOffset(((TimeSpan)value).Ticks, TimeSpan.FromSeconds(0)); } else { return DateTimeOffset.Parse(value.ToString(), CultureInfo.InvariantCulture); } } else #endif if (expectedType == typeof(TimeSpan)) { if (currentType == typeof(NpgsqlDate)) { return new TimeSpan(((DateTime)(NpgsqlDate)value).Ticks); } else if (currentType == typeof(NpgsqlTime)) { return new TimeSpan(((NpgsqlTime)value).Ticks); } else if (currentType == typeof(NpgsqlTimeTZ)) { return new TimeSpan(((NpgsqlTimeTZ)value).UTCTime.Ticks); } else if (currentType == typeof(NpgsqlTimeStamp)) { return new TimeSpan(((NpgsqlTimeStamp)value).Ticks); } else if (currentType == typeof(NpgsqlTimeStampTZ)) { return new TimeSpan(((DateTime)(NpgsqlTimeStampTZ)value).ToUniversalTime().Ticks); } else if (currentType == typeof(NpgsqlInterval)) { return (TimeSpan)(NpgsqlInterval)value; } else if (currentType == typeof(DateTime)) { return new TimeSpan(((DateTime)value).ToUniversalTime().Ticks); } else if (currentType == typeof(DateTimeOffset)) { return new TimeSpan(((DateTimeOffset)value).Ticks); } else { #if NET40 return TimeSpan.Parse(value.ToString(), CultureInfo.InvariantCulture); #else return TimeSpan.Parse(value.ToString()); #endif } } else if (expectedType == typeof(string)) { return value.ToString(); } else if (expectedType == typeof(Guid)) { if (currentType == typeof(byte[])) { return new Guid((byte[])value); } else { return new Guid(value.ToString()); } } else if (expectedType == typeof(DateTime)) { if (currentType == typeof(NpgsqlDate)) { return (DateTime)(NpgsqlDate)value; } else if (currentType == typeof(NpgsqlTime)) { return (DateTime)(NpgsqlTime)value; } else if (currentType == typeof(NpgsqlTimeTZ)) { return (DateTime)(NpgsqlTimeTZ)value; } else if (currentType == typeof(NpgsqlTimeStamp)) { return (DateTime)(NpgsqlTimeStamp)value; } else if (currentType == typeof(NpgsqlTimeStampTZ)) { return (DateTime)(NpgsqlTimeStampTZ)value; } else if (currentType == typeof(NpgsqlInterval)) { return new DateTime(((TimeSpan)(NpgsqlInterval)value).Ticks); } #if NET35 else if (currentType == typeof(DateTimeOffset)) { return ((DateTimeOffset)value).LocalDateTime; } #endif else if (currentType == typeof(TimeSpan)) { return new DateTime(((TimeSpan)value).Ticks); } else { return DateTime.Parse(value.ToString(), CultureInfo.InvariantCulture); } } else if (expectedType == typeof(byte[])) { if (currentType == typeof(Guid)) { return ((Guid)value).ToByteArray(); } else if (value is Array) { Array valueArray = (Array)value; int byteLength = Buffer.ByteLength(valueArray); byte[] bytes = new byte[byteLength]; Buffer.BlockCopy(valueArray, 0, bytes, 0, byteLength); return bytes; } else { // expect InvalidCastException from this call return Convert.ChangeType(value, expectedType); } } else // long, int, short, double, float, decimal, byte, sbyte, bool, and other unspecified types { // ChangeType supports the conversions we want for above expected types return Convert.ChangeType(value, expectedType); } } } }
X Tutup