imported everything from my branch (which is slightly harmless).
[mono.git] / mcs / class / FirebirdSql.Data.Firebird / FirebirdSql.Data.Embedded / XsqldaMarshaler.cs
1 /*\r
2  *      Firebird ADO.NET Data provider for .NET and     Mono \r
3  * \r
4  *         The contents of this file are subject to the Initial \r
5  *         Developer's Public License Version 1.0 (the "License"); \r
6  *         you may not use this file except in compliance with the \r
7  *         License. You may obtain a copy of the License at \r
8  *         http://www.firebirdsql.org/index.php?op=doc&id=idpl\r
9  *\r
10  *         Software distributed under the License is distributed on \r
11  *         an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either \r
12  *         express or implied. See the License for the specific \r
13  *         language governing rights and limitations under the License.\r
14  * \r
15  *      Copyright (c) 2002, 2005 Carlos Guzman Alvarez\r
16  *      All Rights Reserved.\r
17  */\r
18 \r
19 using System;\r
20 using System.Runtime.InteropServices;\r
21 using System.Text;\r
22 \r
23 using FirebirdSql.Data.Common;\r
24 \r
25 namespace FirebirdSql.Data.Embedded\r
26 {\r
27         internal sealed class XsqldaMarshaler\r
28         {\r
29                 #region Static Fields\r
30 \r
31                 private static XsqldaMarshaler instance;\r
32 \r
33                 #endregion\r
34 \r
35                 #region Constructors\r
36 \r
37                 private XsqldaMarshaler()\r
38                 {\r
39                 }\r
40 \r
41                 #endregion\r
42 \r
43                 #region Methods\r
44 \r
45                 public static XsqldaMarshaler GetInstance()\r
46                 {\r
47                         if (XsqldaMarshaler.instance == null)\r
48                         {\r
49                                 XsqldaMarshaler.instance = new XsqldaMarshaler();\r
50                         }\r
51 \r
52                         return XsqldaMarshaler.instance;\r
53                 }\r
54 \r
55                 public void     CleanUpNativeData(ref IntPtr pNativeData)\r
56                 {\r
57                         if (pNativeData != IntPtr.Zero)\r
58                         {\r
59                                 // Obtain XSQLDA information\r
60                                 XSQLDA xsqlda = new     XSQLDA();\r
61                         \r
62                                 xsqlda = (XSQLDA)Marshal.PtrToStructure(pNativeData, typeof(XSQLDA));\r
63 \r
64                                 // Destroy XSQLDA structure\r
65                                 Marshal.DestroyStructure(pNativeData, typeof(XSQLDA));\r
66 \r
67                                 // Destroy XSQLVAR structures\r
68                                 for     (int i = 0;     i <     xsqlda.sqln; i++)\r
69                                 {\r
70                                         // Free sqldata and     sqlind pointers if needed\r
71                                         XSQLVAR sqlvar = (XSQLVAR)Marshal.PtrToStructure(\r
72                                                 this.GetIntPtr(pNativeData,     this.ComputeLength(i)), typeof(XSQLVAR));\r
73 \r
74                                         if (sqlvar.sqldata != IntPtr.Zero)\r
75                                         {\r
76                                                 Marshal.FreeHGlobal(sqlvar.sqldata);\r
77                                                 sqlvar.sqldata = IntPtr.Zero;\r
78                                         }\r
79                                         if (sqlvar.sqlind != IntPtr.Zero)\r
80                                         {\r
81                                                 Marshal.FreeHGlobal(sqlvar.sqlind);\r
82                                                 sqlvar.sqlind = IntPtr.Zero;\r
83                                         }\r
84 \r
85                                         Marshal.DestroyStructure(\r
86                                                 this.GetIntPtr(pNativeData,     this.ComputeLength(i)), typeof(XSQLVAR));\r
87                                 }\r
88 \r
89                                 // Free pointer memory\r
90                                 Marshal.FreeHGlobal(pNativeData);\r
91 \r
92                                 pNativeData = IntPtr.Zero;\r
93                         }\r
94                 }\r
95 \r
96                 public IntPtr MarshalManagedToNative(Charset charset, Descriptor descriptor)\r
97                 {\r
98                         // Set up XSQLDA structure\r
99                         XSQLDA xsqlda = new XSQLDA();\r
100 \r
101                         xsqlda.version = descriptor.Version;\r
102                         xsqlda.sqln      = descriptor.Count;\r
103                         xsqlda.sqld      = descriptor.ActualCount;\r
104                         \r
105                         XSQLVAR[] xsqlvar = new XSQLVAR[descriptor.Count];\r
106 \r
107                         for     (int i = 0;     i <     xsqlvar.Length; i++)\r
108                         {\r
109                                 // Create a     new     XSQLVAR structure and fill it\r
110                                 xsqlvar[i] = new XSQLVAR();\r
111 \r
112                                 xsqlvar[i].sqltype       = descriptor[i].DataType;\r
113                                 xsqlvar[i].sqlscale      = descriptor[i].NumericScale;\r
114                                 xsqlvar[i].sqlsubtype = descriptor[i].SubType;\r
115                                 xsqlvar[i].sqllen        = descriptor[i].Length;\r
116 \r
117                                 // Create a     new     pointer for     the     xsqlvar data\r
118                                 byte[] buffer = this.GetBytes(descriptor[i]);\r
119                                 if (buffer.Length > 0)\r
120                                 {\r
121                                         xsqlvar[i].sqldata = Marshal.AllocHGlobal(buffer.Length);\r
122                                         Marshal.Copy(buffer, 0, xsqlvar[i].sqldata,     buffer.Length);\r
123                                 }\r
124 \r
125                                 // Create a     new     pointer for     the     sqlind value\r
126                                 xsqlvar[i].sqlind = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Int16)));\r
127                                 Marshal.WriteInt16(xsqlvar[i].sqlind, descriptor[i].NullFlag);                            \r
128 \r
129                                 // Name\r
130                                 xsqlvar[i].sqlname               = this.GetStringBuffer(charset,        descriptor[i].Name);\r
131                                 xsqlvar[i].sqlname_length = (short)xsqlvar[i].sqlname.Length;\r
132 \r
133                                 // Relation     Name\r
134                                 xsqlvar[i].relname               = this.GetStringBuffer(charset,        descriptor[i].Relation);\r
135                                 xsqlvar[i].relname_length = (short)xsqlvar[i].relname.Length;\r
136 \r
137                                 // Owner name\r
138                                 xsqlvar[i].ownername     = this.GetStringBuffer(charset,        descriptor[i].Owner);\r
139                                 xsqlvar[i].ownername_length = (short)xsqlvar[i].ownername.Length;\r
140 \r
141                                 // Alias name\r
142                                 xsqlvar[i].aliasname     = this.GetStringBuffer(charset,        descriptor[i].Alias);\r
143                                 xsqlvar[i].aliasname_length = (short)xsqlvar[i].aliasname.Length;\r
144                         }\r
145 \r
146                         return this.MarshalManagedToNative(xsqlda, xsqlvar);\r
147                 }\r
148 \r
149                 public IntPtr MarshalManagedToNative(XSQLDA     xsqlda, XSQLVAR[] xsqlvar)\r
150                 {\r
151                         int             size = this.ComputeLength(xsqlda.sqln);\r
152                         IntPtr  ptr      = Marshal.AllocHGlobal(size);\r
153 \r
154                         Marshal.StructureToPtr(xsqlda, ptr,     true);\r
155 \r
156                         for     (int i = 0;     i <     xsqlvar.Length; i++)\r
157                         {\r
158                                 int     offset = this.ComputeLength(i);\r
159                                 Marshal.StructureToPtr(xsqlvar[i], this.GetIntPtr(ptr, offset), true);\r
160                         }\r
161 \r
162                         return ptr;\r
163                 }\r
164 \r
165                 public Descriptor MarshalNativeToManaged(Charset charset, IntPtr pNativeData)\r
166                 {\r
167                         // Obtain XSQLDA information\r
168                         XSQLDA xsqlda = new     XSQLDA();\r
169                         \r
170                         xsqlda = (XSQLDA)Marshal.PtrToStructure(pNativeData, typeof(XSQLDA));\r
171 \r
172                         // Create a     new     Descriptor\r
173                         Descriptor descriptor = new Descriptor(xsqlda.sqln);\r
174                         descriptor.ActualCount = xsqlda.sqld;\r
175                         \r
176                         // Obtain XSQLVAR members information\r
177                         XSQLVAR[] xsqlvar = new XSQLVAR[xsqlda.sqln];\r
178                         \r
179                         for     (int i = 0;     i <     xsqlvar.Length; i++)\r
180                         {\r
181                                 xsqlvar[i] = (XSQLVAR)Marshal.PtrToStructure(\r
182                                         this.GetIntPtr(pNativeData,     this.ComputeLength(i)), typeof(XSQLVAR));\r
183 \r
184                                 // Map XSQLVAR information to Descriptor\r
185                                 descriptor[i].DataType   = xsqlvar[i].sqltype;\r
186                                 descriptor[i].NumericScale = xsqlvar[i].sqlscale;\r
187                                 descriptor[i].SubType    = xsqlvar[i].sqlsubtype;\r
188                                 descriptor[i].Length     = xsqlvar[i].sqllen;\r
189 \r
190                                 // Decode sqlind value\r
191                                 if (xsqlvar[i].sqlind == IntPtr.Zero)\r
192                                 {\r
193                                         descriptor[i].NullFlag = 0;\r
194                                 }\r
195                                 else\r
196                                 {\r
197                                         descriptor[i].NullFlag = Marshal.ReadInt16(xsqlvar[i].sqlind);\r
198                                 }\r
199                                 \r
200                                 // Set value\r
201                                 if (descriptor[i].NullFlag != -1)\r
202                                 {\r
203                                         descriptor[i].SetValue(this.GetBytes(xsqlvar[i]));\r
204                                 }\r
205                                 \r
206                                 descriptor[i].Name       = this.GetString(charset, xsqlvar[i].sqlname);\r
207                                 descriptor[i].Relation = this.GetString(charset, xsqlvar[i].relname);\r
208                                 descriptor[i].Owner      = this.GetString(charset, xsqlvar[i].ownername);\r
209                                 descriptor[i].Alias      = this.GetString(charset, xsqlvar[i].aliasname);\r
210                         }\r
211 \r
212                         return descriptor;\r
213                 }\r
214 \r
215                 #endregion\r
216 \r
217                 #region Private methods\r
218 \r
219                 private IntPtr GetIntPtr(IntPtr ptr, int offset)\r
220                 {\r
221                         return (IntPtr)(ptr.ToInt32() + offset);\r
222                 }\r
223 \r
224                 private int     ComputeLength(int n)\r
225                 {\r
226                         return (Marshal.SizeOf(typeof(XSQLDA)) + n * Marshal.SizeOf(typeof(XSQLVAR)));\r
227                 }\r
228 \r
229                 private byte[] GetBytes(XSQLVAR xsqlvar)\r
230                 {\r
231                         if (xsqlvar.sqllen == 0 || xsqlvar.sqldata == IntPtr.Zero)\r
232                         {\r
233                                 return null;\r
234                         }\r
235 \r
236                         byte[] buffer = new     byte[xsqlvar.sqllen];\r
237 \r
238                         switch (xsqlvar.sqltype & ~1)\r
239                         {\r
240                                 case IscCodes.SQL_VARYING:\r
241                                         short length = Marshal.ReadInt16(xsqlvar.sqldata);\r
242 \r
243                                         buffer = new byte[length];\r
244 \r
245                                         IntPtr tmp = this.GetIntPtr(xsqlvar.sqldata, 2);\r
246 \r
247                                         Marshal.Copy(tmp, buffer, 0, buffer.Length);\r
248 \r
249                                         return buffer;\r
250 \r
251                                 case IscCodes.SQL_TEXT: \r
252                                 case IscCodes.SQL_SHORT:\r
253                                 case IscCodes.SQL_LONG:\r
254                                 case IscCodes.SQL_FLOAT:\r
255                                 case IscCodes.SQL_DOUBLE:\r
256                                 case IscCodes.SQL_D_FLOAT:\r
257                                 case IscCodes.SQL_QUAD:\r
258                                 case IscCodes.SQL_INT64:\r
259                                 case IscCodes.SQL_BLOB:\r
260                                 case IscCodes.SQL_ARRAY:        \r
261                                 case IscCodes.SQL_TIMESTAMP:\r
262                                 case IscCodes.SQL_TYPE_TIME:\r
263                                 case IscCodes.SQL_TYPE_DATE:\r
264                                         Marshal.Copy(xsqlvar.sqldata, buffer, 0, buffer.Length);\r
265 \r
266                                         return buffer;\r
267 \r
268                                 default:\r
269                                         throw new NotSupportedException("Unknown data type");\r
270                         }\r
271                 }\r
272 \r
273                 private byte[] GetBytes(DbField field)\r
274                 {\r
275                         if (field.DbValue.IsDBNull())\r
276                         {\r
277                                 int     length = field.Length;\r
278                                 \r
279                                 if (field.SqlType == IscCodes.SQL_VARYING)\r
280                                 {\r
281                                         // Add two bytes more for store value length\r
282                                         length += 2;\r
283                                 }\r
284 \r
285                                 return new byte[length];\r
286                         }\r
287 \r
288                         switch (field.DbDataType)\r
289                         {\r
290                                 case DbDataType.Char:\r
291                                 {\r
292                                         string svalue = field.DbValue.GetString();\r
293 \r
294                                         if ((field.Length %     field.Charset.BytesPerCharacter) == 0 &&\r
295                                                 svalue.Length > field.CharCount)\r
296                                         {        \r
297                                                 throw new IscException(335544321);       \r
298                                         }\r
299 \r
300                                         byte[] buffer = new     byte[field.Length];\r
301                                         for     (int i = 0;     i <     buffer.Length; i++)\r
302                                         {\r
303                                                 buffer[i] = 32;\r
304                                         }\r
305 \r
306                                         byte[] bytes = field.Charset.GetBytes(svalue);\r
307 \r
308                                         Buffer.BlockCopy(bytes, 0, buffer, 0, bytes.Length);\r
309 \r
310                                         return buffer;\r
311                                 }\r
312                                 \r
313                                 case DbDataType.VarChar:\r
314                                 {\r
315                                         string svalue = field.Value.ToString();\r
316 \r
317                                         if ((field.Length %     field.Charset.BytesPerCharacter) == 0 &&\r
318                                                 svalue.Length > field.CharCount)\r
319                                         {        \r
320                                                 throw new IscException(335544321);       \r
321                                         }\r
322 \r
323                                         byte[] sbuffer = field.Charset.GetBytes(svalue);\r
324 \r
325                                         byte[] buffer = new     byte[field.Length +     2];\r
326 \r
327                                         // Copy length\r
328                                         Buffer.BlockCopy(\r
329                                                 BitConverter.GetBytes((short)sbuffer.Length), \r
330                                          0, buffer, 0, 2);\r
331                                         \r
332                                         // Copy string value\r
333                                         Buffer.BlockCopy(sbuffer, 0, buffer, 2, sbuffer.Length);\r
334 \r
335                                         return buffer;\r
336                                 }\r
337 \r
338                                 case DbDataType.Numeric:\r
339                                 case DbDataType.Decimal:\r
340                                         return this.GetNumericBytes(field);\r
341 \r
342                                 case DbDataType.SmallInt:\r
343                                         return BitConverter.GetBytes(field.DbValue.GetInt16());\r
344 \r
345                                 case DbDataType.Integer:\r
346                                         return BitConverter.GetBytes(field.DbValue.GetInt32());\r
347 \r
348                                 case DbDataType.Array:\r
349                                 case DbDataType.Binary:\r
350                                 case DbDataType.Text:\r
351                                 case DbDataType.BigInt:\r
352                                         return BitConverter.GetBytes(field.DbValue.GetInt64());\r
353 \r
354                                 case DbDataType.Float:\r
355                                         return BitConverter.GetBytes(field.DbValue.GetFloat());\r
356                                                                         \r
357                                 case DbDataType.Double:\r
358                                         return BitConverter.GetBytes(field.DbValue.GetDouble());\r
359 \r
360                                 case DbDataType.Date:\r
361                                         return BitConverter.GetBytes(\r
362                                                 TypeEncoder.EncodeDate(field.DbValue.GetDateTime()));\r
363                                 \r
364                                 case DbDataType.Time:\r
365                                         return BitConverter.GetBytes(\r
366                                                 TypeEncoder.EncodeTime(field.DbValue.GetDateTime()));\r
367                                 \r
368                                 case DbDataType.TimeStamp:\r
369                                         byte[] date = BitConverter.GetBytes(\r
370                                                 TypeEncoder.EncodeDate(field.DbValue.GetDateTime()));\r
371                                         \r
372                                         byte[] time = BitConverter.GetBytes(\r
373                                                 TypeEncoder.EncodeTime(field.DbValue.GetDateTime()));\r
374                                         \r
375                                         byte[] result = new     byte[8];\r
376 \r
377                                         Buffer.BlockCopy(date, 0, result, 0, date.Length);\r
378                                         Buffer.BlockCopy(time, 0, result, 4, time.Length);\r
379 \r
380                                         return result;\r
381 \r
382                                 case DbDataType.Guid:\r
383                                         return field.DbValue.GetGuid().ToByteArray();\r
384 \r
385                                 default:\r
386                                         throw new NotSupportedException("Unknown data type");\r
387                         }\r
388                 }\r
389 \r
390                 private byte[] GetNumericBytes(DbField field)\r
391                 {\r
392                         decimal value = field.DbValue.GetDecimal();\r
393                         object  numeric = TypeEncoder.EncodeDecimal(value, field.NumericScale, field.DataType);\r
394 \r
395                         switch (field.SqlType)\r
396                         {\r
397                                 case IscCodes.SQL_SHORT:\r
398                                         return BitConverter.GetBytes((short)numeric);\r
399 \r
400                                 case IscCodes.SQL_LONG:\r
401                                         return BitConverter.GetBytes((int)numeric);\r
402 \r
403                                 case IscCodes.SQL_INT64:\r
404                                 case IscCodes.SQL_QUAD:\r
405                                         return BitConverter.GetBytes((long)numeric);\r
406 \r
407                                 case IscCodes.SQL_DOUBLE:\r
408                                         return BitConverter.GetBytes(field.DbValue.GetDouble());\r
409 \r
410                                 default:\r
411                                         return null;\r
412                         }\r
413                 }\r
414 \r
415                 private byte[] GetStringBuffer(Charset charset, string value)\r
416                 {\r
417                         byte[] buffer = new     byte[32];\r
418                         \r
419                         charset.GetBytes(value, 0, value.Length, buffer, 0);\r
420 \r
421                         return buffer;\r
422                 }\r
423 \r
424                 private string GetString(Charset charset, byte[] buffer)\r
425                 {\r
426                         string value = charset.GetString(buffer);\r
427 \r
428                         return value.Replace('\0', ' ').Trim();\r
429                 }\r
430 \r
431                 #endregion\r
432         }\r
433 }\r