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.
23 using System.Collections;
24 using System.Globalization;
26 using FirebirdSql.Data.Common;
28 namespace FirebirdSql.Data.Gds
30 internal sealed class GdsArray : ArrayBase
35 private GdsDatabase db;
36 private GdsTransaction transaction;
42 public override long Handle
44 get { return this.handle; }
45 set { this.handle = value; }
48 public override IDatabase DB
50 get { return this.db; }
51 set { this.db = (GdsDatabase)value; }
54 public override ITransaction Transaction
56 get { return this.transaction; }
57 set { this.transaction = (GdsTransaction)value; }
64 public GdsArray(ArrayDesc descriptor) : base(descriptor)
68 public GdsArray(IDatabase db, ITransaction transaction, string tableName, string fieldName)
69 : this(db, transaction, -1, tableName, fieldName)
73 public GdsArray(IDatabase db, ITransaction transaction, long handle, string tableName, string fieldName)
74 : base(tableName, fieldName)
76 if (!(db is GdsDatabase))
78 throw new ArgumentException("Specified argument is not of GdsDatabase type.");
81 if (!(transaction is GdsTransaction))
83 throw new ArgumentException("Specified argument is not of GdsTransaction type.");
86 this.db = (GdsDatabase)db;
87 this.transaction = (GdsTransaction)transaction;
97 public override byte[] GetSlice(int sliceLength)
103 byte[] sdl = this.GenerateSDL(this.Descriptor);
105 this.db.Send.Write(IscCodes.op_get_slice); // Op code
106 this.db.Send.Write(this.transaction.Handle);// Transaction
107 this.db.Send.Write(this.handle); // Array id
108 this.db.Send.Write(sliceLength); // Slice length
109 this.db.Send.WriteBuffer(sdl); // Slice descriptor language
110 this.db.Send.Write(String.Empty); // Slice parameters
111 this.db.Send.Write(0); // Slice proper
112 this.db.Send.Flush();
114 return this.ReceiveSliceResponse(this.Descriptor);
118 throw new IscException(IscCodes.isc_net_read_err);
123 public override void PutSlice(System.Array sourceArray, int sliceLength)
129 byte[] sdl = this.GenerateSDL(this.Descriptor);
130 byte[] slice = this.EncodeSliceArray(sourceArray);
132 this.db.Send.Write(IscCodes.op_put_slice); // Op code
133 this.db.Send.Write(this.transaction.Handle);// Transaction
134 this.db.Send.Write((long)0); // Array Handle
135 this.db.Send.Write(sliceLength); // Slice length
136 this.db.Send.WriteBuffer(sdl); // Slice descriptor language
137 this.db.Send.Write(String.Empty); // Slice parameters
138 this.db.Send.Write(sliceLength); // Slice length
139 this.db.Send.Write(slice, 0, slice.Length); // Slice proper
140 this.db.Send.Flush();
142 GdsResponse r = this.db.ReadGenericResponse();
144 this.handle = r.BlobId;
148 throw new IscException(IscCodes.isc_net_read_err);
155 #region Protected Methods
157 protected override System.Array DecodeSlice(byte[] slice)
159 DbDataType dbType = DbDataType.Array;
160 Array sliceData = null;
161 Array tempData = null;
162 Type systemType = this.GetSystemType();
163 int[] lengths = new int[this.Descriptor.Dimensions];
164 int[] lowerBounds = new int[this.Descriptor.Dimensions];
168 // Get upper and lower bounds of each dimension
169 for (int i = 0; i < this.Descriptor.Dimensions; i++)
171 lowerBounds[i] = this.Descriptor.Bounds[i].LowerBound;
172 lengths[i] = this.Descriptor.Bounds[i].UpperBound;
174 if (lowerBounds[i] == 0)
182 sliceData = Array.CreateInstance(systemType, lengths);
184 sliceData = Array.CreateInstance(systemType, lengths, lowerBounds);
186 tempData = Array.CreateInstance(systemType, sliceData.Length);
188 // Infer Firebird and Db datatypes
189 type = TypeHelper.GetFbType(this.Descriptor.DataType);
190 dbType = TypeHelper.GetDbDataType(this.Descriptor.DataType, 0, this.Descriptor.Scale);
193 XdrStream xdr = new XdrStream(slice, this.db.Charset);
195 while (xdr.Position < xdr.Length)
199 case DbDataType.Char:
200 tempData.SetValue(xdr.ReadString(this.Descriptor.Length), index);
203 case DbDataType.VarChar:
204 tempData.SetValue(xdr.ReadString(), index);
207 case DbDataType.SmallInt:
208 tempData.SetValue(xdr.ReadInt16(), index);
211 case DbDataType.Integer:
212 tempData.SetValue(xdr.ReadInt32(), index);
215 case DbDataType.BigInt:
216 tempData.SetValue(xdr.ReadInt64(), index);
219 case DbDataType.Numeric:
220 case DbDataType.Decimal:
221 tempData.SetValue(xdr.ReadDecimal(type, this.Descriptor.Scale), index);
224 case DbDataType.Float:
225 tempData.SetValue(xdr.ReadSingle(), index);
228 case DbDataType.Double:
229 tempData.SetValue(xdr.ReadDouble(), index);
232 case DbDataType.Date:
233 tempData.SetValue(xdr.ReadDate(), index);
236 case DbDataType.Time:
237 tempData.SetValue(xdr.ReadTime(), index);
240 case DbDataType.TimeStamp:
241 tempData.SetValue(xdr.ReadDateTime(), index);
248 if (systemType.IsPrimitive)
250 // For primitive types we can use System.Buffer to copy generated data to destination array
251 Buffer.BlockCopy(tempData, 0, sliceData, 0, Buffer.ByteLength(tempData));
255 sliceData = tempData;
266 #region Slice response methods
268 private byte[] ReceiveSliceResponse(ArrayDesc desc)
272 int operation = this.db.ReadOperation();
274 if (operation == IscCodes.op_slice)
277 bool isVariying = false;
279 int length = this.db.Receive.ReadInt32();
281 length = this.db.Receive.ReadInt32();
283 switch (desc.DataType)
285 case IscCodes.blr_text:
286 case IscCodes.blr_text2:
287 case IscCodes.blr_cstring:
288 case IscCodes.blr_cstring2:
289 elements = length / desc.Length;
290 length += elements * ((4 - desc.Length) & 3);
293 case IscCodes.blr_varying:
294 case IscCodes.blr_varying2:
295 elements = length / desc.Length;
299 case IscCodes.blr_short:
300 length = length * desc.Length;
306 XdrStream xdr = new XdrStream();
308 for (int i = 0; i < elements; i++)
310 byte[] buffer = this.db.Receive.ReadOpaque(
311 this.db.Receive.ReadInt32());
313 xdr.WriteBuffer(buffer, buffer.Length);
316 return xdr.ToArray();
320 return this.db.Receive.ReadOpaque(length);
325 this.db.SetOperation(operation);
326 this.db.ReadGenericResponse();
333 throw new IscException(IscCodes.isc_net_read_err);
339 #region Private Methods
341 private byte[] EncodeSliceArray(Array sourceArray)
343 IEnumerator i = sourceArray.GetEnumerator();
344 DbDataType dbType = DbDataType.Array;
345 Charset charset = this.db.Charset;
346 XdrStream xdr = new XdrStream(this.db.Charset);
348 int subtype = (this.Descriptor.Scale < 0) ? 2 : 0;
350 type = TypeHelper.GetFbType(this.Descriptor.DataType);
351 dbType = TypeHelper.GetDbDataType(this.Descriptor.DataType, subtype, this.Descriptor.Scale);
357 case DbDataType.Char:
358 byte[] buffer = charset.GetBytes(i.Current.ToString());
359 xdr.WriteOpaque(buffer, this.Descriptor.Length);
362 case DbDataType.VarChar:
363 xdr.Write((string)i.Current);
366 case DbDataType.SmallInt:
367 xdr.Write((short)i.Current);
370 case DbDataType.Integer:
371 xdr.Write((int)i.Current);
374 case DbDataType.BigInt:
375 xdr.Write((long)i.Current);
378 case DbDataType.Decimal:
379 case DbDataType.Numeric:
380 xdr.Write((decimal)i.Current, type, this.Descriptor.Scale);
383 case DbDataType.Float:
384 xdr.Write((float)i.Current);
387 case DbDataType.Double:
388 xdr.Write((double)i.Current);
391 case DbDataType.Date:
392 xdr.WriteDate(Convert.ToDateTime(i.Current, CultureInfo.CurrentCulture.DateTimeFormat));
395 case DbDataType.Time:
396 xdr.WriteTime(Convert.ToDateTime(i.Current, CultureInfo.CurrentCulture.DateTimeFormat));
399 case DbDataType.TimeStamp:
400 xdr.Write(Convert.ToDateTime(i.Current, CultureInfo.CurrentCulture.DateTimeFormat));
404 throw new NotSupportedException("Unknown data type");
408 return xdr.ToArray();
411 private byte[] GenerateSDL(ArrayDesc desc)
421 dimensions = desc.Dimensions;
425 throw new IscException(IscCodes.isc_invalid_dimension);
428 sdl = new BinaryWriter(new MemoryStream());
430 sdl, 4, IscCodes.isc_sdl_version1,
431 IscCodes.isc_sdl_struct, 1, desc.DataType);
433 switch (desc.DataType)
435 case IscCodes.blr_short:
436 case IscCodes.blr_long:
437 case IscCodes.blr_int64:
438 case IscCodes.blr_quad:
439 this.StuffSdl(sdl, (byte)desc.Scale);
442 case IscCodes.blr_text:
443 case IscCodes.blr_cstring:
444 case IscCodes.blr_varying:
445 this.StuffWord(sdl, desc.Length);
452 this.StuffString(sdl, IscCodes.isc_sdl_relation, desc.RelationName);
453 this.StuffString(sdl, IscCodes.isc_sdl_field, desc.FieldName);
455 if ((desc.Flags & IscCodes.ARRAY_DESC_COLUMN_MAJOR) == IscCodes.ARRAY_DESC_COLUMN_MAJOR)
457 from = dimensions - 1;
468 for (n = from; n != to; n += increment)
470 tail = desc.Bounds[n];
471 if (tail.LowerBound == 1)
473 this.Stuff(sdl, 2, IscCodes.isc_sdl_do1, n);
477 this.Stuff(sdl, 2, IscCodes.isc_sdl_do2, n);
479 this.StuffLiteral(sdl, tail.LowerBound);
482 this.StuffLiteral(sdl, tail.UpperBound);
486 sdl, 5, IscCodes.isc_sdl_element,
487 1, IscCodes.isc_sdl_scalar, 0, dimensions);
489 for (n = 0; n < dimensions; n++)
491 this.Stuff(sdl, 2, IscCodes.isc_sdl_variable, n);
494 this.StuffSdl(sdl, IscCodes.isc_sdl_eoc);
496 return ((MemoryStream)sdl.BaseStream).ToArray();
499 private void Stuff(BinaryWriter sdl, short count, params object[] args)
501 for (int i = 0; i < count; i++)
503 sdl.Write(Convert.ToByte(args[i], CultureInfo.InvariantCulture));
507 private void Stuff(BinaryWriter sdl, byte[] args)
512 private void StuffSdl(BinaryWriter sdl, byte sdl_byte)
514 this.Stuff(sdl, 1, sdl_byte);
517 private void StuffWord(BinaryWriter sdl, short word)
519 this.Stuff(sdl, BitConverter.GetBytes(word));
522 private void StuffLong(BinaryWriter sdl, int word)
524 this.Stuff(sdl, BitConverter.GetBytes(word));
527 private void StuffLiteral(BinaryWriter sdl, int literal)
529 if (literal >= -128 && literal <= 127)
531 this.Stuff(sdl, 2, IscCodes.isc_sdl_tiny_integer, literal);
536 if (literal >= -32768 && literal <= 32767)
538 this.StuffSdl(sdl, IscCodes.isc_sdl_short_integer);
539 this.StuffWord(sdl, (short)literal);
544 this.StuffSdl(sdl, IscCodes.isc_sdl_long_integer);
545 this.StuffLong(sdl, literal);
548 private void StuffString(BinaryWriter sdl, int constant, string value)
550 this.StuffSdl(sdl, (byte)constant);
551 this.StuffSdl(sdl, (byte)value.Length);
553 for (int i = 0; i < value.Length; i++)
555 this.StuffSdl(sdl, (byte)value[i]);