New test.
[mono.git] / mcs / class / FirebirdSql.Data.Firebird / FirebirdSql.Data.Common / ArrayBase.cs
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
9  *
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.
14  * 
15  *  Copyright (c) 2002, 2005 Carlos Guzman Alvarez
16  *  All Rights Reserved.
17  */
18
19 using System;
20 using System.Text;
21 using System.Globalization;
22
23 namespace FirebirdSql.Data.Common
24 {
25         internal abstract class ArrayBase
26         {
27                 #region Fields
28
29                 private ArrayDesc       descriptor;
30                 private string          tableName;
31                 private string          fieldName;
32                 private string          rdbFieldName;
33
34                 #endregion
35
36                 #region Properties
37
38                 public ArrayDesc Descriptor
39                 {
40                         get { return this.descriptor; }
41                 }
42
43                 #endregion
44
45                 #region Abstract Properties
46
47                 public abstract long Handle
48                 {
49                         get;
50                         set;
51                 }
52
53                 public abstract IDatabase DB
54                 {
55                         get;
56                         set;
57                 }
58
59                 public abstract ITransaction Transaction
60                 {
61                         get;
62                         set;
63                 }
64
65                 #endregion
66
67                 #region Constructors
68
69                 protected ArrayBase(ArrayDesc descriptor)
70                 {
71                         this.tableName  = descriptor.RelationName;
72                         this.fieldName  = descriptor.FieldName;
73                         this.descriptor = descriptor;
74                 }
75
76                 protected ArrayBase(string tableName, string fieldName)
77                 {
78                         this.tableName          = tableName;
79                         this.fieldName          = fieldName;
80                         this.rdbFieldName       = String.Empty;
81                 }
82
83                 #endregion
84
85                 #region Abstract Methods
86
87                 public abstract byte[] GetSlice(int slice_length);
88                 public abstract void PutSlice(System.Array source_array, int slice_length);
89
90                 #endregion
91
92                 #region Protected Abstract Methods
93
94                 protected abstract System.Array DecodeSlice(byte[] slice);
95
96                 #endregion
97
98                 #region Methods
99
100                 public Array Read()
101                 {
102                         byte[] slice = this.GetSlice(this.GetSliceLength(true));
103
104                         return this.DecodeSlice(slice);
105                 }
106
107                 public void Write(System.Array sourceArray)
108                 {
109                         this.SetDesc(sourceArray);
110                         this.PutSlice(sourceArray, this.GetSliceLength(false));
111                 }
112
113                 public void SetDesc(System.Array sourceArray)
114                 {
115                         this.descriptor.Dimensions = (short)sourceArray.Rank;
116
117                         for (int i = 0; i < sourceArray.Rank; i++)
118                         {
119                                 int lb = this.descriptor.Bounds[i].LowerBound;
120                                 int ub = sourceArray.GetLength(i) - 1 + lb;
121
122                                 this.descriptor.Bounds[i].UpperBound = ub;
123                         }
124                 }
125
126                 public void LookupBounds()
127                 {
128                         this.LookupDesc();
129
130                         StatementBase lookup = this.DB.CreateStatement(this.Transaction);
131
132                         lookup.Prepare(this.GetArrayBounds());
133                         lookup.Execute();
134
135                         int i = 0;
136                         this.descriptor.Bounds = new ArrayBound[16];
137                         DbValue[] values;
138
139                         while ((values = lookup.Fetch()) != null)
140                         {
141                                 this.descriptor.Bounds[i].LowerBound = values[0].GetInt32();
142                                 this.descriptor.Bounds[i].UpperBound = values[1].GetInt32();
143
144                                 i++;
145                         }
146
147                         lookup.Release();
148                         lookup = null;
149                 }
150
151                 public void LookupDesc()
152                 {
153                         // Initializa array descriptor information
154                         this.descriptor = new ArrayDesc();
155
156                         // Create statement for retrieve information
157                         StatementBase lookup = this.DB.CreateStatement(this.Transaction);
158
159                         lookup.Prepare(this.GetArrayDesc());
160                         lookup.Execute();
161
162                         DbValue[] values = lookup.Fetch();
163                         if (values != null && values.Length > 0)
164                         {
165                                 this.descriptor.RelationName    = tableName;
166                                 this.descriptor.FieldName               = fieldName;
167                                 this.descriptor.DataType                = values[0].GetByte();
168                                 this.descriptor.Scale                   = values[1].GetInt16();
169                                 this.descriptor.Length                  = values[2].GetInt16();
170                                 this.descriptor.Dimensions              = values[3].GetInt16();
171                                 this.descriptor.Flags                   = 0;
172
173                                 this.rdbFieldName = values[4].GetString().Trim();
174                         }
175                         else
176                         {
177                                 throw new InvalidOperationException();
178                         }
179
180                         lookup.Release();
181                         lookup = null;
182                 }
183
184                 #endregion
185
186                 #region Protected Methods
187
188                 protected int GetSliceLength(bool read)
189                 {
190                         int length = 0;
191                         int elements = 0;
192
193                         for (int i = 0; i < this.descriptor.Dimensions; i++)
194                         {
195                                 ArrayBound bound = this.descriptor.Bounds[i];
196
197                                 elements += (bound.UpperBound - bound.LowerBound) + 1;
198                         }
199
200                         length = elements * this.descriptor.Length;
201
202                         switch (this.descriptor.DataType)
203                         {
204                                 case IscCodes.blr_varying:
205                                 case IscCodes.blr_varying2:
206                                         length += elements * 2;
207                                         break;
208                         }
209
210                         return length;
211                 }
212
213                 protected Type GetSystemType()
214                 {
215                         Type systemType;
216
217                         switch (this.descriptor.DataType)
218                         {
219                                 case IscCodes.blr_text:
220                                 case IscCodes.blr_text2:
221                                 case IscCodes.blr_cstring:
222                                 case IscCodes.blr_cstring2:
223                                         // Char
224                                         systemType = typeof(System.String);
225                                         break;
226
227                                 case IscCodes.blr_varying:
228                                 case IscCodes.blr_varying2:
229                                         // VarChar
230                                         systemType = typeof(System.String);
231                                         break;
232
233                                 case IscCodes.blr_short:
234                                         // Short/Smallint
235                                         if (this.descriptor.Scale < 0)
236                                         {
237                                                 systemType = typeof(System.Decimal);
238                                         }
239                                         else
240                                         {
241                                                 systemType = typeof(System.Int16);
242                                         }
243                                         break;
244
245                                 case IscCodes.blr_long:
246                                         // Integer
247                                         if (this.descriptor.Scale < 0)
248                                         {
249                                                 systemType = typeof(System.Decimal);
250                                         }
251                                         else
252                                         {
253                                                 systemType = typeof(System.Int32);
254                                         }
255                                         break;
256
257                                 case IscCodes.blr_float:
258                                         // Float
259                                         systemType = typeof(System.Single);
260                                         break;
261
262                                 case IscCodes.blr_double:
263                                 case IscCodes.blr_d_float:
264                                         // Double
265                                         systemType = typeof(System.Double);
266                                         break;
267
268                                 case IscCodes.blr_quad:
269                                 case IscCodes.blr_int64:
270                                         // Long/Quad
271                                         if (this.descriptor.Scale < 0)
272                                         {
273                                                 systemType = typeof(System.Decimal);
274                                         }
275                                         else
276                                         {
277                                                 systemType = typeof(System.Int64);
278                                         }
279                                         break;
280
281                                 case IscCodes.blr_timestamp:
282                                         // Timestamp
283                                         systemType = typeof(System.DateTime);
284                                         break;
285
286                                 case IscCodes.blr_sql_time:
287                                         // Time
288                                         systemType = typeof(System.DateTime);
289                                         break;
290
291                                 case IscCodes.blr_sql_date:
292                                         // Date
293                                         systemType = typeof(System.DateTime);
294                                         break;
295
296                                 default:
297                                         throw new NotSupportedException("Unknown data type");
298                         }
299
300                         return systemType;
301                 }
302
303                 #endregion
304
305                 #region Private Methods
306
307                 private string GetArrayDesc()
308                 {
309                         StringBuilder sql = new StringBuilder();
310
311                         sql.Append(
312                                 "SELECT Y.RDB$FIELD_TYPE, Y.RDB$FIELD_SCALE, Y.RDB$FIELD_LENGTH, Y.RDB$DIMENSIONS, X.RDB$FIELD_SOURCE " +
313                                 "FROM RDB$RELATION_FIELDS X, RDB$FIELDS Y " +
314                                 "WHERE X.RDB$FIELD_SOURCE = Y.RDB$FIELD_NAME ");
315
316                         if (this.tableName != null && this.tableName.Length != 0)
317                         {
318                                 sql.AppendFormat(
319                                         CultureInfo.CurrentCulture, " AND X.RDB$RELATION_NAME = '{0}'", tableName);
320                         }
321
322                         if (this.fieldName != null && this.fieldName.Length != 0)
323                         {
324                                 sql.AppendFormat(
325                                         CultureInfo.CurrentCulture, " AND X.RDB$FIELD_NAME = '{0}'", fieldName);
326                         }
327
328                         return sql.ToString();
329                 }
330
331                 private string GetArrayBounds()
332                 {
333                         StringBuilder sql = new StringBuilder();
334
335                         sql.Append("SELECT X.RDB$LOWER_BOUND, X.RDB$UPPER_BOUND FROM RDB$FIELD_DIMENSIONS X ");
336
337                         if (this.fieldName != null && this.fieldName.Length != 0)
338                         {
339                                 sql.AppendFormat(
340                                         CultureInfo.CurrentCulture, "WHERE X.RDB$FIELD_NAME = '{0}'", rdbFieldName);
341                         }
342
343                         sql.Append(" ORDER BY X.RDB$DIMENSION");
344
345                         return sql.ToString();
346                 }
347
348                 #endregion
349         }
350 }