2009-09-24 Zoltan Varga <vargaz@gmail.com>
[mcs.git] / class / FirebirdSql.Data.Firebird / FirebirdSql.Data.Embedded / XsqldaMarshaler.cs
blob5998c09360118fec4c00783542155e57597aa535
1 /*
2 * Firebird ADO.NET Data provider for .NET and Mono
3 *
4 * The contents of this file are subject to the Initial
5 * Developer's Public License Version 1.0 (the "License");
6 * you may not use this file except in compliance with the
7 * License. You may obtain a copy of the License at
8 * http://www.firebirdsql.org/index.php?op=doc&id=idpl
10 * Software distributed under the License is distributed on
11 * an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
12 * express or implied. See the License for the specific
13 * language governing rights and limitations under the License.
15 * Copyright (c) 2002, 2005 Carlos Guzman Alvarez
16 * All Rights Reserved.
19 using System;
20 using System.Runtime.InteropServices;
21 using System.Text;
23 using FirebirdSql.Data.Common;
25 namespace FirebirdSql.Data.Embedded
27 internal sealed class XsqldaMarshaler
29 #region Static Fields
31 private static XsqldaMarshaler instance;
33 #endregion
35 #region Constructors
37 private XsqldaMarshaler()
41 #endregion
43 #region Methods
45 public static XsqldaMarshaler GetInstance()
47 if (XsqldaMarshaler.instance == null)
49 XsqldaMarshaler.instance = new XsqldaMarshaler();
52 return XsqldaMarshaler.instance;
55 public void CleanUpNativeData(ref IntPtr pNativeData)
57 if (pNativeData != IntPtr.Zero)
59 // Obtain XSQLDA information
60 XSQLDA xsqlda = new XSQLDA();
62 xsqlda = (XSQLDA)Marshal.PtrToStructure(pNativeData, typeof(XSQLDA));
64 // Destroy XSQLDA structure
65 Marshal.DestroyStructure(pNativeData, typeof(XSQLDA));
67 // Destroy XSQLVAR structures
68 for (int i = 0; i < xsqlda.sqln; i++)
70 // Free sqldata and sqlind pointers if needed
71 XSQLVAR sqlvar = (XSQLVAR)Marshal.PtrToStructure(
72 this.GetIntPtr(pNativeData, this.ComputeLength(i)), typeof(XSQLVAR));
74 if (sqlvar.sqldata != IntPtr.Zero)
76 Marshal.FreeHGlobal(sqlvar.sqldata);
77 sqlvar.sqldata = IntPtr.Zero;
79 if (sqlvar.sqlind != IntPtr.Zero)
81 Marshal.FreeHGlobal(sqlvar.sqlind);
82 sqlvar.sqlind = IntPtr.Zero;
85 Marshal.DestroyStructure(
86 this.GetIntPtr(pNativeData, this.ComputeLength(i)), typeof(XSQLVAR));
89 // Free pointer memory
90 Marshal.FreeHGlobal(pNativeData);
92 pNativeData = IntPtr.Zero;
96 public IntPtr MarshalManagedToNative(Charset charset, Descriptor descriptor)
98 // Set up XSQLDA structure
99 XSQLDA xsqlda = new XSQLDA();
101 xsqlda.version = descriptor.Version;
102 xsqlda.sqln = descriptor.Count;
103 xsqlda.sqld = descriptor.ActualCount;
105 XSQLVAR[] xsqlvar = new XSQLVAR[descriptor.Count];
107 for (int i = 0; i < xsqlvar.Length; i++)
109 // Create a new XSQLVAR structure and fill it
110 xsqlvar[i] = new XSQLVAR();
112 xsqlvar[i].sqltype = descriptor[i].DataType;
113 xsqlvar[i].sqlscale = descriptor[i].NumericScale;
114 xsqlvar[i].sqlsubtype = descriptor[i].SubType;
115 xsqlvar[i].sqllen = descriptor[i].Length;
117 // Create a new pointer for the xsqlvar data
118 byte[] buffer = this.GetBytes(descriptor[i]);
119 if (buffer.Length > 0)
121 xsqlvar[i].sqldata = Marshal.AllocHGlobal(buffer.Length);
122 Marshal.Copy(buffer, 0, xsqlvar[i].sqldata, buffer.Length);
125 // Create a new pointer for the sqlind value
126 xsqlvar[i].sqlind = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Int16)));
127 Marshal.WriteInt16(xsqlvar[i].sqlind, descriptor[i].NullFlag);
129 // Name
130 xsqlvar[i].sqlname = this.GetStringBuffer(charset, descriptor[i].Name);
131 xsqlvar[i].sqlname_length = (short)xsqlvar[i].sqlname.Length;
133 // Relation Name
134 xsqlvar[i].relname = this.GetStringBuffer(charset, descriptor[i].Relation);
135 xsqlvar[i].relname_length = (short)xsqlvar[i].relname.Length;
137 // Owner name
138 xsqlvar[i].ownername = this.GetStringBuffer(charset, descriptor[i].Owner);
139 xsqlvar[i].ownername_length = (short)xsqlvar[i].ownername.Length;
141 // Alias name
142 xsqlvar[i].aliasname = this.GetStringBuffer(charset, descriptor[i].Alias);
143 xsqlvar[i].aliasname_length = (short)xsqlvar[i].aliasname.Length;
146 return this.MarshalManagedToNative(xsqlda, xsqlvar);
149 public IntPtr MarshalManagedToNative(XSQLDA xsqlda, XSQLVAR[] xsqlvar)
151 int size = this.ComputeLength(xsqlda.sqln);
152 IntPtr ptr = Marshal.AllocHGlobal(size);
154 Marshal.StructureToPtr(xsqlda, ptr, true);
156 for (int i = 0; i < xsqlvar.Length; i++)
158 int offset = this.ComputeLength(i);
159 Marshal.StructureToPtr(xsqlvar[i], this.GetIntPtr(ptr, offset), true);
162 return ptr;
165 public Descriptor MarshalNativeToManaged(Charset charset, IntPtr pNativeData)
167 // Obtain XSQLDA information
168 XSQLDA xsqlda = new XSQLDA();
170 xsqlda = (XSQLDA)Marshal.PtrToStructure(pNativeData, typeof(XSQLDA));
172 // Create a new Descriptor
173 Descriptor descriptor = new Descriptor(xsqlda.sqln);
174 descriptor.ActualCount = xsqlda.sqld;
176 // Obtain XSQLVAR members information
177 XSQLVAR[] xsqlvar = new XSQLVAR[xsqlda.sqln];
179 for (int i = 0; i < xsqlvar.Length; i++)
181 xsqlvar[i] = (XSQLVAR)Marshal.PtrToStructure(
182 this.GetIntPtr(pNativeData, this.ComputeLength(i)), typeof(XSQLVAR));
184 // Map XSQLVAR information to Descriptor
185 descriptor[i].DataType = xsqlvar[i].sqltype;
186 descriptor[i].NumericScale = xsqlvar[i].sqlscale;
187 descriptor[i].SubType = xsqlvar[i].sqlsubtype;
188 descriptor[i].Length = xsqlvar[i].sqllen;
190 // Decode sqlind value
191 if (xsqlvar[i].sqlind == IntPtr.Zero)
193 descriptor[i].NullFlag = 0;
195 else
197 descriptor[i].NullFlag = Marshal.ReadInt16(xsqlvar[i].sqlind);
200 // Set value
201 if (descriptor[i].NullFlag != -1)
203 descriptor[i].SetValue(this.GetBytes(xsqlvar[i]));
206 descriptor[i].Name = this.GetString(charset, xsqlvar[i].sqlname);
207 descriptor[i].Relation = this.GetString(charset, xsqlvar[i].relname);
208 descriptor[i].Owner = this.GetString(charset, xsqlvar[i].ownername);
209 descriptor[i].Alias = this.GetString(charset, xsqlvar[i].aliasname);
212 return descriptor;
215 #endregion
217 #region Private methods
219 private IntPtr GetIntPtr(IntPtr ptr, int offset)
221 return (IntPtr)(ptr.ToInt32() + offset);
224 private int ComputeLength(int n)
226 return (Marshal.SizeOf(typeof(XSQLDA)) + n * Marshal.SizeOf(typeof(XSQLVAR)));
229 private byte[] GetBytes(XSQLVAR xsqlvar)
231 if (xsqlvar.sqllen == 0 || xsqlvar.sqldata == IntPtr.Zero)
233 return null;
236 byte[] buffer = new byte[xsqlvar.sqllen];
238 switch (xsqlvar.sqltype & ~1)
240 case IscCodes.SQL_VARYING:
241 short length = Marshal.ReadInt16(xsqlvar.sqldata);
243 buffer = new byte[length];
245 IntPtr tmp = this.GetIntPtr(xsqlvar.sqldata, 2);
247 Marshal.Copy(tmp, buffer, 0, buffer.Length);
249 return buffer;
251 case IscCodes.SQL_TEXT:
252 case IscCodes.SQL_SHORT:
253 case IscCodes.SQL_LONG:
254 case IscCodes.SQL_FLOAT:
255 case IscCodes.SQL_DOUBLE:
256 case IscCodes.SQL_D_FLOAT:
257 case IscCodes.SQL_QUAD:
258 case IscCodes.SQL_INT64:
259 case IscCodes.SQL_BLOB:
260 case IscCodes.SQL_ARRAY:
261 case IscCodes.SQL_TIMESTAMP:
262 case IscCodes.SQL_TYPE_TIME:
263 case IscCodes.SQL_TYPE_DATE:
264 Marshal.Copy(xsqlvar.sqldata, buffer, 0, buffer.Length);
266 return buffer;
268 default:
269 throw new NotSupportedException("Unknown data type");
273 private byte[] GetBytes(DbField field)
275 if (field.DbValue.IsDBNull())
277 int length = field.Length;
279 if (field.SqlType == IscCodes.SQL_VARYING)
281 // Add two bytes more for store value length
282 length += 2;
285 return new byte[length];
288 switch (field.DbDataType)
290 case DbDataType.Char:
292 string svalue = field.DbValue.GetString();
294 if ((field.Length % field.Charset.BytesPerCharacter) == 0 &&
295 svalue.Length > field.CharCount)
297 throw new IscException(335544321);
300 byte[] buffer = new byte[field.Length];
301 for (int i = 0; i < buffer.Length; i++)
303 buffer[i] = 32;
306 byte[] bytes = field.Charset.GetBytes(svalue);
308 Buffer.BlockCopy(bytes, 0, buffer, 0, bytes.Length);
310 return buffer;
313 case DbDataType.VarChar:
315 string svalue = field.Value.ToString();
317 if ((field.Length % field.Charset.BytesPerCharacter) == 0 &&
318 svalue.Length > field.CharCount)
320 throw new IscException(335544321);
323 byte[] sbuffer = field.Charset.GetBytes(svalue);
325 byte[] buffer = new byte[field.Length + 2];
327 // Copy length
328 Buffer.BlockCopy(
329 BitConverter.GetBytes((short)sbuffer.Length),
330 0, buffer, 0, 2);
332 // Copy string value
333 Buffer.BlockCopy(sbuffer, 0, buffer, 2, sbuffer.Length);
335 return buffer;
338 case DbDataType.Numeric:
339 case DbDataType.Decimal:
340 return this.GetNumericBytes(field);
342 case DbDataType.SmallInt:
343 return BitConverter.GetBytes(field.DbValue.GetInt16());
345 case DbDataType.Integer:
346 return BitConverter.GetBytes(field.DbValue.GetInt32());
348 case DbDataType.Array:
349 case DbDataType.Binary:
350 case DbDataType.Text:
351 case DbDataType.BigInt:
352 return BitConverter.GetBytes(field.DbValue.GetInt64());
354 case DbDataType.Float:
355 return BitConverter.GetBytes(field.DbValue.GetFloat());
357 case DbDataType.Double:
358 return BitConverter.GetBytes(field.DbValue.GetDouble());
360 case DbDataType.Date:
361 return BitConverter.GetBytes(
362 TypeEncoder.EncodeDate(field.DbValue.GetDateTime()));
364 case DbDataType.Time:
365 return BitConverter.GetBytes(
366 TypeEncoder.EncodeTime(field.DbValue.GetDateTime()));
368 case DbDataType.TimeStamp:
369 byte[] date = BitConverter.GetBytes(
370 TypeEncoder.EncodeDate(field.DbValue.GetDateTime()));
372 byte[] time = BitConverter.GetBytes(
373 TypeEncoder.EncodeTime(field.DbValue.GetDateTime()));
375 byte[] result = new byte[8];
377 Buffer.BlockCopy(date, 0, result, 0, date.Length);
378 Buffer.BlockCopy(time, 0, result, 4, time.Length);
380 return result;
382 case DbDataType.Guid:
383 return field.DbValue.GetGuid().ToByteArray();
385 default:
386 throw new NotSupportedException("Unknown data type");
390 private byte[] GetNumericBytes(DbField field)
392 decimal value = field.DbValue.GetDecimal();
393 object numeric = TypeEncoder.EncodeDecimal(value, field.NumericScale, field.DataType);
395 switch (field.SqlType)
397 case IscCodes.SQL_SHORT:
398 return BitConverter.GetBytes((short)numeric);
400 case IscCodes.SQL_LONG:
401 return BitConverter.GetBytes((int)numeric);
403 case IscCodes.SQL_INT64:
404 case IscCodes.SQL_QUAD:
405 return BitConverter.GetBytes((long)numeric);
407 case IscCodes.SQL_DOUBLE:
408 return BitConverter.GetBytes(field.DbValue.GetDouble());
410 default:
411 return null;
415 private byte[] GetStringBuffer(Charset charset, string value)
417 byte[] buffer = new byte[32];
419 charset.GetBytes(value, 0, value.Length, buffer, 0);
421 return buffer;
424 private string GetString(Charset charset, byte[] buffer)
426 string value = charset.GetString(buffer);
428 return value.Replace('\0', ' ').Trim();
431 #endregion