2 * Firebird ADO.NET Data provider for .NET and Mono
\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
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
15 * Copyright (c) 2002, 2005 Carlos Guzman Alvarez
\r
16 * All Rights Reserved.
\r
20 using System.Runtime.InteropServices;
\r
23 using FirebirdSql.Data.Common;
\r
25 namespace FirebirdSql.Data.Embedded
\r
27 internal sealed class XsqldaMarshaler
\r
29 #region Static Fields
\r
31 private static XsqldaMarshaler instance;
\r
35 #region Constructors
\r
37 private XsqldaMarshaler()
\r
45 public static XsqldaMarshaler GetInstance()
\r
47 if (XsqldaMarshaler.instance == null)
\r
49 XsqldaMarshaler.instance = new XsqldaMarshaler();
\r
52 return XsqldaMarshaler.instance;
\r
55 public void CleanUpNativeData(ref IntPtr pNativeData)
\r
57 if (pNativeData != IntPtr.Zero)
\r
59 // Obtain XSQLDA information
\r
60 XSQLDA xsqlda = new XSQLDA();
\r
62 xsqlda = (XSQLDA)Marshal.PtrToStructure(pNativeData, typeof(XSQLDA));
\r
64 // Destroy XSQLDA structure
\r
65 Marshal.DestroyStructure(pNativeData, typeof(XSQLDA));
\r
67 // Destroy XSQLVAR structures
\r
68 for (int i = 0; i < xsqlda.sqln; i++)
\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
74 if (sqlvar.sqldata != IntPtr.Zero)
\r
76 Marshal.FreeHGlobal(sqlvar.sqldata);
\r
77 sqlvar.sqldata = IntPtr.Zero;
\r
79 if (sqlvar.sqlind != IntPtr.Zero)
\r
81 Marshal.FreeHGlobal(sqlvar.sqlind);
\r
82 sqlvar.sqlind = IntPtr.Zero;
\r
85 Marshal.DestroyStructure(
\r
86 this.GetIntPtr(pNativeData, this.ComputeLength(i)), typeof(XSQLVAR));
\r
89 // Free pointer memory
\r
90 Marshal.FreeHGlobal(pNativeData);
\r
92 pNativeData = IntPtr.Zero;
\r
96 public IntPtr MarshalManagedToNative(Charset charset, Descriptor descriptor)
\r
98 // Set up XSQLDA structure
\r
99 XSQLDA xsqlda = new XSQLDA();
\r
101 xsqlda.version = descriptor.Version;
\r
102 xsqlda.sqln = descriptor.Count;
\r
103 xsqlda.sqld = descriptor.ActualCount;
\r
105 XSQLVAR[] xsqlvar = new XSQLVAR[descriptor.Count];
\r
107 for (int i = 0; i < xsqlvar.Length; i++)
\r
109 // Create a new XSQLVAR structure and fill it
\r
110 xsqlvar[i] = new XSQLVAR();
\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
117 // Create a new pointer for the xsqlvar data
\r
118 byte[] buffer = this.GetBytes(descriptor[i]);
\r
119 if (buffer.Length > 0)
\r
121 xsqlvar[i].sqldata = Marshal.AllocHGlobal(buffer.Length);
\r
122 Marshal.Copy(buffer, 0, xsqlvar[i].sqldata, buffer.Length);
\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
130 xsqlvar[i].sqlname = this.GetStringBuffer(charset, descriptor[i].Name);
\r
131 xsqlvar[i].sqlname_length = (short)xsqlvar[i].sqlname.Length;
\r
134 xsqlvar[i].relname = this.GetStringBuffer(charset, descriptor[i].Relation);
\r
135 xsqlvar[i].relname_length = (short)xsqlvar[i].relname.Length;
\r
138 xsqlvar[i].ownername = this.GetStringBuffer(charset, descriptor[i].Owner);
\r
139 xsqlvar[i].ownername_length = (short)xsqlvar[i].ownername.Length;
\r
142 xsqlvar[i].aliasname = this.GetStringBuffer(charset, descriptor[i].Alias);
\r
143 xsqlvar[i].aliasname_length = (short)xsqlvar[i].aliasname.Length;
\r
146 return this.MarshalManagedToNative(xsqlda, xsqlvar);
\r
149 public IntPtr MarshalManagedToNative(XSQLDA xsqlda, XSQLVAR[] xsqlvar)
\r
151 int size = this.ComputeLength(xsqlda.sqln);
\r
152 IntPtr ptr = Marshal.AllocHGlobal(size);
\r
154 Marshal.StructureToPtr(xsqlda, ptr, true);
\r
156 for (int i = 0; i < xsqlvar.Length; i++)
\r
158 int offset = this.ComputeLength(i);
\r
159 Marshal.StructureToPtr(xsqlvar[i], this.GetIntPtr(ptr, offset), true);
\r
165 public Descriptor MarshalNativeToManaged(Charset charset, IntPtr pNativeData)
\r
167 // Obtain XSQLDA information
\r
168 XSQLDA xsqlda = new XSQLDA();
\r
170 xsqlda = (XSQLDA)Marshal.PtrToStructure(pNativeData, typeof(XSQLDA));
\r
172 // Create a new Descriptor
\r
173 Descriptor descriptor = new Descriptor(xsqlda.sqln);
\r
174 descriptor.ActualCount = xsqlda.sqld;
\r
176 // Obtain XSQLVAR members information
\r
177 XSQLVAR[] xsqlvar = new XSQLVAR[xsqlda.sqln];
\r
179 for (int i = 0; i < xsqlvar.Length; i++)
\r
181 xsqlvar[i] = (XSQLVAR)Marshal.PtrToStructure(
\r
182 this.GetIntPtr(pNativeData, this.ComputeLength(i)), typeof(XSQLVAR));
\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
190 // Decode sqlind value
\r
191 if (xsqlvar[i].sqlind == IntPtr.Zero)
\r
193 descriptor[i].NullFlag = 0;
\r
197 descriptor[i].NullFlag = Marshal.ReadInt16(xsqlvar[i].sqlind);
\r
201 if (descriptor[i].NullFlag != -1)
\r
203 descriptor[i].SetValue(this.GetBytes(xsqlvar[i]));
\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
217 #region Private methods
\r
219 private IntPtr GetIntPtr(IntPtr ptr, int offset)
\r
221 return (IntPtr)(ptr.ToInt32() + offset);
\r
224 private int ComputeLength(int n)
\r
226 return (Marshal.SizeOf(typeof(XSQLDA)) + n * Marshal.SizeOf(typeof(XSQLVAR)));
\r
229 private byte[] GetBytes(XSQLVAR xsqlvar)
\r
231 if (xsqlvar.sqllen == 0 || xsqlvar.sqldata == IntPtr.Zero)
\r
236 byte[] buffer = new byte[xsqlvar.sqllen];
\r
238 switch (xsqlvar.sqltype & ~1)
\r
240 case IscCodes.SQL_VARYING:
\r
241 short length = Marshal.ReadInt16(xsqlvar.sqldata);
\r
243 buffer = new byte[length];
\r
245 IntPtr tmp = this.GetIntPtr(xsqlvar.sqldata, 2);
\r
247 Marshal.Copy(tmp, buffer, 0, buffer.Length);
\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
269 throw new NotSupportedException("Unknown data type");
\r
273 private byte[] GetBytes(DbField field)
\r
275 if (field.DbValue.IsDBNull())
\r
277 int length = field.Length;
\r
279 if (field.SqlType == IscCodes.SQL_VARYING)
\r
281 // Add two bytes more for store value length
\r
285 return new byte[length];
\r
288 switch (field.DbDataType)
\r
290 case DbDataType.Char:
\r
292 string svalue = field.DbValue.GetString();
\r
294 if ((field.Length % field.Charset.BytesPerCharacter) == 0 &&
\r
295 svalue.Length > field.CharCount)
\r
297 throw new IscException(335544321);
\r
300 byte[] buffer = new byte[field.Length];
\r
301 for (int i = 0; i < buffer.Length; i++)
\r
306 byte[] bytes = field.Charset.GetBytes(svalue);
\r
308 Buffer.BlockCopy(bytes, 0, buffer, 0, bytes.Length);
\r
313 case DbDataType.VarChar:
\r
315 string svalue = field.Value.ToString();
\r
317 if ((field.Length % field.Charset.BytesPerCharacter) == 0 &&
\r
318 svalue.Length > field.CharCount)
\r
320 throw new IscException(335544321);
\r
323 byte[] sbuffer = field.Charset.GetBytes(svalue);
\r
325 byte[] buffer = new byte[field.Length + 2];
\r
329 BitConverter.GetBytes((short)sbuffer.Length),
\r
332 // Copy string value
\r
333 Buffer.BlockCopy(sbuffer, 0, buffer, 2, sbuffer.Length);
\r
338 case DbDataType.Numeric:
\r
339 case DbDataType.Decimal:
\r
340 return this.GetNumericBytes(field);
\r
342 case DbDataType.SmallInt:
\r
343 return BitConverter.GetBytes(field.DbValue.GetInt16());
\r
345 case DbDataType.Integer:
\r
346 return BitConverter.GetBytes(field.DbValue.GetInt32());
\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
354 case DbDataType.Float:
\r
355 return BitConverter.GetBytes(field.DbValue.GetFloat());
\r
357 case DbDataType.Double:
\r
358 return BitConverter.GetBytes(field.DbValue.GetDouble());
\r
360 case DbDataType.Date:
\r
361 return BitConverter.GetBytes(
\r
362 TypeEncoder.EncodeDate(field.DbValue.GetDateTime()));
\r
364 case DbDataType.Time:
\r
365 return BitConverter.GetBytes(
\r
366 TypeEncoder.EncodeTime(field.DbValue.GetDateTime()));
\r
368 case DbDataType.TimeStamp:
\r
369 byte[] date = BitConverter.GetBytes(
\r
370 TypeEncoder.EncodeDate(field.DbValue.GetDateTime()));
\r
372 byte[] time = BitConverter.GetBytes(
\r
373 TypeEncoder.EncodeTime(field.DbValue.GetDateTime()));
\r
375 byte[] result = new byte[8];
\r
377 Buffer.BlockCopy(date, 0, result, 0, date.Length);
\r
378 Buffer.BlockCopy(time, 0, result, 4, time.Length);
\r
382 case DbDataType.Guid:
\r
383 return field.DbValue.GetGuid().ToByteArray();
\r
386 throw new NotSupportedException("Unknown data type");
\r
390 private byte[] GetNumericBytes(DbField field)
\r
392 decimal value = field.DbValue.GetDecimal();
\r
393 object numeric = TypeEncoder.EncodeDecimal(value, field.NumericScale, field.DataType);
\r
395 switch (field.SqlType)
\r
397 case IscCodes.SQL_SHORT:
\r
398 return BitConverter.GetBytes((short)numeric);
\r
400 case IscCodes.SQL_LONG:
\r
401 return BitConverter.GetBytes((int)numeric);
\r
403 case IscCodes.SQL_INT64:
\r
404 case IscCodes.SQL_QUAD:
\r
405 return BitConverter.GetBytes((long)numeric);
\r
407 case IscCodes.SQL_DOUBLE:
\r
408 return BitConverter.GetBytes(field.DbValue.GetDouble());
\r
415 private byte[] GetStringBuffer(Charset charset, string value)
\r
417 byte[] buffer = new byte[32];
\r
419 charset.GetBytes(value, 0, value.Length, buffer, 0);
\r
424 private string GetString(Charset charset, byte[] buffer)
\r
426 string value = charset.GetString(buffer);
\r
428 return value.Replace('\0', ' ').Trim();
\r