2009-09-24 Zoltan Varga <vargaz@gmail.com>
[mcs.git] / class / FirebirdSql.Data.Firebird / FirebirdSql.Data.Embedded / FesArray.cs
blobf1b4337577e6d7a0169fad2c5aecf4af0a731854
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.Collections;
21 using System.Globalization;
22 using System.IO;
23 using System.Net;
24 using System.Runtime.InteropServices;
25 using System.Text;
27 using FirebirdSql.Data.Common;
29 namespace FirebirdSql.Data.Embedded
31 internal sealed class FesArray : ArrayBase
33 #region Fields
35 private long handle;
36 private FesDatabase db;
37 private FesTransaction transaction;
39 #endregion
41 #region Properties
43 public override long Handle
45 get { return this.handle; }
46 set { this.handle = value; }
49 public override IDatabase DB
51 get { return this.db; }
52 set { this.db = (FesDatabase)value; }
55 public override ITransaction Transaction
57 get { return this.transaction; }
58 set { this.transaction = (FesTransaction)value; }
61 #endregion
63 #region Constructors
65 public FesArray(ArrayDesc descriptor) : base(descriptor)
69 public FesArray(
70 IDatabase db,
71 ITransaction transaction,
72 string tableName,
73 string fieldName) : this(db, transaction, -1, tableName, fieldName)
77 public FesArray(
78 IDatabase db,
79 ITransaction transaction,
80 long handle,
81 string tableName,
82 string fieldName) : base(tableName, fieldName)
84 if (!(db is FesDatabase))
86 throw new ArgumentException("Specified argument is not of GdsDatabase type.");
88 if (!(transaction is FesTransaction))
90 throw new ArgumentException("Specified argument is not of GdsTransaction type.");
92 this.db = (FesDatabase)db;
93 this.transaction = (FesTransaction)transaction;
94 this.handle = handle;
96 this.LookupBounds();
99 #endregion
101 #region Methods
103 public override byte[] GetSlice(int sliceLength)
105 int[] statusVector = FesConnection.GetNewStatusVector();
107 int dbHandle = this.db.Handle;
108 int trHandle = this.transaction.Handle;
110 ArrayDescMarshaler marshaler = ArrayDescMarshaler.GetInstance();
112 IntPtr arrayDesc = marshaler.MarshalManagedToNative(this.Descriptor);
114 byte[] buffer = new byte[sliceLength];
116 FbClient.isc_array_get_slice(
117 statusVector,
118 ref dbHandle,
119 ref trHandle,
120 ref this.handle,
121 arrayDesc,
122 buffer,
123 ref sliceLength);
125 // Free memory
126 marshaler.CleanUpNativeData(ref arrayDesc);
128 FesConnection.ParseStatusVector(statusVector);
130 return buffer;
133 public override void PutSlice(System.Array sourceArray, int sliceLength)
135 int[] statusVector = FesConnection.GetNewStatusVector();
137 int dbHandle = this.db.Handle;
138 int trHandle = this.transaction.Handle;
140 ArrayDescMarshaler marshaler = ArrayDescMarshaler.GetInstance();
142 IntPtr arrayDesc = marshaler.MarshalManagedToNative(this.Descriptor);
144 // Obtain the System of type of Array elements and
145 // Fill buffer
146 Type systemType = this.GetSystemType();
148 byte[] buffer = new byte[sliceLength];
149 if (systemType.IsPrimitive)
151 Buffer.BlockCopy(sourceArray, 0, buffer, 0, buffer.Length);
153 else
155 buffer = this.EncodeSlice(this.Descriptor, sourceArray, sliceLength);
158 FbClient.isc_array_put_slice(
159 statusVector,
160 ref dbHandle,
161 ref trHandle,
162 ref this.handle,
163 arrayDesc,
164 buffer,
165 ref sliceLength);
167 // Free memory
168 marshaler.CleanUpNativeData(ref arrayDesc);
170 FesConnection.ParseStatusVector(statusVector);
173 #endregion
175 #region Protected Methods
177 protected override System.Array DecodeSlice(byte[] slice)
179 Array sliceData = null;
180 int slicePosition = 0;
181 int type = 0;
182 DbDataType dbType = DbDataType.Array;
183 Type systemType = this.GetSystemType();
184 Charset charset = this.db.Charset;
185 int[] lengths = new int[this.Descriptor.Dimensions];
186 int[] lowerBounds = new int[this.Descriptor.Dimensions];
188 // Get upper and lower bounds of each dimension
189 for (int i = 0; i < this.Descriptor.Dimensions; i++)
191 lowerBounds[i] = this.Descriptor.Bounds[i].LowerBound;
192 lengths[i] = this.Descriptor.Bounds[i].UpperBound;
194 if (lowerBounds[i] == 0)
196 lengths[i]++;
200 // Create slice arrays
201 sliceData = Array.CreateInstance(systemType, lengths, lowerBounds);
203 Array tempData = Array.CreateInstance(systemType, sliceData.Length);
205 // Infer data types
206 type = TypeHelper.GetFbType(this.Descriptor.DataType);
207 dbType = TypeHelper.GetDbDataType(this.Descriptor.DataType, 0, this.Descriptor.Scale);
209 int itemLength = this.Descriptor.Length;
211 for (int i = 0; i < tempData.Length; i++)
213 if (slicePosition >= slice.Length)
215 break;
218 switch(dbType)
220 case DbDataType.Char:
221 tempData.SetValue(charset.GetString(slice, slicePosition, itemLength), i);
222 break;
224 case DbDataType.VarChar:
226 int index = slicePosition;
227 int count = 0;
228 while (slice[index++] != 0)
230 count ++;
232 tempData.SetValue(charset.GetString(slice, slicePosition, count), i);
234 slicePosition += 2;
236 break;
238 case DbDataType.SmallInt:
239 tempData.SetValue(BitConverter.ToInt16(slice, slicePosition), i);
240 break;
242 case DbDataType.Integer:
243 tempData.SetValue(BitConverter.ToInt32(slice, slicePosition), i);
244 break;
246 case DbDataType.BigInt:
247 tempData.SetValue(BitConverter.ToInt64(slice, slicePosition), i);
248 break;
250 case DbDataType.Decimal:
251 case DbDataType.Numeric:
253 object evalue = null;
255 switch (type)
257 case IscCodes.SQL_SHORT:
258 evalue = BitConverter.ToInt16(slice, slicePosition);
259 break;
261 case IscCodes.SQL_LONG:
262 evalue = BitConverter.ToInt32(slice, slicePosition);
263 break;
265 case IscCodes.SQL_QUAD:
266 case IscCodes.SQL_INT64:
267 evalue = BitConverter.ToInt64(slice, slicePosition);
268 break;
271 decimal dvalue = TypeDecoder.DecodeDecimal(evalue, this.Descriptor.Scale, type);
273 tempData.SetValue(dvalue, i);
275 break;
277 case DbDataType.Double:
278 tempData.SetValue(BitConverter.ToDouble(slice, slicePosition), i);
279 break;
281 case DbDataType.Float:
282 tempData.SetValue(BitConverter.ToSingle(slice, slicePosition), i);
283 break;
285 case DbDataType.Date:
287 int idate = BitConverter.ToInt32(slice, slicePosition);
289 DateTime date = TypeDecoder.DecodeDate(idate);
291 tempData.SetValue(date, i);
293 break;
295 case DbDataType.Time:
297 int itime = BitConverter.ToInt32(slice, slicePosition);
299 DateTime time = TypeDecoder.DecodeTime(itime);
301 tempData.SetValue(time, i);
303 break;
305 case DbDataType.TimeStamp:
307 int idate = BitConverter.ToInt32(slice, slicePosition);
308 int itime = BitConverter.ToInt32(slice, slicePosition + 4);
310 DateTime date = TypeDecoder.DecodeDate(idate);
311 DateTime time = TypeDecoder.DecodeTime(itime);
313 DateTime timestamp = new System.DateTime(
314 date.Year, date.Month, date.Day,
315 time.Hour,time.Minute, time.Second, time.Millisecond);
317 tempData.SetValue(timestamp, i);
319 break;
322 slicePosition += itemLength;
325 if (systemType.IsPrimitive)
327 // For primitive types we can use System.Buffer to copy generated data to destination array
328 Buffer.BlockCopy(tempData, 0, sliceData, 0, Buffer.ByteLength(tempData));
330 else
332 sliceData = tempData;
335 return sliceData;
338 #endregion
340 #region Private Metods
342 private byte[] EncodeSlice(ArrayDesc desc, Array sourceArray, int length)
344 BinaryWriter writer = new BinaryWriter(new MemoryStream());
345 IEnumerator i = sourceArray.GetEnumerator();
346 Charset charset = this.db.Charset;
347 DbDataType dbType = DbDataType.Array;
348 int type = 0;
349 int subtype = (this.Descriptor.Scale < 0) ? 2 : 0;
351 // Infer data types
352 type = TypeHelper.GetFbType(this.Descriptor.DataType);
353 dbType = TypeHelper.GetDbDataType(this.Descriptor.DataType, subtype, this.Descriptor.Scale);
355 while (i.MoveNext())
357 switch (dbType)
359 case DbDataType.Char:
361 string value = i.Current != null ? (string)i.Current : String.Empty;
362 byte[] buffer = charset.GetBytes(value);
364 writer.Write(buffer);
366 if (desc.Length > buffer.Length)
368 for (int j = buffer.Length; j < desc.Length; j++)
370 writer.Write((byte)32);
374 break;
376 case DbDataType.VarChar:
378 string value = i.Current != null ? (string)i.Current : String.Empty;
380 value = value.TrimEnd();
382 byte[] buffer = charset.GetBytes(value);
383 writer.Write(buffer);
385 if (desc.Length > buffer.Length)
387 for (int j = buffer.Length; j < desc.Length; j++)
389 writer.Write((byte)0);
392 writer.Write((short)0);
394 break;
396 case DbDataType.SmallInt:
397 writer.Write((short)i.Current);
398 break;
400 case DbDataType.Integer:
401 writer.Write((int)i.Current);
402 break;
404 case DbDataType.BigInt:
405 writer.Write((long)i.Current);
406 break;
408 case DbDataType.Float:
409 writer.Write((float)i.Current);
410 break;
412 case DbDataType.Double:
413 writer.Write((double)i.Current);
414 break;
416 case DbDataType.Numeric:
417 case DbDataType.Decimal:
419 object numeric = TypeEncoder.EncodeDecimal((decimal)i.Current, desc.Scale, type);
421 switch (type)
423 case IscCodes.SQL_SHORT:
424 writer.Write((short)numeric);
425 break;
427 case IscCodes.SQL_LONG:
428 writer.Write((int)numeric);
429 break;
431 case IscCodes.SQL_QUAD:
432 case IscCodes.SQL_INT64:
433 writer.Write((long)numeric);
434 break;
437 break;
439 case DbDataType.Date:
440 writer.Write(TypeEncoder.EncodeDate(Convert.ToDateTime(i.Current, CultureInfo.CurrentCulture.DateTimeFormat)));
441 break;
443 case DbDataType.Time:
444 writer.Write(TypeEncoder.EncodeTime(Convert.ToDateTime(i.Current, CultureInfo.CurrentCulture.DateTimeFormat)));
445 break;
447 case DbDataType.TimeStamp:
448 writer.Write(TypeEncoder.EncodeDate(Convert.ToDateTime(i.Current, CultureInfo.CurrentCulture.DateTimeFormat)));
449 writer.Write(TypeEncoder.EncodeTime(Convert.ToDateTime(i.Current, CultureInfo.CurrentCulture.DateTimeFormat)));
450 break;
452 default:
453 throw new NotSupportedException("Unknown data type");
457 return ((MemoryStream)writer.BaseStream).ToArray();
460 #endregion