// 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);
}
}
}
}