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.AddType("int4", NpgsqlDbType.Integer, DbType.Int32, false,
187                 null);
188
189                 NativeTypeMapping.AddTypeAlias("int4", typeof(Int32));
190
191                 NativeTypeMapping.AddType("int8", NpgsqlDbType.Bigint, DbType.Int64, false,
192                 null);
193
194                 NativeTypeMapping.AddTypeAlias("int8", typeof(Int64));
195
196                 NativeTypeMapping.AddType("float4", NpgsqlDbType.Real, DbType.Single, false,
197                 null);
198
199                 NativeTypeMapping.AddTypeAlias("float4", typeof(Single));
200
201                 NativeTypeMapping.AddType("float8", NpgsqlDbType.Double, DbType.Double, false,
202                 null);
203
204                 NativeTypeMapping.AddTypeAlias("float8", typeof(Double));
205
206                 NativeTypeMapping.AddType("numeric", NpgsqlDbType.Numeric, DbType.Decimal, false,
207                 null);
208
209                 NativeTypeMapping.AddTypeAlias("numeric", typeof(Decimal));
210
211                 NativeTypeMapping.AddType("currency", NpgsqlDbType.Money, DbType.Currency, true,
212                 new ConvertNativeToBackendHandler(BasicNativeToBackendTypeConverter.ToMoney));
213
214                 NativeTypeMapping.AddType("date", NpgsqlDbType.Date, DbType.Date, true,
215                 new ConvertNativeToBackendHandler(BasicNativeToBackendTypeConverter.ToDate));
216
217                 NativeTypeMapping.AddType("time", NpgsqlDbType.Time, DbType.Time, true,
218                 new ConvertNativeToBackendHandler(BasicNativeToBackendTypeConverter.ToTime));
219
220                 NativeTypeMapping.AddType("timestamp", NpgsqlDbType.Timestamp, DbType.DateTime, true,
221                 new ConvertNativeToBackendHandler(BasicNativeToBackendTypeConverter.ToDateTime));
222
223                 NativeTypeMapping.AddTypeAlias("timestamp", typeof(DateTime));
224
225                 NativeTypeMapping.AddType("point", NpgsqlDbType.Point, DbType.Object, true,
226                 new ConvertNativeToBackendHandler(ExtendedNativeToBackendTypeConverter.ToPoint));
227
228                 NativeTypeMapping.AddTypeAlias("point", typeof(NpgsqlPoint));
229                 
230                 NativeTypeMapping.AddType("box", NpgsqlDbType.Box, DbType.Object, true,
231                 new ConvertNativeToBackendHandler(ExtendedNativeToBackendTypeConverter.ToBox));
232
233                 NativeTypeMapping.AddTypeAlias("box", typeof(NpgsqlBox));
234                 
235                 NativeTypeMapping.AddType("lseg", NpgsqlDbType.LSeg, DbType.Object, true,
236                 new ConvertNativeToBackendHandler(ExtendedNativeToBackendTypeConverter.ToLSeg));
237
238                 NativeTypeMapping.AddTypeAlias("lseg", typeof(NpgsqlLSeg));
239
240                 NativeTypeMapping.AddType("path", NpgsqlDbType.Path, DbType.Object, true,
241                 new ConvertNativeToBackendHandler(ExtendedNativeToBackendTypeConverter.ToPath));
242
243                 NativeTypeMapping.AddTypeAlias("path", typeof(NpgsqlPath));
244
245                 NativeTypeMapping.AddType("polygon", NpgsqlDbType.Polygon, DbType.Object, true,
246                 new ConvertNativeToBackendHandler(ExtendedNativeToBackendTypeConverter.ToPolygon));
247
248                 NativeTypeMapping.AddTypeAlias("polygon", typeof(NpgsqlPolygon));
249
250                 NativeTypeMapping.AddType("circle", NpgsqlDbType.Circle, DbType.Object, true,
251                 new ConvertNativeToBackendHandler(ExtendedNativeToBackendTypeConverter.ToCircle));
252
253                 NativeTypeMapping.AddTypeAlias("circle", typeof(NpgsqlCircle));
254             }
255         }
256
257         ///<summary>
258         /// This method creates (or retrieves from cache) a mapping between type and OID 
259         /// of all natively supported postgresql data types.
260         /// This is needed as from one version to another, this mapping can be changed and
261         /// so we avoid hardcoding them.
262         /// </summary>
263         /// <returns>NpgsqlTypeMapping containing all known data types.  The mapping must be
264         /// cloned before it is modified because it is cached; changes made by one connection may
265         /// effect another connection.</returns>
266         public static NpgsqlBackendTypeMapping CreateAndLoadInitialTypesMapping(NpgsqlConnector conn)
267         {
268             NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "LoadTypesMapping");
269
270             // [TODO] Verify another way to get higher concurrency.
271             lock(CLASSNAME)
272             {
273                 // Check the cache for an initial types map.
274                 NpgsqlBackendTypeMapping oidToNameMapping = (NpgsqlBackendTypeMapping) BackendTypeMappingCache[conn.ServerVersion];
275
276                 if (oidToNameMapping != null)
277                 {
278                     return oidToNameMapping;
279                 }
280
281                 // Not in cache, create a new one.
282                 oidToNameMapping = new NpgsqlBackendTypeMapping();
283
284                 // Create a list of all natively supported postgresql data types.
285                 NpgsqlBackendTypeInfo[] TypeInfoList = new NpgsqlBackendTypeInfo[]
286                 {
287                     new NpgsqlBackendTypeInfo(0, "unknown", NpgsqlDbType.Text, DbType.String, typeof(String),
288                         null),
289
290                     new NpgsqlBackendTypeInfo(0, "char", NpgsqlDbType.Text, DbType.String, typeof(String),
291                         null),
292
293                     new NpgsqlBackendTypeInfo(0, "bpchar", NpgsqlDbType.Text, DbType.String, typeof(String),
294                         null),
295
296                     new NpgsqlBackendTypeInfo(0, "varchar", NpgsqlDbType.Text, DbType.String, typeof(String),
297                         null),
298
299                     new NpgsqlBackendTypeInfo(0, "text", NpgsqlDbType.Text, DbType.String, typeof(String),
300                         null),
301                         
302                     new NpgsqlBackendTypeInfo(0, "name", NpgsqlDbType.Text, DbType.String, typeof(String),
303                         null),
304
305                     new NpgsqlBackendTypeInfo(0, "bytea", NpgsqlDbType.Bytea, DbType.Binary, typeof(Byte[]),
306                         new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter.ToBinary)),
307
308
309                     new NpgsqlBackendTypeInfo(0, "bool", NpgsqlDbType.Boolean, DbType.Boolean, typeof(Boolean),
310                         new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter.ToBoolean)),
311
312
313                     new NpgsqlBackendTypeInfo(0, "int2", NpgsqlDbType.Smallint, DbType.Int16, typeof(Int16),
314                         null),
315
316                     new NpgsqlBackendTypeInfo(0, "int4", NpgsqlDbType.Integer, DbType.Int32, typeof(Int32),
317                         null),
318
319                     new NpgsqlBackendTypeInfo(0, "int8", NpgsqlDbType.Bigint, DbType.Int64, typeof(Int64),
320                         null),
321
322                     new NpgsqlBackendTypeInfo(0, "oid", NpgsqlDbType.Bigint, DbType.Int64, typeof(Int64),
323                         null),
324
325
326                     new NpgsqlBackendTypeInfo(0, "float4", NpgsqlDbType.Real, DbType.Single, typeof(Single),
327                         null),
328
329                     new NpgsqlBackendTypeInfo(0, "float8", NpgsqlDbType.Double, DbType.Double, typeof(Double),
330                         null),
331
332                     new NpgsqlBackendTypeInfo(0, "numeric", NpgsqlDbType.Numeric, DbType.Decimal, typeof(Decimal),
333                         null),
334
335                     new NpgsqlBackendTypeInfo(0, "money", NpgsqlDbType.Money, DbType.Decimal, typeof(Decimal),
336                         new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter.ToMoney)),
337
338
339                     new NpgsqlBackendTypeInfo(0, "date", NpgsqlDbType.Date, DbType.Date, typeof(DateTime),
340                         new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter.ToDate)),
341
342                     new NpgsqlBackendTypeInfo(0, "time", NpgsqlDbType.Time, DbType.Time, typeof(DateTime),
343                         new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter.ToTime)),
344
345                     new NpgsqlBackendTypeInfo(0, "timetz", NpgsqlDbType.Time, DbType.Time, typeof(DateTime),
346                         new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter.ToTime)),
347
348                     new NpgsqlBackendTypeInfo(0, "timestamp", NpgsqlDbType.Timestamp, DbType.DateTime, typeof(DateTime),
349                         new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter.ToDateTime)),
350
351                     new NpgsqlBackendTypeInfo(0, "timestamptz", NpgsqlDbType.Timestamp, DbType.DateTime, typeof(DateTime),
352                         new ConvertBackendToNativeHandler(BasicBackendToNativeTypeConverter.ToDateTime)),
353
354
355                     new NpgsqlBackendTypeInfo(0, "point", NpgsqlDbType.Point, DbType.Object, typeof(NpgsqlPoint),
356                         new ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter.ToPoint)),
357
358                     new NpgsqlBackendTypeInfo(0, "lseg", NpgsqlDbType.LSeg, DbType.Object, typeof(NpgsqlLSeg),
359                         new ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter.ToLSeg)),
360
361                     new NpgsqlBackendTypeInfo(0, "path", NpgsqlDbType.Path, DbType.Object, typeof(NpgsqlPath),
362                         new ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter.ToPath)),
363
364                     new NpgsqlBackendTypeInfo(0, "box", NpgsqlDbType.Box, DbType.Object, typeof(NpgsqlBox),
365                         new ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter.ToBox)),
366
367                     new NpgsqlBackendTypeInfo(0, "circle", NpgsqlDbType.Circle, DbType.Object, typeof(NpgsqlCircle),
368                         new ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter.ToCircle)),
369
370                     new NpgsqlBackendTypeInfo(0, "polygon", NpgsqlDbType.Polygon, DbType.Object, typeof(NpgsqlPolygon),
371                         new ConvertBackendToNativeHandler(ExtendedBackendToNativeTypeConverter.ToPolygon)),
372                 };
373
374                 // Attempt to map each type info in the list to an OID on the backend and
375                 // add each mapped type to the new type mapping object.
376                 LoadTypesMappings(conn, oidToNameMapping, TypeInfoList);
377
378                 // Add this mapping to the per-server-version cache so we don't have to
379                 // do these expensive queries on every connection startup.
380                 BackendTypeMappingCache.Add(conn.ServerVersion, oidToNameMapping);
381
382                 return oidToNameMapping;
383             }
384
385
386         }
387
388         /// <summary>
389         /// Attempt to map types by issuing a query against pg_type.
390         /// This function takes a list of NpgsqlTypeInfo and attempts to resolve the OID field
391         /// of each by querying pg_type.  If the mapping is found, the type info object is
392         /// updated (OID) and added to the provided NpgsqlTypeMapping object.
393         /// </summary>
394         /// <param name="conn">NpgsqlConnector to send query through.</param>
395         /// <param name="TypeMappings">Mapping object to add types too.</param>
396         /// <param name="TypeInfoList">List of types that need to have OID's mapped.</param>
397         public static void LoadTypesMappings(NpgsqlConnector conn, NpgsqlBackendTypeMapping TypeMappings, IList TypeInfoList)
398         {
399             StringBuilder       InList = new StringBuilder();
400             Hashtable           NameIndex = new Hashtable();
401
402             // Build a clause for the SELECT statement.
403             // Build a name->typeinfo mapping so we can match the results of the query
404             /// with the list of type objects efficiently.
405             foreach (NpgsqlBackendTypeInfo TypeInfo in TypeInfoList) {
406                 NameIndex.Add(TypeInfo.Name, TypeInfo);
407                 InList.AppendFormat("{0}'{1}'", ((InList.Length > 0) ? ", " : ""), TypeInfo.Name);
408             }
409
410             if (InList.Length == 0) {
411                 return;
412             }
413
414             NpgsqlCommand       command = new NpgsqlCommand("SELECT oid, typname FROM pg_type WHERE typname IN (" + InList.ToString() + ")", conn);
415             NpgsqlDataReader    dr = command.ExecuteReader();
416
417             while (dr.Read()) {
418                 NpgsqlBackendTypeInfo TypeInfo = (NpgsqlBackendTypeInfo)NameIndex[dr[1].ToString()];
419
420                 TypeInfo._OID = Convert.ToInt32(dr[0]);
421
422                 TypeMappings.AddType(TypeInfo);
423             }
424         }
425     }
426
427     /// <summary>
428     /// Delegate called to convert the given backend data to its native representation.
429     /// </summary>
430     internal delegate Object ConvertBackendToNativeHandler(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier);
431     /// <summary>
432     /// Delegate called to convert the given native data to its backand representation.
433     /// </summary>
434     internal delegate String ConvertNativeToBackendHandler(NpgsqlNativeTypeInfo TypeInfo, Object NativeData);
435
436     /// <summary>
437     /// Represents a backend data type.
438     /// This class can be called upon to convert a backend field representation to a native object.
439     /// </summary>
440     internal class NpgsqlBackendTypeInfo
441     {
442         private event ConvertBackendToNativeHandler _ConvertBackendToNative;
443
444         internal Int32           _OID;
445         private String           _Name;
446         private NpgsqlDbType     _NpgsqlDbType;
447         private DbType           _DbType;
448         private Type             _Type;
449
450         /// <summary>
451         /// Construct a new NpgsqlTypeInfo with the given attributes and conversion handlers.
452         /// </summary>
453         /// <param name="OID">Type OID provided by the backend server.</param>
454         /// <param name="Name">Type name provided by the backend server.</param>
455         /// <param name="NpgsqlDbType">NpgsqlDbType</param>
456         /// <param name="Type">System type to convert fields of this type to.</param>
457         /// <param name="ConvertBackendToNative">Data conversion handler.</param>
458         public NpgsqlBackendTypeInfo(Int32 OID, String Name, NpgsqlDbType NpgsqlDbType, DbType DbType, Type Type,
459                               ConvertBackendToNativeHandler ConvertBackendToNative)
460         {
461             _OID = OID;
462             _Name = Name;
463             _NpgsqlDbType = NpgsqlDbType;
464             _DbType = DbType;
465             _Type = Type;
466             _ConvertBackendToNative = ConvertBackendToNative;
467         }
468
469         /// <summary>
470         /// Type OID provided by the backend server.
471         /// </summary>
472         public Int32 OID
473         {
474           get { return _OID; }
475         }
476
477         /// <summary>
478         /// Type name provided by the backend server.
479         /// </summary>
480         public String Name
481         { get { return _Name; } }
482
483         /// <summary>
484         /// NpgsqlDbType.
485         /// </summary>
486         public NpgsqlDbType NpgsqlDbType
487         { get { return _NpgsqlDbType; } }
488
489         /// <summary>
490         /// NpgsqlDbType.
491         /// </summary>
492         public DbType DbType
493         { get { return _DbType; } }
494         
495         /// <summary>
496         /// System type to convert fields of this type to.
497         /// </summary>
498         public Type Type
499         { get { return _Type; } }
500
501         /// <summary>
502         /// Perform a data conversion from a backend representation to 
503         /// a native object.
504         /// </summary>
505         /// <param name="BackendData">Data sent from the backend.</param>
506         /// <param name="TypeModifier">Type modifier field sent from the backend.</param>
507         public Object ConvertToNative(String BackendData, Int16 TypeSize, Int32 TypeModifier)
508         {
509             if (_ConvertBackendToNative != null) {
510                 return _ConvertBackendToNative(this, BackendData, TypeSize, TypeModifier);
511             } else {
512                 try {
513                     return Convert.ChangeType(BackendData, Type, CultureInfo.InvariantCulture);
514                 } catch {
515                     return BackendData;
516                 }
517             }
518         }
519     }
520
521     /// <summary>
522     /// Represents a backend data type.
523     /// This class can be called upon to convert a native object to its backend field representation,
524     /// </summary>
525     internal class NpgsqlNativeTypeInfo
526     {
527         private event ConvertNativeToBackendHandler _ConvertNativeToBackend;
528
529         private String           _Name;
530         private NpgsqlDbType     _NpgsqlDbType;
531         private DbType           _DbType;
532         private Boolean          _Quote;
533
534         /// <summary>
535         /// Construct a new NpgsqlTypeInfo with the given attributes and conversion handlers.
536         /// </summary>
537         /// <param name="OID">Type OID provided by the backend server.</param>
538         /// <param name="Name">Type name provided by the backend server.</param>
539         /// <param name="NpgsqlDbType">NpgsqlDbType</param>
540         /// <param name="Type">System type to convert fields of this type to.</param>
541         /// <param name="ConvertBackendToNative">Data conversion handler.</param>
542         /// <param name="ConvertNativeToBackend">Data conversion handler.</param>
543         public NpgsqlNativeTypeInfo(String Name, NpgsqlDbType NpgsqlDbType, DbType DbType, Boolean Quote,
544                               ConvertNativeToBackendHandler ConvertNativeToBackend)
545         {
546             _Name = Name;
547             _NpgsqlDbType = NpgsqlDbType;
548             _DbType = DbType;
549             _Quote = Quote;
550             _ConvertNativeToBackend = ConvertNativeToBackend;
551         }
552
553         /// <summary>
554         /// Type name provided by the backend server.
555         /// </summary>
556         public String Name
557         { get { return _Name; } }
558
559         /// <summary>
560         /// NpgsqlDbType.
561         /// </summary>
562         public NpgsqlDbType NpgsqlDbType
563         { get { return _NpgsqlDbType; } }
564
565         /// <summary>
566         /// DbType.
567         /// </summary>
568         public DbType DbType
569         { get { return _DbType; } }
570         
571         
572         /// <summary>
573         /// Apply quoting.
574         /// </summary>
575         public Boolean Quote
576         { get { return _Quote; } }
577
578         /// <summary>
579         /// Perform a data conversion from a native object to
580         /// a backend representation.
581         /// DBNull and null values are handled differently depending if a plain query is used
582         /// When 
583         /// </summary>
584         /// <param name="NativeData">Native .NET object to be converted.</param>
585         /// <param name="ForExtendedQuery">Flag indicating if the conversion has to be done for 
586                 /// plain queries or extended queries</param>
587         public String ConvertToBackend(Object NativeData, Boolean ForExtendedQuery)
588         {
589             if ((NativeData == DBNull.Value) || (NativeData == null))
590                 if (ForExtendedQuery)
591                         return null;    // Extended query expects null values be represented as null.
592                 else
593                         return "NULL";  // Plain queries exptects null values as string NULL. 
594                             
595             else if (_ConvertNativeToBackend != null) {
596                 return QuoteString(! ForExtendedQuery, _ConvertNativeToBackend(this, NativeData));
597                 
598             } else {
599                 return QuoteString(! ForExtendedQuery, (String)Convert.ChangeType(NativeData, typeof(String), CultureInfo.InvariantCulture));
600             }
601         }
602
603         private static String QuoteString(Boolean Quote, String S)
604         {
605             if (Quote) {
606                 return String.Format("'{0}'", S);
607             } else {
608                 return S;
609             }
610         }
611     }
612
613     /// <summary>
614     /// Provide mapping between type OID, type name, and a NpgsqlBackendTypeInfo object that represents it.
615     /// </summary>
616     internal class NpgsqlBackendTypeMapping
617     {
618         private Hashtable       OIDIndex;
619         private Hashtable       NameIndex;
620
621         /// <summary>
622         /// Construct an empty mapping.
623         /// </summary>
624         public NpgsqlBackendTypeMapping()
625         {
626             OIDIndex = new Hashtable();
627             NameIndex = new Hashtable();
628         }
629
630         /// <summary>
631         /// Copy constuctor.
632         /// </summary>
633         private NpgsqlBackendTypeMapping(NpgsqlBackendTypeMapping Other)
634         {
635             OIDIndex = (Hashtable)Other.OIDIndex.Clone();
636             NameIndex = (Hashtable)Other.NameIndex.Clone();
637         }
638
639         /// <summary>
640         /// Add the given NpgsqlBackendTypeInfo to this mapping.
641         /// </summary>
642         public void AddType(NpgsqlBackendTypeInfo T)
643         {
644             if (OIDIndex.Contains(T.OID)) {
645                 throw new Exception("Type already mapped");
646             }
647
648             OIDIndex[T.OID] = T;
649             NameIndex[T.Name] = T;
650         }
651
652         /// <summary>
653         /// Add a new NpgsqlBackendTypeInfo with the given attributes and conversion handlers to this mapping.
654         /// </summary>
655         /// <param name="OID">Type OID provided by the backend server.</param>
656         /// <param name="Name">Type name provided by the backend server.</param>
657         /// <param name="NpgsqlDbType">NpgsqlDbType</param>
658         /// <param name="Type">System type to convert fields of this type to.</param>
659         /// <param name="ConvertBackendToNative">Data conversion handler.</param>
660         public void AddType(Int32 OID, String Name, NpgsqlDbType NpgsqlDbType, DbType DbType, Type Type,
661                             ConvertBackendToNativeHandler BackendConvert)
662         {
663             AddType(new NpgsqlBackendTypeInfo(OID, Name, NpgsqlDbType, DbType, Type, BackendConvert));
664         }
665
666         /// <summary>
667         /// Get the number of type infos held.
668         /// </summary>
669         public Int32 Count
670         { get { return NameIndex.Count; } }
671
672         /// <summary>
673         /// Retrieve the NpgsqlBackendTypeInfo with the given backend type OID, or null if none found.
674         /// </summary>
675         public NpgsqlBackendTypeInfo this [Int32 OID]
676         {
677             get
678             {
679                 return (NpgsqlBackendTypeInfo)OIDIndex[OID];
680             }
681         }
682
683         /// <summary>
684         /// Retrieve the NpgsqlBackendTypeInfo with the given backend type name, or null if none found.
685         /// </summary>
686         public NpgsqlBackendTypeInfo this [String Name]
687         {
688             get
689             {
690                 return (NpgsqlBackendTypeInfo)NameIndex[Name];
691             }
692         }
693
694         /// <summary>
695         /// Make a shallow copy of this type mapping.
696         /// </summary>
697         public NpgsqlBackendTypeMapping Clone()
698         {
699             return new NpgsqlBackendTypeMapping(this);
700         }
701
702         /// <summary>
703         /// Determine if a NpgsqlBackendTypeInfo with the given backend type OID exists in this mapping.
704         /// </summary>
705         public Boolean ContainsOID(Int32 OID)
706         {
707             return OIDIndex.ContainsKey(OID);
708         }
709
710         /// <summary>
711         /// Determine if a NpgsqlBackendTypeInfo with the given backend type name exists in this mapping.
712         /// </summary>
713         public Boolean ContainsName(String Name)
714         {
715             return NameIndex.ContainsKey(Name);
716         }
717     }
718
719
720
721     /// <summary>
722     /// Provide mapping between type Type, NpgsqlDbType and a NpgsqlNativeTypeInfo object that represents it.
723     /// </summary>
724     internal class NpgsqlNativeTypeMapping
725     {
726         private Hashtable       NameIndex;
727         private Hashtable       NpgsqlDbTypeIndex;
728         private Hashtable       DbTypeIndex;
729         private Hashtable       TypeIndex;
730
731         /// <summary>
732         /// Construct an empty mapping.
733         /// </summary>
734         public NpgsqlNativeTypeMapping()
735         {
736             NameIndex = new Hashtable();
737             NpgsqlDbTypeIndex = new Hashtable();
738             DbTypeIndex = new Hashtable();
739             TypeIndex = new Hashtable();
740         }
741
742         /// <summary>
743         /// Add the given NpgsqlNativeTypeInfo to this mapping.
744         /// </summary>
745         public void AddType(NpgsqlNativeTypeInfo T)
746         {
747             if (NameIndex.Contains(T.Name)) {
748                 throw new Exception("Type already mapped");
749             }
750
751             NameIndex[T.Name] = T;
752             NpgsqlDbTypeIndex[T.NpgsqlDbType] = T;
753             DbTypeIndex[T.DbType] = T;
754         }
755
756         /// <summary>
757         /// Add a new NpgsqlNativeTypeInfo with the given attributes and conversion handlers to this mapping.
758         /// </summary>
759         /// <param name="OID">Type OID provided by the backend server.</param>
760         /// <param name="Name">Type name provided by the backend server.</param>
761         /// <param name="NpgsqlDbType">NpgsqlDbType</param>
762         /// <param name="ConvertBackendToNative">Data conversion handler.</param>
763         /// <param name="ConvertNativeToBackend">Data conversion handler.</param>
764         public void AddType(String Name, NpgsqlDbType NpgsqlDbType, DbType DbType, Boolean Quote,
765                             ConvertNativeToBackendHandler NativeConvert)
766         {
767             AddType(new NpgsqlNativeTypeInfo(Name, NpgsqlDbType, DbType, Quote, NativeConvert));
768         }
769
770         public void AddNpgsqlDbTypeAlias(String Name, NpgsqlDbType NpgsqlDbType)
771         {
772             if (NpgsqlDbTypeIndex.Contains(NpgsqlDbType)) {
773                 throw new Exception("NpgsqlDbType already aliased");
774             }
775
776             NpgsqlDbTypeIndex[NpgsqlDbType] = NameIndex[Name];
777         }
778         
779         public void AddDbTypeAlias(String Name, DbType DbType)
780         {
781             if (DbTypeIndex.Contains(DbType)) {
782                 throw new Exception("NpgsqlDbType already aliased");
783             }
784
785             DbTypeIndex[DbType] = NameIndex[Name];
786         }
787
788         public void AddTypeAlias(String Name, Type Type)
789         {
790             if (TypeIndex.Contains(Type)) {
791                 throw new Exception("Type already aliased");
792             }
793
794             TypeIndex[Type] = NameIndex[Name];
795         }
796
797         /// <summary>
798         /// Get the number of type infos held.
799         /// </summary>
800         public Int32 Count
801         { get { return NameIndex.Count; } }
802
803         /// <summary>
804         /// Retrieve the NpgsqlNativeTypeInfo with the given backend type name, or null if none found.
805         /// </summary>
806         public NpgsqlNativeTypeInfo this [String Name]
807         {
808             get
809             {
810                 return (NpgsqlNativeTypeInfo)NameIndex[Name];
811             }
812         }
813
814         /// <summary>
815         /// Retrieve the NpgsqlNativeTypeInfo with the given NpgsqlDbType, or null if none found.
816         /// </summary>
817         public NpgsqlNativeTypeInfo this [NpgsqlDbType NpgsqlDbType]
818         {
819             get
820             {
821                 return (NpgsqlNativeTypeInfo)NpgsqlDbTypeIndex[NpgsqlDbType];
822             }
823         }
824         
825         /// <summary>
826         /// Retrieve the NpgsqlNativeTypeInfo with the given DbType, or null if none found.
827         /// </summary>
828         public NpgsqlNativeTypeInfo this [DbType DbType]
829         {
830             get
831             {
832                 return (NpgsqlNativeTypeInfo)DbTypeIndex[DbType];
833             }
834         }
835         
836         
837
838         /// <summary>
839         /// Retrieve the NpgsqlNativeTypeInfo with the given Type, or null if none found.
840         /// </summary>
841         public NpgsqlNativeTypeInfo this [Type Type]
842         {
843             get
844             {
845                 return (NpgsqlNativeTypeInfo)TypeIndex[Type];            
846             }
847         }
848
849         /// <summary>
850         /// Determine if a NpgsqlNativeTypeInfo with the given backend type name exists in this mapping.
851         /// </summary>
852         public Boolean ContainsName(String Name)
853         {
854             return NameIndex.ContainsKey(Name);
855         }
856
857         /// <summary>
858         /// Determine if a NpgsqlNativeTypeInfo with the given NpgsqlDbType exists in this mapping.
859         /// </summary>
860         public Boolean ContainsNpgsqlDbType(NpgsqlDbType NpgsqlDbType)
861         {
862             return NpgsqlDbTypeIndex.ContainsKey(NpgsqlDbType);
863         }
864
865         /// <summary>
866         /// Determine if a NpgsqlNativeTypeInfo with the given Type name exists in this mapping.
867         /// </summary>
868         public Boolean ContainsType(Type Type)
869         {
870             return TypeIndex.ContainsKey(Type);
871         }
872     }
873 }