2 * Firebird ADO.NET Data provider for .NET and Mono
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.
20 using System
.Runtime
.InteropServices
;
23 using FirebirdSql
.Data
.Common
;
25 namespace FirebirdSql
.Data
.Embedded
27 internal sealed class XsqldaMarshaler
31 private static XsqldaMarshaler instance
;
37 private XsqldaMarshaler()
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
);
130 xsqlvar
[i
].sqlname
= this.GetStringBuffer(charset
, descriptor
[i
].Name
);
131 xsqlvar
[i
].sqlname_length
= (short)xsqlvar
[i
].sqlname
.Length
;
134 xsqlvar
[i
].relname
= this.GetStringBuffer(charset
, descriptor
[i
].Relation
);
135 xsqlvar
[i
].relname_length
= (short)xsqlvar
[i
].relname
.Length
;
138 xsqlvar
[i
].ownername
= this.GetStringBuffer(charset
, descriptor
[i
].Owner
);
139 xsqlvar
[i
].ownername_length
= (short)xsqlvar
[i
].ownername
.Length
;
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);
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;
197 descriptor
[i
].NullFlag
= Marshal
.ReadInt16(xsqlvar
[i
].sqlind
);
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
);
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
)
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
);
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
);
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
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
++)
306 byte[] bytes
= field
.Charset
.GetBytes(svalue
);
308 Buffer
.BlockCopy(bytes
, 0, buffer
, 0, bytes
.Length
);
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];
329 BitConverter
.GetBytes((short)sbuffer
.Length
),
333 Buffer
.BlockCopy(sbuffer
, 0, buffer
, 2, sbuffer
.Length
);
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
);
382 case DbDataType
.Guid
:
383 return field
.DbValue
.GetGuid().ToByteArray();
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());
415 private byte[] GetStringBuffer(Charset charset
, string value)
417 byte[] buffer
= new byte[32];
419 charset
.GetBytes(value, 0, value.Length
, buffer
, 0);
424 private string GetString(Charset charset
, byte[] buffer
)
426 string value = charset
.GetString(buffer
);
428 return value.Replace('\0', ' ').Trim();