2004-09-12 Francisco Figueiredo Jr. <fxjrlists@yahoo.com.br>
[mono.git] / mcs / class / Npgsql / NpgsqlTypes / NpgsqlTypesHelper.cs
1 // NpgsqlTypes.NpgsqlTypesHelper.cs
2 //
3 // Author:
4 //      Francisco Jr. (fxjrlists@yahoo.com.br)
5 //
6 //      Copyright (C) 2002 The Npgsql Development Team
7 //      npgsql-general@gborg.postgresql.org
8 //      http://gborg.postgresql.org/project/npgsql/projdisplay.php
9 //
10 // This library is free software; you can redistribute it and/or
11 // modify it under the terms of the GNU Lesser General Public
12 // License as published by the Free Software Foundation; either
13 // version 2.1 of the License, or (at your option) any later version.
14 //
15 // This library is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18 // Lesser General Public License for more details.
19 //
20 // You should have received a copy of the GNU Lesser General Public
21 // License along with this library; if not, write to the Free Software
22 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23
24 using System;
25 using System.Collections;
26 using System.Globalization;
27 using System.Data;
28 using System.Net;
29 using System.Text;
30 using System.Resources;
31 using Npgsql;
32
33
34 namespace NpgsqlTypes
35 {
36     /// <summary>
37     /// This class contains helper methods for type conversion between
38     /// the .Net type system and postgresql.
39     /// </summary>
40     internal abstract class NpgsqlTypesHelper
41     {
42         // Logging related values
43         private static readonly String CLASSNAME = "NpgsqlTypesHelper";
44         private static ResourceManager resman = new ResourceManager(typeof(NpgsqlTypesHelper));
45
46         /// <summary>
47         /// A cache of basic datatype mappings keyed by server version.  This way we don't
48         /// have to load the basic type mappings for every connection.
49         /// </summary>
50         private static Hashtable BackendTypeMappingCache = new Hashtable();
51         private static NpgsqlNativeTypeMapping NativeTypeMapping = null;
52
53
54         /// <summary>
55         /// Find a NpgsqlNativeTypeInfo in the default types map that can handle objects
56         /// of the given NpgsqlDbType.
57         /// </summary>
58         public static NpgsqlNativeTypeInfo GetNativeTypeInfo(NpgsqlDbType NpgsqlDbType)
59         {
60             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetBackendTypeNameFromNpgsqlDbType");
61
62             VerifyDefaultTypesMap();
63             return NativeTypeMapping[NpgsqlDbType];
64         }
65         
66         /// <summary>
67         /// Find a NpgsqlNativeTypeInfo in the default types map that can handle objects
68         /// of the given DbType.
69         /// </summary>
70         public static NpgsqlNativeTypeInfo GetNativeTypeInfo(DbType DbType)
71         {
72             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetBackendTypeNameFromNpgsqlDbType");
73
74             VerifyDefaultTypesMap();
75             return NativeTypeMapping[DbType];
76         }
77         
78         
79
80         /// <summary>
81         /// Find a NpgsqlNativeTypeInfo in the default types map that can handle objects
82         /// of the given System.Type.
83         /// </summary>
84         public static NpgsqlNativeTypeInfo GetNativeTypeInfo(Type Type)
85         {
86             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetBackendTypeNameFromNpgsqlDbType");
87
88             VerifyDefaultTypesMap();
89             return NativeTypeMapping[Type];
90         }
91
92         // CHECKME
93         // Not sure what to do with this one.  I don't believe we ever ask for a binary
94         // formatting, so this shouldn't even be used right now.
95         // At some point this will need to be merged into the type converter system somehow?
96         public static Object ConvertBackendBytesToSystemType(NpgsqlBackendTypeInfo TypeInfo, Byte[] data, Encoding encoding, Int32 fieldValueSize, Int32 typeModifier)
97         {
98             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ConvertBackendBytesToStytemType");
99
100             /*
101             // We are never guaranteed to know about every possible data type the server can send us.
102             // When we encounter an unknown type, we punt and return the data without modification.
103             if (TypeInfo == null)
104                 return data;
105
106             switch (TypeInfo.NpgsqlDbType)
107             {
108             case NpgsqlDbType.Binary:
109                 return data;
110             case NpgsqlDbType.Boolean:
111                 return BitConverter.ToBoolean(data, 0);
112             case NpgsqlDbType.DateTime:
113                 return DateTime.MinValue.AddTicks(IPAddress.NetworkToHostOrder(BitConverter.ToInt64(data, 0)));
114
115             case NpgsqlDbType.Int16:
116                 return IPAddress.NetworkToHostOrder(BitConverter.ToInt16(data, 0));
117             case NpgsqlDbType.Int32:
118                 return IPAddress.NetworkToHostOrder(BitConverter.ToInt32(data, 0));
119             case NpgsqlDbType.Int64:
120                 return IPAddress.NetworkToHostOrder(BitConverter.ToInt64(data, 0));
121             case NpgsqlDbType.String:
122             case NpgsqlDbType.AnsiString:
123             case NpgsqlDbType.StringFixedLength:
124                 return encoding.GetString(data, 0, fieldValueSize);
125             default:
126                 throw new InvalidCastException("Type not supported in binary format");
127             }*/
128             
129             return null;
130         }
131
132         ///<summary>
133         /// This method is responsible to convert the string received from the backend
134         /// to the corresponding NpgsqlType.
135         /// The given TypeInfo is called upon to do the conversion.
136         /// If no TypeInfo object is provided, no conversion is performed.
137         /// </summary>
138         public static Object ConvertBackendStringToSystemType(NpgsqlBackendTypeInfo TypeInfo, String data, Int16 typeSize, Int32 typeModifier)
139         {
140             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "ConvertBackendStringToSystemType");
141
142             if (TypeInfo != null) {
143                 return TypeInfo.ConvertToNative(data, typeSize, typeModifier);
144             } else {
145                 return data;
146             }
147         }
148
149         /// <summary>
150         /// Create the one and only native to backend type map.
151         /// This map is used when formatting native data
152         /// types to backend representations.
153         /// </summary>
154         private static void VerifyDefaultTypesMap()
155         {
156             lock(CLASSNAME) {
157                 if (NativeTypeMapping != null) {
158                     return;
159                 }
160
161                 NativeTypeMapping = new NpgsqlNativeTypeMapping();
162
163                 NativeTypeMapping.AddType("text", NpgsqlDbType.Text, DbType.String, true,
164                 new ConvertNativeToBackendHandler(BasicNativeToBackendTypeConverter.ToString));
165
166                 NativeTypeMapping.AddDbTypeAlias("text", DbType.StringFixedLength);
167                 NativeTypeMapping.AddDbTypeAlias("text", DbType.AnsiString);
168                 NativeTypeMapping.AddDbTypeAlias("text", DbType.AnsiStringFixedLength);
169                 NativeTypeMapping.AddTypeAlias("text", typeof(String));
170
171                 NativeTypeMapping.AddType("bytea", NpgsqlDbType.Bytea, DbType.Binary, true,
172                 new ConvertNativeToBackendHandler(BasicNativeToBackendTypeConverter.ToBinary));
173
174                 NativeTypeMapping.AddTypeAlias("bytea", typeof(Byte[]));
175
176                 NativeTypeMapping.AddType("bool", NpgsqlDbType.Boolean, DbType.Boolean, false,
177                 new ConvertNativeToBackendHandler(BasicNativeToBackendTypeConverter.ToBoolean));
178
179                 NativeTypeMapping.AddTypeAlias("bool", typeof(Boolean));
180                                 
181                 NativeTypeMapping.AddType("int2", NpgsqlDbType.Smallint, DbType.Int16, false,
182                 null);
183
184                 NativeTypeMapping.AddTypeAlias("int2", typeof(Int16));
185                 
186                 NativeTypeMapping.AddDbTypeAlias("int2", DbType.Byte);
187
188                 NativeTypeMapping.AddType("int4", NpgsqlDbType.Integer, DbType.Int32, false,
189                 null);
190
191                 NativeTypeMapping.AddTypeAlias("int4", typeof(Int32));
192
193                 NativeTypeMapping.AddType("int8", NpgsqlDbType.Bigint, DbType.Int64, false,
194                 null);
195
196                 NativeTypeMapping.AddTypeAlias("int8", typeof(Int64));
197
198                 NativeTypeMapping.AddType("float4", NpgsqlDbType.Real, DbType.Single, false,
199                 null);
200
201                 NativeTypeMapping.AddTypeAlias("float4", typeof(Single));
202
203                 NativeTypeMapping.AddType("float8", NpgsqlDbType.Double, DbType.Double, false,
204                 null);
205
206                 NativeTypeMapping.AddTypeAlias("float8", typeof(Double));
207
208                 NativeTypeMapping.AddType("numeric", NpgsqlDbType.Numeric, DbType.Decimal, false,
209                 null);
210
211                 NativeTypeMapping.AddTypeAlias("numeric", typeof(Decimal));
212
213                 NativeTypeMapping.AddType("currency", NpgsqlDbType.Money, DbType.Currency, true,
214                 new ConvertNativeToBackendHandler(BasicNativeToBackendTypeConverter.ToMoney));
215
216                 NativeTypeMapping.AddType("date", NpgsqlDbType.Date, DbType.Date, true,
217                 new ConvertNativeToBackendHandler(BasicNativeToBackendTypeConverter.ToDate));
218
219                 NativeTypeMapping.AddType("time", NpgsqlDbType.Time, DbType.Time, true,
220                 new ConvertNativeToBackendHandler(BasicNativeToBackendTypeConverter.ToTime));
221
222                 NativeTypeMapping.AddType("timestamp", NpgsqlDbType.Timestamp, DbType.DateTime, true,
223                 new ConvertNativeToBackendHandler(BasicNativeToBackendTypeConverter.ToDateTime));
224
225                 NativeTypeMapping.AddTypeAlias("timestamp", typeof(DateTime));
226
227                 NativeTypeMapping.AddType("point", NpgsqlDbType.Point, DbType.Object, true,
228                 new ConvertNativeToBackendHandler(ExtendedNativeToBackendTypeConverter.ToPoint));
229
230                 NativeTypeMapping.AddTypeAlias("point", typeof(NpgsqlPoint));
231                 
232                 NativeTypeMapping.AddType("box", NpgsqlDbType.Box, DbType.Object, true,
233                 new ConvertNativeToBackendHandler(ExtendedNativeToBackendTypeConverter.ToBox));
234
235                 NativeTypeMapping.AddTypeAlias("box", typeof(NpgsqlBox));
236                 
237                 NativeTypeMapping.AddType("lseg", NpgsqlDbType.LSeg, DbType.Object, true,
238                 new ConvertNativeToBackendHandler(ExtendedNativeToBackendTypeConverter.ToLSeg));
239
240                 NativeTypeMapping.AddTypeAlias("lseg", typeof(NpgsqlLSeg));
241
242                 NativeTypeMapping.AddType("path", NpgsqlDbType.Path, DbType.Object, true,
243                 new ConvertNativeToBackendHandler(ExtendedNativeToBackendTypeConverter.ToPath));
244
245                 NativeTypeMapping.AddTypeAlias("path", typeof(NpgsqlPath));
246
247                 NativeTypeMapping.AddType("polygon", NpgsqlDbType.Polygon, DbType.Object, true,
248                 new ConvertNativeToBackendHandler(ExtendedNativeToBackendTypeConverter.ToPolygon));
249
250                 NativeTypeMapping.AddTypeAlias("polygon", typeof(NpgsqlPolygon));
251
252                 NativeTypeMapping.AddType("circle", NpgsqlDbType.Circle, DbType.Object, true,
253                 new ConvertNativeToBackendHandler(ExtendedNativeToBackendTypeConverter.ToCircle));
254
255                 NativeTypeMapping.AddTypeAlias("circle", typeof(NpgsqlCircle));
256             }
257         }
258
259         ///<summary>
260         /// This method creates (or retrieves from cache) a mapping between type and OID 
261         /// of all natively supported postgresql data types.
262         /// This is needed as from one version to another, this mapping can be changed and
263         /// so we avoid hardcoding them.
264         /// </summary>
265         /// <returns>NpgsqlTypeMapping containing all known data types.  The mapping must be
266         /// cloned before it is modified because it is cached; changes made by one connection may
267         /// effect another connection.</returns>
268         public static NpgsqlBackendTypeMapping CreateAndLoadInitialTypesMapping(NpgsqlConnector conn)
269         {
270             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "LoadTypesMapping");
271
272             // [TODO] Verify another way to get higher concurrency.
273             lock(CLASSNAME)
274             {
275                 // Check the cache for an initial types map.
276                 NpgsqlBackendTypeMapping oidToNameMapping = (NpgsqlBackendTypeMapping) BackendTypeMappingCache[conn.ServerVersion];
277
278                 if (oidToNameMapping != null)
279                 {
280                     return oidToNameMapping;
281                 }
282
283                 // Not in cache, create a new one.
284                 oidToNameMapping = new NpgsqlBackendTypeMapping();
285
286                 // Create a list of all natively supported postgresql data types.
287                 NpgsqlBackendTypeInfo[] TypeInfoList = new NpgsqlBackendTypeInfo[]
288                 {
289                     new NpgsqlBackendTypeInfo(0, "unknown", NpgsqlDbType.Text, DbType.String, typeof(String),
290                         null),
291
292                     new NpgsqlBackendTypeInfo(0, "char", NpgsqlDbType.Text, DbType.String, typeof(String),
293                         null),
294
295                     new NpgsqlBackendTypeInfo(0, "bpchar", NpgsqlDbType.Text, DbType.String, typeof(String),
296                         null),
297
298                     new NpgsqlBackendTypeInfo(0, "varchar", NpgsqlDbType.Text, DbType.String, typeof(String),
299                         null),
300
301                     new NpgsqlBackendTypeInfo(0, "text", NpgsqlDbType.Text, DbType.String, typeof(String),
302                         null),
303                         
304                     new NpgsqlBackendTypeInfo(0, "name", NpgsqlDbType.Text, DbType.String, typeof(String),
305                         null),
306
307                     new NpgsqlBackendTypeInfo(0, "bytea", NpgsqlDbType.Bytea, DbType.Binary, typeof(Byte[]),
308                         new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter.ToBinary)),
309
310
311                     new NpgsqlBackendTypeInfo(0, "bool", NpgsqlDbType.Boolean, DbType.Boolean, typeof(Boolean),
312                         new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter.ToBoolean)),
313
314
315                     new NpgsqlBackendTypeInfo(0, "int2", NpgsqlDbType.Smallint, DbType.Int16, typeof(Int16),
316                         null),
317
318                     new NpgsqlBackendTypeInfo(0, "int4", NpgsqlDbType.Integer, DbType.Int32, typeof(Int32),
319                         null),
320
321                     new NpgsqlBackendTypeInfo(0, "int8", NpgsqlDbType.Bigint, DbType.Int64, typeof(Int64),
322                         null),
323
324                     new NpgsqlBackendTypeInfo(0, "oid", NpgsqlDbType.Bigint, DbType.Int64, typeof(Int64),
325                         null),
326
327
328                     new NpgsqlBackendTypeInfo(0, "float4", NpgsqlDbType.Real, DbType.Single, typeof(Single),
329                         null),
330
331                     new NpgsqlBackendTypeInfo(0, "float8", NpgsqlDbType.Double, DbType.Double, typeof(Double),
332                         null),
333
334                     new NpgsqlBackendTypeInfo(0, "numeric", NpgsqlDbType.Numeric, DbType.Decimal, typeof(Decimal),
335                         null),
336
337                     new NpgsqlBackendTypeInfo(0, "money", NpgsqlDbType.Money, DbType.Decimal, typeof(Decimal),
338                         new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter.ToMoney)),
339
340
341                     new NpgsqlBackendTypeInfo(0, "date", NpgsqlDbType.Date, DbType.Date, typeof(DateTime),
342                         new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter.ToDate)),
343
344                     new NpgsqlBackendTypeInfo(0, "time", NpgsqlDbType.Time, DbType.Time, typeof(DateTime),
345                         new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter.ToTime)),
346
347                     new NpgsqlBackendTypeInfo(0, "timetz", NpgsqlDbType.Time, DbType.Time, typeof(DateTime),
348                         new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter.ToTime)),
349
350                     new NpgsqlBackendTypeInfo(0, "timestamp", NpgsqlDbType.Timestamp, DbType.DateTime, typeof(DateTime),
351                         new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter.ToDateTime)),
352
353                     new NpgsqlBackendTypeInfo(0, "timestamptz", NpgsqlDbType.Timestamp, DbType.DateTime, typeof(DateTime),
354                         new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter.ToDateTime)),
355
356
357                     new NpgsqlBackendTypeInfo(0, "point", NpgsqlDbType.Point, DbType.Object, typeof(NpgsqlPoint),
358                         new ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter.ToPoint)),
359
360                     new NpgsqlBackendTypeInfo(0, "lseg", NpgsqlDbType.LSeg, DbType.Object, typeof(NpgsqlLSeg),
361                         new ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter.ToLSeg)),
362
363                     new NpgsqlBackendTypeInfo(0, "path", NpgsqlDbType.Path, DbType.Object, typeof(NpgsqlPath),
364                         new ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter.ToPath)),
365
366                     new NpgsqlBackendTypeInfo(0, "box", NpgsqlDbType.Box, DbType.Object, typeof(NpgsqlBox),
367                         new ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter.ToBox)),
368
369                     new NpgsqlBackendTypeInfo(0, "circle", NpgsqlDbType.Circle, DbType.Object, typeof(NpgsqlCircle),
370                         new ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter.ToCircle)),
371
372                     new NpgsqlBackendTypeInfo(0, "polygon", NpgsqlDbType.Polygon, DbType.Object, typeof(NpgsqlPolygon),
373                         new ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter.ToPolygon)),
374                 };
375
376                 // Attempt to map each type info in the list to an OID on the backend and
377                 // add each mapped type to the new type mapping object.
378                 LoadTypesMappings(conn, oidToNameMapping, TypeInfoList);
379
380                 // Add this mapping to the per-server-version cache so we don't have to
381                 // do these expensive queries on every connection startup.
382                 BackendTypeMappingCache.Add(conn.ServerVersion, oidToNameMapping);
383
384                 return oidToNameMapping;
385             }
386
387
388         }
389
390         /// <summary>
391         /// Attempt to map types by issuing a query against pg_type.
392         /// This function takes a list of NpgsqlTypeInfo and attempts to resolve the OID field
393         /// of each by querying pg_type.  If the mapping is found, the type info object is
394         /// updated (OID) and added to the provided NpgsqlTypeMapping object.
395         /// </summary>
396         /// <param name="conn">NpgsqlConnector to send query through.</param>
397         /// <param name="TypeMappings">Mapping object to add types too.</param>
398         /// <param name="TypeInfoList">List of types that need to have OID's mapped.</param>
399         public static void LoadTypesMappings(NpgsqlConnector conn, NpgsqlBackendTypeMapping TypeMappings, IList TypeInfoList)
400         {
401             StringBuilder       InList = new StringBuilder();
402             Hashtable           NameIndex = new Hashtable();
403
404             // Build a clause for the SELECT statement.
405             // Build a name->typeinfo mapping so we can match the results of the query
406             /// with the list of type objects efficiently.
407             foreach (NpgsqlBackendTypeInfo TypeInfo in TypeInfoList) {
408                 NameIndex.Add(TypeInfo.Name, TypeInfo);
409                 InList.AppendFormat("{0}'{1}'", ((InList.Length > 0) ? ", " : ""), TypeInfo.Name);
410             }
411
412             if (InList.Length == 0) {
413                 return;
414             }
415
416             NpgsqlCommand       command = new NpgsqlCommand("SELECT oid, typname FROM pg_type WHERE typname IN (" + InList.ToString() + ")", conn);
417             NpgsqlDataReader    dr = command.ExecuteReader();
418
419             while (dr.Read()) {
420                 NpgsqlBackendTypeInfo TypeInfo = (NpgsqlBackendTypeInfo)NameIndex[dr[1].ToString()];
421
422                 TypeInfo._OID = Convert.ToInt32(dr[0]);
423
424                 TypeMappings.AddType(TypeInfo);
425             }
426         }
427     }
428
429     /// <summary>
430     /// Delegate called to convert the given backend data to its native representation.
431     /// </summary>
432     internal delegate Object ConvertBackendToNativeHandler(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier);
433     /// <summary>
434     /// Delegate called to convert the given native data to its backand representation.
435     /// </summary>
436     internal delegate String ConvertNativeToBackendHandler(NpgsqlNativeTypeInfo TypeInfo, Object NativeData);
437
438     /// <summary>
439     /// Represents a backend data type.
440     /// This class can be called upon to convert a backend field representation to a native object.
441     /// </summary>
442     internal class NpgsqlBackendTypeInfo
443     {
444         private event ConvertBackendToNativeHandler _ConvertBackendToNative;
445
446         internal Int32           _OID;
447         private String           _Name;
448         private NpgsqlDbType     _NpgsqlDbType;
449         private DbType           _DbType;
450         private Type             _Type;
451
452         /// <summary>
453         /// Construct a new NpgsqlTypeInfo with the given attributes and conversion handlers.
454         /// </summary>
455         /// <param name="OID">Type OID provided by the backend server.</param>
456         /// <param name="Name">Type name provided by the backend server.</param>
457         /// <param name="NpgsqlDbType">NpgsqlDbType</param>
458         /// <param name="Type">System type to convert fields of this type to.</param>
459         /// <param name="ConvertBackendToNative">Data conversion handler.</param>
460         public NpgsqlBackendTypeInfo(Int32 OID, String Name, NpgsqlDbType NpgsqlDbType, DbType DbType, Type Type,
461                               ConvertBackendToNativeHandler ConvertBackendToNative)
462         {
463             _OID = OID;
464             _Name = Name;
465             _NpgsqlDbType = NpgsqlDbType;
466             _DbType = DbType;
467             _Type = Type;
468             _ConvertBackendToNative = ConvertBackendToNative;
469         }
470
471         /// <summary>
472         /// Type OID provided by the backend server.
473         /// </summary>
474         public Int32 OID
475         {
476           get { return _OID; }
477         }
478
479         /// <summary>
480         /// Type name provided by the backend server.
481         /// </summary>
482         public String Name
483         { get { return _Name; } }
484
485         /// <summary>
486         /// NpgsqlDbType.
487         /// </summary>
488         public NpgsqlDbType NpgsqlDbType
489         { get { return _NpgsqlDbType; } }
490
491         /// <summary>
492         /// NpgsqlDbType.
493         /// </summary>
494         public DbType DbType
495         { get { return _DbType; } }
496         
497         /// <summary>
498         /// System type to convert fields of this type to.
499         /// </summary>
500         public Type Type
501         { get { return _Type; } }
502
503         /// <summary>
504         /// Perform a data conversion from a backend representation to 
505         /// a native object.
506         /// </summary>
507         /// <param name="BackendData">Data sent from the backend.</param>
508         /// <param name="TypeModifier">Type modifier field sent from the backend.</param>
509         public Object ConvertToNative(String BackendData, Int16 TypeSize, Int32 TypeModifier)
510         {
511             if (_ConvertBackendToNative != null) {
512                 return _ConvertBackendToNative(this, BackendData, TypeSize, TypeModifier);
513             } else {
514                 try {
515                     return Convert.ChangeType(BackendData, Type, CultureInfo.InvariantCulture);
516                 } catch {
517                     return BackendData;
518                 }
519             }
520         }
521     }
522
523     /// <summary>
524     /// Represents a backend data type.
525     /// This class can be called upon to convert a native object to its backend field representation,
526     /// </summary>
527     internal class NpgsqlNativeTypeInfo
528     {
529         private event ConvertNativeToBackendHandler _ConvertNativeToBackend;
530
531         private String           _Name;
532         private NpgsqlDbType     _NpgsqlDbType;
533         private DbType           _DbType;
534         private Boolean          _Quote;
535
536         /// <summary>
537         /// Construct a new NpgsqlTypeInfo with the given attributes and conversion handlers.
538         /// </summary>
539         /// <param name="OID">Type OID provided by the backend server.</param>
540         /// <param name="Name">Type name provided by the backend server.</param>
541         /// <param name="NpgsqlDbType">NpgsqlDbType</param>
542         /// <param name="Type">System type to convert fields of this type to.</param>
543         /// <param name="ConvertBackendToNative">Data conversion handler.</param>
544         /// <param name="ConvertNativeToBackend">Data conversion handler.</param>
545         public NpgsqlNativeTypeInfo(String Name, NpgsqlDbType NpgsqlDbType, DbType DbType, Boolean Quote,
546                               ConvertNativeToBackendHandler ConvertNativeToBackend)
547         {
548             _Name = Name;
549             _NpgsqlDbType = NpgsqlDbType;
550             _DbType = DbType;
551             _Quote = Quote;
552             _ConvertNativeToBackend = ConvertNativeToBackend;
553         }
554
555         /// <summary>
556         /// Type name provided by the backend server.
557         /// </summary>
558         public String Name
559         { get { return _Name; } }
560
561         /// <summary>
562         /// NpgsqlDbType.
563         /// </summary>
564         public NpgsqlDbType NpgsqlDbType
565         { get { return _NpgsqlDbType; } }
566
567         /// <summary>
568         /// DbType.
569         /// </summary>
570         public DbType DbType
571         { get { return _DbType; } }
572         
573         
574         /// <summary>
575         /// Apply quoting.
576         /// </summary>
577         public Boolean Quote
578         { get { return _Quote; } }
579
580         /// <summary>
581         /// Perform a data conversion from a native object to
582         /// a backend representation.
583         /// DBNull and null values are handled differently depending if a plain query is used
584         /// When 
585         /// </summary>
586         /// <param name="NativeData">Native .NET object to be converted.</param>
587         /// <param name="ForExtendedQuery">Flag indicating if the conversion has to be done for 
588                 /// plain queries or extended queries</param>
589         public String ConvertToBackend(Object NativeData, Boolean ForExtendedQuery)
590         {
591             if ((NativeData == DBNull.Value) || (NativeData == null))
592                 if (ForExtendedQuery)
593                         return null;    // Extended query expects null values be represented as null.
594                 else
595                         return "NULL";  // Plain queries exptects null values as string NULL. 
596                             
597             else if (_ConvertNativeToBackend != null) {
598                 return QuoteString(! ForExtendedQuery, _ConvertNativeToBackend(this, NativeData));
599                 
600             } else {
601                 return QuoteString(! ForExtendedQuery, (String)Convert.ChangeType(NativeData, typeof(String), CultureInfo.InvariantCulture));
602             }
603         }
604
605         private static String QuoteString(Boolean Quote, String S)
606         {
607             if (Quote) {
608                 return String.Format("'{0}'", S);
609             } else {
610                 return S;
611             }
612         }
613     }
614
615     /// <summary>
616     /// Provide mapping between type OID, type name, and a NpgsqlBackendTypeInfo object that represents it.
617     /// </summary>
618     internal class NpgsqlBackendTypeMapping
619     {
620         private Hashtable       OIDIndex;
621         private Hashtable       NameIndex;
622
623         /// <summary>
624         /// Construct an empty mapping.
625         /// </summary>
626         public NpgsqlBackendTypeMapping()
627         {
628             OIDIndex = new Hashtable();
629             NameIndex = new Hashtable();
630         }
631
632         /// <summary>
633         /// Copy constuctor.
634         /// </summary>
635         private NpgsqlBackendTypeMapping(NpgsqlBackendTypeMapping Other)
636         {
637             OIDIndex = (Hashtable)Other.OIDIndex.Clone();
638             NameIndex = (Hashtable)Other.NameIndex.Clone();
639         }
640
641         /// <summary>
642         /// Add the given NpgsqlBackendTypeInfo to this mapping.
643         /// </summary>
644         public void AddType(NpgsqlBackendTypeInfo T)
645         {
646             if (OIDIndex.Contains(T.OID)) {
647                 throw new Exception("Type already mapped");
648             }
649
650             OIDIndex[T.OID] = T;
651             NameIndex[T.Name] = T;
652         }
653
654         /// <summary>
655         /// Add a new NpgsqlBackendTypeInfo with the given attributes and conversion handlers to this mapping.
656         /// </summary>
657         /// <param name="OID">Type OID provided by the backend server.</param>
658         /// <param name="Name">Type name provided by the backend server.</param>
659         /// <param name="NpgsqlDbType">NpgsqlDbType</param>
660         /// <param name="Type">System type to convert fields of this type to.</param>
661         /// <param name="ConvertBackendToNative">Data conversion handler.</param>
662         public void AddType(Int32 OID, String Name, NpgsqlDbType NpgsqlDbType, DbType DbType, Type Type,
663                             ConvertBackendToNativeHandler BackendConvert)
664         {
665             AddType(new NpgsqlBackendTypeInfo(OID, Name, NpgsqlDbType, DbType, Type, BackendConvert));
666         }
667
668         /// <summary>
669         /// Get the number of type infos held.
670         /// </summary>
671         public Int32 Count
672         { get { return NameIndex.Count; } }
673
674         /// <summary>
675         /// Retrieve the NpgsqlBackendTypeInfo with the given backend type OID, or null if none found.
676         /// </summary>
677         public NpgsqlBackendTypeInfo this [Int32 OID]
678         {
679             get
680             {
681                 return (NpgsqlBackendTypeInfo)OIDIndex[OID];
682             }
683         }
684
685         /// <summary>
686         /// Retrieve the NpgsqlBackendTypeInfo with the given backend type name, or null if none found.
687         /// </summary>
688         public NpgsqlBackendTypeInfo this [String Name]
689         {
690             get
691             {
692                 return (NpgsqlBackendTypeInfo)NameIndex[Name];
693             }
694         }
695
696         /// <summary>
697         /// Make a shallow copy of this type mapping.
698         /// </summary>
699         public NpgsqlBackendTypeMapping Clone()
700         {
701             return new NpgsqlBackendTypeMapping(this);
702         }
703
704         /// <summary>
705         /// Determine if a NpgsqlBackendTypeInfo with the given backend type OID exists in this mapping.
706         /// </summary>
707         public Boolean ContainsOID(Int32 OID)
708         {
709             return OIDIndex.ContainsKey(OID);
710         }
711
712         /// <summary>
713         /// Determine if a NpgsqlBackendTypeInfo with the given backend type name exists in this mapping.
714         /// </summary>
715         public Boolean ContainsName(String Name)
716         {
717             return NameIndex.ContainsKey(Name);
718         }
719     }
720
721
722
723     /// <summary>
724     /// Provide mapping between type Type, NpgsqlDbType and a NpgsqlNativeTypeInfo object that represents it.
725     /// </summary>
726     internal class NpgsqlNativeTypeMapping
727     {
728         private Hashtable       NameIndex;
729         private Hashtable       NpgsqlDbTypeIndex;
730         private Hashtable       DbTypeIndex;
731         private Hashtable       TypeIndex;
732
733         /// <summary>
734         /// Construct an empty mapping.
735         /// </summary>
736         public NpgsqlNativeTypeMapping()
737         {
738             NameIndex = new Hashtable();
739             NpgsqlDbTypeIndex = new Hashtable();
740             DbTypeIndex = new Hashtable();
741             TypeIndex = new Hashtable();
742         }
743
744         /// <summary>
745         /// Add the given NpgsqlNativeTypeInfo to this mapping.
746         /// </summary>
747         public void AddType(NpgsqlNativeTypeInfo T)
748         {
749             if (NameIndex.Contains(T.Name)) {
750                 throw new Exception("Type already mapped");
751             }
752
753             NameIndex[T.Name] = T;
754             NpgsqlDbTypeIndex[T.NpgsqlDbType] = T;
755             DbTypeIndex[T.DbType] = T;
756         }
757
758         /// <summary>
759         /// Add a new NpgsqlNativeTypeInfo with the given attributes and conversion handlers to this mapping.
760         /// </summary>
761         /// <param name="OID">Type OID provided by the backend server.</param>
762         /// <param name="Name">Type name provided by the backend server.</param>
763         /// <param name="NpgsqlDbType">NpgsqlDbType</param>
764         /// <param name="ConvertBackendToNative">Data conversion handler.</param>
765         /// <param name="ConvertNativeToBackend">Data conversion handler.</param>
766         public void AddType(String Name, NpgsqlDbType NpgsqlDbType, DbType DbType, Boolean Quote,
767                             ConvertNativeToBackendHandler NativeConvert)
768         {
769             AddType(new NpgsqlNativeTypeInfo(Name, NpgsqlDbType, DbType, Quote, NativeConvert));
770         }
771
772         public void AddNpgsqlDbTypeAlias(String Name, NpgsqlDbType NpgsqlDbType)
773         {
774             if (NpgsqlDbTypeIndex.Contains(NpgsqlDbType)) {
775                 throw new Exception("NpgsqlDbType already aliased");
776             }
777
778             NpgsqlDbTypeIndex[NpgsqlDbType] = NameIndex[Name];
779         }
780         
781         public void AddDbTypeAlias(String Name, DbType DbType)
782         {
783             if (DbTypeIndex.Contains(DbType)) {
784                 throw new Exception("NpgsqlDbType already aliased");
785             }
786
787             DbTypeIndex[DbType] = NameIndex[Name];
788         }
789
790         public void AddTypeAlias(String Name, Type Type)
791         {
792             if (TypeIndex.Contains(Type)) {
793                 throw new Exception("Type already aliased");
794             }
795
796             TypeIndex[Type] = NameIndex[Name];
797         }
798
799         /// <summary>
800         /// Get the number of type infos held.
801         /// </summary>
802         public Int32 Count
803         { get { return NameIndex.Count; } }
804
805         /// <summary>
806         /// Retrieve the NpgsqlNativeTypeInfo with the given backend type name, or null if none found.
807         /// </summary>
808         public NpgsqlNativeTypeInfo this [String Name]
809         {
810             get
811             {
812                 return (NpgsqlNativeTypeInfo)NameIndex[Name];
813             }
814         }
815
816         /// <summary>
817         /// Retrieve the NpgsqlNativeTypeInfo with the given NpgsqlDbType, or null if none found.
818         /// </summary>
819         public NpgsqlNativeTypeInfo this [NpgsqlDbType NpgsqlDbType]
820         {
821             get
822             {
823                 return (NpgsqlNativeTypeInfo)NpgsqlDbTypeIndex[NpgsqlDbType];
824             }
825         }
826         
827         /// <summary>
828         /// Retrieve the NpgsqlNativeTypeInfo with the given DbType, or null if none found.
829         /// </summary>
830         public NpgsqlNativeTypeInfo this [DbType DbType]
831         {
832             get
833             {
834                 return (NpgsqlNativeTypeInfo)DbTypeIndex[DbType];
835             }
836         }
837         
838         
839
840         /// <summary>
841         /// Retrieve the NpgsqlNativeTypeInfo with the given Type, or null if none found.
842         /// </summary>
843         public NpgsqlNativeTypeInfo this [Type Type]
844         {
845             get
846             {
847                 return (NpgsqlNativeTypeInfo)TypeIndex[Type];            
848             }
849         }
850
851         /// <summary>
852         /// Determine if a NpgsqlNativeTypeInfo with the given backend type name exists in this mapping.
853         /// </summary>
854         public Boolean ContainsName(String Name)
855         {
856             return NameIndex.ContainsKey(Name);
857         }
858
859         /// <summary>
860         /// Determine if a NpgsqlNativeTypeInfo with the given NpgsqlDbType exists in this mapping.
861         /// </summary>
862         public Boolean ContainsNpgsqlDbType(NpgsqlDbType NpgsqlDbType)
863         {
864             return NpgsqlDbTypeIndex.ContainsKey(NpgsqlDbType);
865         }
866
867         /// <summary>
868         /// Determine if a NpgsqlNativeTypeInfo with the given Type name exists in this mapping.
869         /// </summary>
870         public Boolean ContainsType(Type Type)
871         {
872             return TypeIndex.ContainsKey(Type);
873         }
874     }
875 }