]> git.stg.codes - stg.git/blob - stglibs/ibpp.lib/array.cpp
Check NULL value pairs.
[stg.git] / stglibs / ibpp.lib / array.cpp
1 ///////////////////////////////////////////////////////////////////////////////\r
2 //\r
3 //      File    : $Id: array.cpp,v 1.2 2009/03/19 20:00:28 faust Exp $\r
4 //      Subject : IBPP, Array class implementation\r
5 //\r
6 ///////////////////////////////////////////////////////////////////////////////\r
7 //\r
8 //      (C) Copyright 2000-2006 T.I.P. Group S.A. and the IBPP Team (www.ibpp.org)\r
9 //\r
10 //      The contents of this file are subject to the IBPP License (the "License");\r
11 //      you may not use this file except in compliance with the License.  You may\r
12 //      obtain a copy of the License at http://www.ibpp.org or in the 'license.txt'\r
13 //      file which must have been distributed along with this file.\r
14 //\r
15 //      This software, distributed under the License, is distributed on an "AS IS"\r
16 //      basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the\r
17 //      License for the specific language governing rights and limitations\r
18 //      under the License.\r
19 //\r
20 ///////////////////////////////////////////////////////////////////////////////\r
21 //\r
22 //      COMMENTS\r
23 //      * Tabulations should be set every four characters when editing this file.\r
24 //\r
25 ///////////////////////////////////////////////////////////////////////////////\r
26 \r
27 #ifdef _MSC_VER\r
28 #pragma warning(disable: 4786 4996)\r
29 #ifndef _DEBUG\r
30 #pragma warning(disable: 4702)\r
31 #endif\r
32 #endif\r
33 \r
34 #include "_ibpp.h"\r
35 \r
36 #ifdef HAS_HDRSTOP\r
37 #pragma hdrstop\r
38 #endif\r
39 \r
40 #include <math.h>\r
41 #include <cstring>\r
42 \r
43 using namespace ibpp_internals;\r
44 \r
45 //      (((((((( OBJECT INTERFACE IMPLEMENTATION ))))))))\r
46 \r
47 void ArrayImpl::Describe(const std::string& table, const std::string& column)\r
48 {\r
49         //if (mIdAssigned)\r
50         //      throw LogicExceptionImpl("Array::Lookup", _("Array already in use."));\r
51         if (mDatabase == 0)\r
52                 throw LogicExceptionImpl("Array::Lookup", _("No Database is attached."));\r
53         if (mTransaction == 0)\r
54                 throw LogicExceptionImpl("Array::Lookup", _("No Transaction is attached."));\r
55 \r
56         ResetId();      // Re-use this array object if was previously assigned\r
57 \r
58         IBS status;\r
59         (*gds.Call()->m_array_lookup_bounds)(status.Self(), mDatabase->GetHandlePtr(),\r
60                 mTransaction->GetHandlePtr(), const_cast<char*>(table.c_str()),\r
61                         const_cast<char*>(column.c_str()), &mDesc);\r
62         if (status.Errors())\r
63                 throw SQLExceptionImpl(status, "Array::Lookup",\r
64                         _("isc_array_lookup_bounds failed."));\r
65 \r
66         AllocArrayBuffer();\r
67 \r
68         mDescribed = true;\r
69 }\r
70 \r
71 void ArrayImpl::SetBounds(int dim, int low, int high)\r
72 {\r
73         if (! mDescribed)\r
74                 throw LogicExceptionImpl("Array::SetBounds", _("Array description not set."));\r
75         if (mDatabase == 0)\r
76                 throw LogicExceptionImpl("Array::SetBounds", _("No Database is attached."));\r
77         if (mTransaction == 0)\r
78                 throw LogicExceptionImpl("Array::SetBounds", _("No Transaction is attached."));\r
79         if (dim < 0 || dim > mDesc.array_desc_dimensions-1)\r
80                 throw LogicExceptionImpl("Array::SetBounds", _("Invalid dimension."));\r
81         if (low > high ||\r
82                 low < mDesc.array_desc_bounds[dim].array_bound_lower ||\r
83                 low > mDesc.array_desc_bounds[dim].array_bound_upper ||\r
84                 high > mDesc.array_desc_bounds[dim].array_bound_upper ||\r
85                 high < mDesc.array_desc_bounds[dim].array_bound_lower)\r
86                 throw LogicExceptionImpl("Array::SetBounds",\r
87                         _("Invalid bounds. You can only narrow the bounds."));\r
88 \r
89         mDesc.array_desc_bounds[dim].array_bound_lower = short(low);\r
90         mDesc.array_desc_bounds[dim].array_bound_upper = short(high);\r
91 \r
92         AllocArrayBuffer();\r
93 }\r
94 \r
95 IBPP::SDT ArrayImpl::ElementType()\r
96 {\r
97         if (! mDescribed)\r
98                 throw LogicExceptionImpl("Array::ElementType",\r
99                         _("Array description not set."));\r
100 \r
101         IBPP::SDT value;\r
102         switch (mDesc.array_desc_dtype)\r
103         {\r
104                 case blr_text :                 value = IBPP::sdString;         break;\r
105                 case blr_varying :              value = IBPP::sdString;         break;\r
106                 case blr_cstring :              value = IBPP::sdString;         break;\r
107                 case blr_short :                value = IBPP::sdSmallint;       break;\r
108                 case blr_long :                 value = IBPP::sdInteger;        break;\r
109                 case blr_int64 :                value = IBPP::sdLargeint;       break;\r
110                 case blr_float :                value = IBPP::sdFloat;          break;\r
111                 case blr_double :               value = IBPP::sdDouble;         break;\r
112                 case blr_timestamp :    value = IBPP::sdTimestamp;      break;\r
113                 case blr_sql_date :             value = IBPP::sdDate;           break;\r
114                 case blr_sql_time :             value = IBPP::sdTime;           break;\r
115                 default : throw LogicExceptionImpl("Array::ElementType",\r
116                                                 _("Found an unknown sqltype !"));\r
117         }\r
118 \r
119         return value;\r
120 }\r
121 \r
122 int ArrayImpl::ElementSize()\r
123 {\r
124         if (! mDescribed)\r
125                 throw LogicExceptionImpl("Array::ElementSize",\r
126                         _("Array description not set."));\r
127 \r
128         return mDesc.array_desc_length;\r
129 }\r
130 \r
131 int ArrayImpl::ElementScale()\r
132 {\r
133         if (! mDescribed)\r
134                 throw LogicExceptionImpl("Array::ElementScale",\r
135                         _("Array description not set."));\r
136 \r
137         return mDesc.array_desc_scale;\r
138 }\r
139 \r
140 int ArrayImpl::Dimensions()\r
141 {\r
142         if (! mDescribed)\r
143                 throw LogicExceptionImpl("Array::Dimensions",\r
144                         _("Array description not set."));\r
145 \r
146         return mDesc.array_desc_dimensions;\r
147 }\r
148 \r
149 void ArrayImpl::Bounds(int dim, int* low, int* high)\r
150 {\r
151         if (! mDescribed)\r
152                 throw LogicExceptionImpl("Array::Bounds", _("Array description not set."));\r
153         if (dim < 0 || dim > mDesc.array_desc_dimensions-1)\r
154                 throw LogicExceptionImpl("Array::Bounds", _("Invalid dimension."));\r
155         if (low == 0 || high == 0)\r
156                 throw LogicExceptionImpl("Array::Bounds", _("Null reference detected."));\r
157 \r
158         *low =  mDesc.array_desc_bounds[dim].array_bound_lower;\r
159         *high = mDesc.array_desc_bounds[dim].array_bound_upper;\r
160 }\r
161 \r
162 /*\r
163 \r
164 COMMENTS\r
165 \r
166 1)\r
167 For an array column of type CHAR(X), the internal type returned or expected is blr_text.\r
168 In such case, the byte array received or submitted to get/put_slice is formatted in\r
169 elements of X bytes, which correspond to what is reported in array_desc_length.\r
170 The elements are not '\0' terminated but are right-padded with spaces ' '.\r
171 \r
172 2)\r
173 For an array column of type VARCHAR(X), the internal type is blr_varying.\r
174 The underlying format is rather curious and different than what is used in XSQLDA.\r
175 The element size is reported in array_desc_length as X.\r
176 Yet each element of the byte array is expected to be of size X+2 (just as if we were\r
177 to stuff a short in the first 2 bytes to store the length (as is done with XSQLDA).\r
178 No. The string of X characters maximum has to be stored in the chunks of X+2 bytes as\r
179 a zero-terminated c-string. Note that the buffer is indeed one byte too large.\r
180 Internally, the API probably convert in-place in these chunks the zero-terminated string\r
181 to a variable-size string with a short in front and the string data non zero-terminated\r
182 behind.\r
183 \r
184 3)\r
185 With Interbase 6.0 and Firebird 1.0 (initial april 2002 release), the int64 support is\r
186 broken regarding the arrays. It is not possible to work on arrays using a datatype stored\r
187 in an int64, as for instance any NUMERIC(x,0) where x is equal or greater than 10. That's\r
188 a bug in the engine, not in IBPP, which has been fixed in june 2002. Engines compiled from\r
189 the current Firebird CVS code as of july 2002 are okay. As will be the 1.01 Firebird version.\r
190 We have no idea if this is fixed or not in Interbase 6.5 though.\r
191 \r
192 */\r
193 \r
194 void ArrayImpl::ReadTo(IBPP::ADT adtype, void* data, int datacount)\r
195 {\r
196         if (! mIdAssigned)\r
197                 throw LogicExceptionImpl("Array::ReadTo", _("Array Id not read from column."));\r
198         if (! mDescribed)\r
199                 throw LogicExceptionImpl("Array::ReadTo", _("Array description not set."));\r
200         if (mDatabase == 0)\r
201                 throw LogicExceptionImpl("Array::ReadTo", _("No Database is attached."));\r
202         if (mTransaction == 0)\r
203                 throw LogicExceptionImpl("Array::ReadTo", _("No Transaction is attached."));\r
204         if (datacount != mElemCount)\r
205                 throw LogicExceptionImpl("Array::ReadTo", _("Wrong count of array elements"));\r
206 \r
207         IBS status;\r
208         ISC_LONG lenbuf = mBufferSize;\r
209         (*gds.Call()->m_array_get_slice)(status.Self(), mDatabase->GetHandlePtr(),\r
210                 mTransaction->GetHandlePtr(), &mId, &mDesc, mBuffer, &lenbuf);\r
211         if (status.Errors())\r
212                 throw SQLExceptionImpl(status, "Array::ReadTo", _("isc_array_get_slice failed."));\r
213         if (lenbuf != mBufferSize)\r
214                 throw SQLExceptionImpl(status, "Array::ReadTo", _("Internal buffer size discrepancy."));\r
215 \r
216         // Now, convert the types and copy values to the user array...\r
217         int len;\r
218         char* src = (char*)mBuffer;\r
219         char* dst = (char*)data;\r
220 \r
221         switch (mDesc.array_desc_dtype)\r
222         {\r
223                 case blr_text :\r
224                         if (adtype == IBPP::adString)\r
225                         {\r
226                                 for (int i = 0; i < mElemCount; i++)\r
227                                 {\r
228                                         strncpy(dst, src, mElemSize);\r
229                                         dst[mElemSize] = '\0';\r
230                                         src += mElemSize;\r
231                                         dst += (mElemSize + 1);\r
232                                 }\r
233                         }\r
234                         else if (adtype == IBPP::adBool)\r
235                         {\r
236                                 for (int i = 0; i < mElemCount; i++)\r
237                                 {\r
238                                         if (*src == 't' || *src == 'T' || *src == 'y' || *src == 'Y' || *src == '1')\r
239                                                 *(bool*)dst = true;\r
240                                         else *(bool*)dst = false;\r
241                                         src += mElemSize;\r
242                                         dst += sizeof(bool);\r
243                                 }\r
244                         }\r
245                         else throw LogicExceptionImpl("Array::ReadTo", _("Incompatible types."));\r
246                         break;\r
247 \r
248                 case blr_varying :\r
249                         if (adtype == IBPP::adString)\r
250                         {\r
251                                 for (int i = 0; i < mElemCount; i++)\r
252                                 {\r
253                                         len = (int)strlen(src);\r
254                                         if (len > mElemSize-2) len = mElemSize-2;\r
255                                         strncpy(dst, src, len);\r
256                                         dst[len] = '\0';\r
257                                         src += mElemSize;\r
258                                         dst += (mElemSize - 2 + 1);\r
259                                 }\r
260                         }\r
261                         else if (adtype == IBPP::adBool)\r
262                         {\r
263                                 for (int i = 0; i < mElemCount; i++)\r
264                                 {\r
265                                         if (*src == 't' || *src == 'T' || *src == 'y' || *src == 'Y' || *src == '1')\r
266                                                 *(bool*)dst = true;\r
267                                         else *(bool*)dst = false;\r
268                                         src += mElemSize;\r
269                                         dst += sizeof(bool);\r
270                                 }\r
271                         }\r
272                         else throw LogicExceptionImpl("Array::ReadTo", _("Incompatible types."));\r
273                         break;\r
274 \r
275                 case blr_short :\r
276                         if (adtype == IBPP::adBool)\r
277                         {\r
278                                 for (int i = 0; i < mElemCount; i++)\r
279                                 {\r
280                                         *(bool*)dst = (*(short*)src != 0) ? true : false;\r
281                                         src += mElemSize;\r
282                                         dst += sizeof(bool);\r
283                                 }\r
284                         }\r
285                         else if (adtype == IBPP::adInt16)\r
286                         {\r
287                                 for (int i = 0; i < mElemCount; i++)\r
288                                 {\r
289                                         *(short*)dst = *(short*)src;\r
290                                         src += mElemSize;\r
291                                         dst += sizeof(short);\r
292                                 }\r
293                         }\r
294                         else if (adtype == IBPP::adInt32)\r
295                         {\r
296                                 for (int i = 0; i < mElemCount; i++)\r
297                                 {\r
298                                         *(int*)dst = (int)*(short*)src;\r
299                                         src += mElemSize;\r
300                                         dst += sizeof(int);\r
301                                 }\r
302                         }\r
303                         else if (adtype == IBPP::adInt64)\r
304                         {\r
305                                 for (int i = 0; i < mElemCount; i++)\r
306                                 {\r
307                                         *(int64_t*)dst = (int64_t)*(short*)src;\r
308                                         src += mElemSize;\r
309                                         dst += sizeof(int64_t);\r
310                                 }\r
311                         }\r
312                         else if (adtype == IBPP::adFloat)\r
313                         {\r
314                                 // This SQL_SHORT is a NUMERIC(x,y), scale it !\r
315                                 double divisor = consts::dscales[-mDesc.array_desc_scale];\r
316                                 for (int i = 0; i < mElemCount; i++)\r
317                                 {\r
318                                         *(float*)dst = (float)(*(short*)src / divisor);\r
319                                         src += mElemSize;\r
320                                         dst += sizeof(float);\r
321                                 }\r
322                         }\r
323                         else if (adtype == IBPP::adDouble)\r
324                         {\r
325                                 // This SQL_SHORT is a NUMERIC(x,y), scale it !\r
326                                 double divisor = consts::dscales[-mDesc.array_desc_scale];\r
327                                 for (int i = 0; i < mElemCount; i++)\r
328                                 {\r
329                                         *(double*)dst = (double)(*(short*)src / divisor);\r
330                                         src += mElemSize;\r
331                                         dst += sizeof(double);\r
332                                 }\r
333                         }\r
334                         else throw LogicExceptionImpl("Array::ReadTo", _("Incompatible types."));\r
335                         break;\r
336 \r
337                 case blr_long :\r
338                         if (adtype == IBPP::adBool)\r
339                         {\r
340                                 for (int i = 0; i < mElemCount; i++)\r
341                                 {\r
342                                         *(bool*)dst = (*(long*)src != 0) ? true : false;\r
343                                         src += mElemSize;\r
344                                         dst += sizeof(bool);\r
345                                 }\r
346                         }\r
347                         else if (adtype == IBPP::adInt16)\r
348                         {\r
349                                 for (int i = 0; i < mElemCount; i++)\r
350                                 {\r
351                                         if (*(long*)src < consts::min16 || *(long*)src > consts::max16)\r
352                                                 throw LogicExceptionImpl("Array::ReadTo",\r
353                                                         _("Out of range numeric conversion !"));\r
354                                         *(short*)dst = short(*(long*)src);\r
355                                         src += mElemSize;\r
356                                         dst += sizeof(short);\r
357                                 }\r
358                         }\r
359                         else if (adtype == IBPP::adInt32)\r
360                         {\r
361                                 for (int i = 0; i < mElemCount; i++)\r
362                                 {\r
363                                         *(long*)dst = *(long*)src;\r
364                                         src += mElemSize;\r
365                                         dst += sizeof(long);\r
366                                 }\r
367                         }\r
368                         else if (adtype == IBPP::adInt64)\r
369                         {\r
370                                 for (int i = 0; i < mElemCount; i++)\r
371                                 {\r
372                                         *(int64_t*)dst = (int64_t)*(long*)src;\r
373                                         src += mElemSize;\r
374                                         dst += sizeof(int64_t);\r
375                                 }\r
376                         }\r
377                         else if (adtype == IBPP::adFloat)\r
378                         {\r
379                                 // This SQL_SHORT is a NUMERIC(x,y), scale it !\r
380                                 double divisor = consts::dscales[-mDesc.array_desc_scale];\r
381                                 for (int i = 0; i < mElemCount; i++)\r
382                                 {\r
383                                         *(float*)dst = (float)(*(long*)src / divisor);\r
384                                         src += mElemSize;\r
385                                         dst += sizeof(float);\r
386                                 }\r
387                         }\r
388                         else if (adtype == IBPP::adDouble)\r
389                         {\r
390                                 // This SQL_SHORT is a NUMERIC(x,y), scale it !\r
391                                 double divisor = consts::dscales[-mDesc.array_desc_scale];\r
392                                 for (int i = 0; i < mElemCount; i++)\r
393                                 {\r
394                                         *(double*)dst = (double)(*(long*)src / divisor);\r
395                                         src += mElemSize;\r
396                                         dst += sizeof(double);\r
397                                 }\r
398                         }\r
399                         else throw LogicExceptionImpl("Array::ReadTo", _("Incompatible types."));\r
400                         break;\r
401 \r
402                 case blr_int64 :\r
403                         if (adtype == IBPP::adBool)\r
404                         {\r
405                                 for (int i = 0; i < mElemCount; i++)\r
406                                 {\r
407                                         *(bool*)dst = (*(int64_t*)src != 0) ? true : false;\r
408                                         src += mElemSize;\r
409                                         dst += sizeof(bool);\r
410                                 }\r
411                         }\r
412                         else if (adtype == IBPP::adInt16)\r
413                         {\r
414                                 for (int i = 0; i < mElemCount; i++)\r
415                                 {\r
416                                         if (*(int64_t*)src < consts::min16 || *(int64_t*)src > consts::max16)\r
417                                                 throw LogicExceptionImpl("Array::ReadTo",\r
418                                                         _("Out of range numeric conversion !"));\r
419                                         *(short*)dst = short(*(int64_t*)src);\r
420                                         src += mElemSize;\r
421                                         dst += sizeof(short);\r
422                                 }\r
423                         }\r
424                         else if (adtype == IBPP::adInt32)\r
425                         {\r
426                                 for (int i = 0; i < mElemCount; i++)\r
427                                 {\r
428                                         if (*(int64_t*)src < consts::min32 || *(int64_t*)src > consts::max32)\r
429                                                 throw LogicExceptionImpl("Array::ReadTo",\r
430                                                         _("Out of range numeric conversion !"));\r
431                                         *(long*)dst = (long)*(int64_t*)src;\r
432                                         src += mElemSize;\r
433                                         dst += sizeof(long);\r
434                                 }\r
435                         }\r
436                         else if (adtype == IBPP::adInt64)\r
437                         {\r
438                                 for (int i = 0; i < mElemCount; i++)\r
439                                 {\r
440                                         *(int64_t*)dst = *(int64_t*)src;\r
441                                         src += mElemSize;\r
442                                         dst += sizeof(int64_t);\r
443                                 }\r
444                         }\r
445                         else if (adtype == IBPP::adFloat)\r
446                         {\r
447                                 // This SQL_SHORT is a NUMERIC(x,y), scale it !\r
448                                 double divisor = consts::dscales[-mDesc.array_desc_scale];\r
449                                 for (int i = 0; i < mElemCount; i++)\r
450                                 {\r
451                                         *(float*)dst = (float)(*(int64_t*)src / divisor);\r
452                                         src += mElemSize;\r
453                                         dst += sizeof(float);\r
454                                 }\r
455                         }\r
456                         else if (adtype == IBPP::adDouble)\r
457                         {\r
458                                 // This SQL_SHORT is a NUMERIC(x,y), scale it !\r
459                                 double divisor = consts::dscales[-mDesc.array_desc_scale];\r
460                                 for (int i = 0; i < mElemCount; i++)\r
461                                 {\r
462                                         *(double*)dst = (double)(*(int64_t*)src / divisor);\r
463                                         src += mElemSize;\r
464                                         dst += sizeof(double);\r
465                                 }\r
466                         }\r
467                         else throw LogicExceptionImpl("Array::ReadTo", _("Incompatible types."));\r
468                         break;\r
469 \r
470                 case blr_float :\r
471                         if (adtype != IBPP::adFloat || mDesc.array_desc_scale != 0)\r
472                                 throw LogicExceptionImpl("Array::ReadTo", _("Incompatible types."));\r
473                         for (int i = 0; i < mElemCount; i++)\r
474                         {\r
475                                 *(float*)dst = *(float*)src;\r
476                                 src += mElemSize;\r
477                                 dst += sizeof(float);\r
478                         }\r
479                         break;\r
480 \r
481                 case blr_double :\r
482                         if (adtype != IBPP::adDouble) throw LogicExceptionImpl("Array::ReadTo",\r
483                                                                                 _("Incompatible types."));\r
484                         if (mDesc.array_desc_scale != 0)\r
485                         {\r
486                                 // Round to scale of NUMERIC(x,y)\r
487                                 double divisor = consts::dscales[-mDesc.array_desc_scale];\r
488                                 for (int i = 0; i < mElemCount; i++)\r
489                                 {\r
490                                         *(double*)dst = (double)(*(double*)src / divisor);\r
491                                         src += mElemSize;\r
492                                         dst += sizeof(double);\r
493                                 }\r
494                         }\r
495                         else\r
496                         {\r
497                                 for (int i = 0; i < mElemCount; i++)\r
498                                 {\r
499                                         *(double*)dst = *(double*)src;\r
500                                         src += mElemSize;\r
501                                         dst += sizeof(double);\r
502                                 }\r
503                         }\r
504                         break;\r
505 \r
506                 case blr_timestamp :\r
507                         if (adtype != IBPP::adTimestamp) throw LogicExceptionImpl("Array::ReadTo",\r
508                                                                                                 _("Incompatible types."));\r
509                         for (int i = 0; i < mElemCount; i++)\r
510                         {\r
511                                 decodeTimestamp(*(IBPP::Timestamp*)dst, *(ISC_TIMESTAMP*)src);\r
512                                 src += mElemSize;\r
513                                 dst += sizeof(IBPP::Timestamp);\r
514                         }\r
515                         break;\r
516 \r
517                 case blr_sql_date :\r
518                         if (adtype != IBPP::adDate) throw LogicExceptionImpl("Array::ReadTo",\r
519                                                                                                 _("Incompatible types."));\r
520                         for (int i = 0; i < mElemCount; i++)\r
521                         {\r
522                                 decodeDate(*(IBPP::Date*)dst, *(ISC_DATE*)src);\r
523                                 src += mElemSize;\r
524                                 dst += sizeof(IBPP::Date);\r
525                         }\r
526                         break;\r
527 \r
528                 case blr_sql_time :\r
529                         if (adtype != IBPP::adTime) throw LogicExceptionImpl("Array::ReadTo",\r
530                                                                                                 _("Incompatible types."));\r
531                         for (int i = 0; i < mElemCount; i++)\r
532                         {\r
533                                 decodeTime(*(IBPP::Time*)dst, *(ISC_TIME*)src);\r
534                                 src += mElemSize;\r
535                                 dst += sizeof(IBPP::Time);\r
536                         }\r
537                         break;\r
538 \r
539                 default :\r
540                         throw LogicExceptionImpl("Array::ReadTo", _("Unknown sql type."));\r
541         }\r
542 }\r
543 \r
544 void ArrayImpl::WriteFrom(IBPP::ADT adtype, const void* data, int datacount)\r
545 {\r
546         if (! mDescribed)\r
547                 throw LogicExceptionImpl("Array::WriteFrom", _("Array description not set."));\r
548         if (mDatabase == 0)\r
549                 throw LogicExceptionImpl("Array::WriteFrom", _("No Database is attached."));\r
550         if (mTransaction == 0)\r
551                 throw LogicExceptionImpl("Array::WriteFrom", _("No Transaction is attached."));\r
552         if (datacount != mElemCount)\r
553                 throw LogicExceptionImpl("Array::ReadTo", _("Wrong count of array elements"));\r
554 \r
555         // Read user data and convert types to the mBuffer\r
556         int len;\r
557         char* src = (char*)data;\r
558         char* dst = (char*)mBuffer;\r
559 \r
560         switch (mDesc.array_desc_dtype)\r
561         {\r
562                 case blr_text :\r
563                         if (adtype == IBPP::adString)\r
564                         {\r
565                                 for (int i = 0; i < mElemCount; i++)\r
566                                 {\r
567                                         len = (int)strlen(src);\r
568                                         if (len > mElemSize) len = mElemSize;\r
569                                         strncpy(dst, src, len);\r
570                                         while (len < mElemSize) dst[len++] = ' ';\r
571                                         src += (mElemSize + 1);\r
572                                         dst += mElemSize;\r
573                                 }\r
574                         }\r
575                         else if (adtype == IBPP::adBool)\r
576                         {\r
577                                 for (int i = 0; i < mElemCount; i++)\r
578                                 {\r
579                                         *dst = *(bool*)src ? 'T' : 'F';\r
580                                         len = 1;\r
581                                         while (len < mElemSize) dst[len++] = ' ';\r
582                                         src += sizeof(bool);\r
583                                         dst += mElemSize;\r
584                                 }\r
585                         }\r
586                         else throw LogicExceptionImpl("Array::WriteFrom", _("Incompatible types."));\r
587                         break;\r
588 \r
589                 case blr_varying :\r
590                         if (adtype == IBPP::adString)\r
591                         {\r
592                                 for (int i = 0; i < mElemCount; i++)\r
593                                 {\r
594                                         len = (int)strlen(src);\r
595                                         if (len > mElemSize-2) len = mElemSize-2;\r
596                                         strncpy(dst, src, len);\r
597                                         dst[len] = '\0';\r
598                                         src += (mElemSize - 2 + 1);\r
599                                         dst += mElemSize;\r
600                                 }\r
601                         }\r
602                         else if (adtype == IBPP::adBool)\r
603                         {\r
604                                 for (int i = 0; i < mElemCount; i++)\r
605                                 {\r
606                                         *(short*)dst = (short)1;\r
607                                         dst[2] = *(bool*)src ? 'T' : 'F';\r
608                                         src += sizeof(bool);\r
609                                         dst += mElemSize;\r
610                                 }\r
611                         }\r
612                         else throw LogicExceptionImpl("Array::WriteFrom", _("Incompatible types."));\r
613                         break;\r
614 \r
615                 case blr_short :\r
616                         if (adtype == IBPP::adBool)\r
617                         {\r
618                                 for (int i = 0; i < mElemCount; i++)\r
619                                 {\r
620                                         *(short*)dst = short(*(bool*)src ? 1 : 0);\r
621                                         src += sizeof(bool);\r
622                                         dst += mElemSize;\r
623                                 }\r
624                         }\r
625                         else if (adtype == IBPP::adInt16)\r
626                         {\r
627                                 for (int i = 0; i < mElemCount; i++)\r
628                                 {\r
629                                         *(short*)dst = *(short*)src;\r
630                                         src += sizeof(short);\r
631                                         dst += mElemSize;\r
632                                 }\r
633                         }\r
634                         else if (adtype == IBPP::adInt32)\r
635                         {\r
636                                 for (int i = 0; i < mElemCount; i++)\r
637                                 {\r
638                                         if (*(long*)src < consts::min16 || *(long*)src > consts::max16)\r
639                                                 throw LogicExceptionImpl("Array::WriteFrom",\r
640                                                         _("Out of range numeric conversion !"));\r
641                                         *(short*)dst = (short)*(int*)src;\r
642                                         src += sizeof(int);\r
643                                         dst += mElemSize;\r
644                                 }\r
645                         }\r
646                         else if (adtype == IBPP::adInt64)\r
647                         {\r
648                                 for (int i = 0; i < mElemCount; i++)\r
649                                 {\r
650                                         if (*(int64_t*)src < consts::min16 || *(int64_t*)src > consts::max16)\r
651                                                 throw LogicExceptionImpl("Array::WriteFrom",\r
652                                                         _("Out of range numeric conversion !"));\r
653                                         *(short*)dst = (short)*(int64_t*)src;\r
654                                         src += sizeof(int64_t);\r
655                                         dst += mElemSize;\r
656                                 }\r
657                         }\r
658                         else if (adtype == IBPP::adFloat)\r
659                         {\r
660                                 // This SQL_SHORT is a NUMERIC(x,y), scale it !\r
661                                 double multiplier = consts::dscales[-mDesc.array_desc_scale];\r
662                                 for (int i = 0; i < mElemCount; i++)\r
663                                 {\r
664                                         *(short*)dst =\r
665                                                 (short)floor(*(float*)src * multiplier + 0.5);\r
666                                         src += sizeof(float);\r
667                                         dst += mElemSize;\r
668                                 }\r
669                         }\r
670                         else if (adtype == IBPP::adDouble)\r
671                         {\r
672                                 // This SQL_SHORT is a NUMERIC(x,y), scale it !\r
673                                 double multiplier = consts::dscales[-mDesc.array_desc_scale];\r
674                                 for (int i = 0; i < mElemCount; i++)\r
675                                 {\r
676                                         *(short*)dst =\r
677                                                 (short)floor(*(double*)src * multiplier + 0.5);\r
678                                         src += sizeof(double);\r
679                                         dst += mElemSize;\r
680                                 }\r
681                         }\r
682                         else throw LogicExceptionImpl("Array::WriteFrom", _("Incompatible types."));\r
683                         break;\r
684 \r
685                 case blr_long :\r
686                         if (adtype == IBPP::adBool)\r
687                         {\r
688                                 for (int i = 0; i < mElemCount; i++)\r
689                                 {\r
690                                         *(long*)dst = *(bool*)src ? 1 : 0;\r
691                                         src += sizeof(bool);\r
692                                         dst += mElemSize;\r
693                                 }\r
694                         }\r
695                         else if (adtype == IBPP::adInt16)\r
696                         {\r
697                                 for (int i = 0; i < mElemCount; i++)\r
698                                 {\r
699                                         *(long*)dst = *(short*)src;\r
700                                         src += sizeof(short);\r
701                                         dst += mElemSize;\r
702                                 }\r
703                         }\r
704                         else if (adtype == IBPP::adInt32)\r
705                         {\r
706                                 for (int i = 0; i < mElemCount; i++)\r
707                                 {\r
708                                         *(long*)dst = *(long*)src;\r
709                                         src += sizeof(long);\r
710                                         dst += mElemSize;\r
711                                 }\r
712                         }\r
713                         else if (adtype == IBPP::adInt64)\r
714                         {\r
715                                 for (int i = 0; i < mElemCount; i++)\r
716                                 {\r
717                                         if (*(int64_t*)src < consts::min32 || *(int64_t*)src > consts::max32)\r
718                                                 throw LogicExceptionImpl("Array::WriteFrom",\r
719                                                         _("Out of range numeric conversion !"));\r
720                                         *(long*)dst = (long)*(int64_t*)src;\r
721                                         src += sizeof(int64_t);\r
722                                         dst += mElemSize;\r
723                                 }\r
724                         }\r
725                         else if (adtype == IBPP::adFloat)\r
726                         {\r
727                                 // This SQL_INT is a NUMERIC(x,y), scale it !\r
728                                 double multiplier = consts::dscales[-mDesc.array_desc_scale];\r
729                                 for (int i = 0; i < mElemCount; i++)\r
730                                 {\r
731                                         *(long*)dst =\r
732                                                 (long)floor(*(float*)src * multiplier + 0.5);\r
733                                         src += sizeof(float);\r
734                                         dst += mElemSize;\r
735                                 }\r
736                         }\r
737                         else if (adtype == IBPP::adDouble)\r
738                         {\r
739                                 // This SQL_INT is a NUMERIC(x,y), scale it !\r
740                                 double multiplier = consts::dscales[-mDesc.array_desc_scale];\r
741                                 for (int i = 0; i < mElemCount; i++)\r
742                                 {\r
743                                         *(long*)dst =\r
744                                                 (long)floor(*(double*)src * multiplier + 0.5);\r
745                                         src += sizeof(double);\r
746                                         dst += mElemSize;\r
747                                 }\r
748                         }\r
749                         else throw LogicExceptionImpl("Array::WriteFrom", _("Incompatible types."));\r
750                         break;\r
751 \r
752                 case blr_int64 :\r
753                         if (adtype == IBPP::adBool)\r
754                         {\r
755                                 for (int i = 0; i < mElemCount; i++)\r
756                                 {\r
757                                         *(int64_t*)dst = *(bool*)src ? 1 : 0;\r
758                                         src += sizeof(bool);\r
759                                         dst += mElemSize;\r
760                                 }\r
761                         }\r
762                         else if (adtype == IBPP::adInt16)\r
763                         {\r
764                                 for (int i = 0; i < mElemCount; i++)\r
765                                 {\r
766                                         *(int64_t*)dst = *(short*)src;\r
767                                         src += sizeof(short);\r
768                                         dst += mElemSize;\r
769                                 }\r
770                         }\r
771                         else if (adtype == IBPP::adInt32)\r
772                         {\r
773                                 for (int i = 0; i < mElemCount; i++)\r
774                                 {\r
775                                         *(int64_t*)dst = *(long*)src;\r
776                                         src += sizeof(long);\r
777                                         dst += mElemSize;\r
778                                 }\r
779                         }\r
780                         else if (adtype == IBPP::adInt64)\r
781                         {\r
782                                 for (int i = 0; i < mElemCount; i++)\r
783                                 {\r
784                                         *(int64_t*)dst = *(int64_t*)src;\r
785                                         src += sizeof(int64_t);\r
786                                         dst += mElemSize;\r
787                                 }\r
788                         }\r
789                         else if (adtype == IBPP::adFloat)\r
790                         {\r
791                                 // This SQL_INT is a NUMERIC(x,y), scale it !\r
792                                 double multiplier = consts::dscales[-mDesc.array_desc_scale];\r
793                                 for (int i = 0; i < mElemCount; i++)\r
794                                 {\r
795                                         *(int64_t*)dst =\r
796                                                 (int64_t)floor(*(float*)src * multiplier + 0.5);\r
797                                         src += sizeof(float);\r
798                                         dst += mElemSize;\r
799                                 }\r
800                         }\r
801                         else if (adtype == IBPP::adDouble)\r
802                         {\r
803                                 // This SQL_INT is a NUMERIC(x,y), scale it !\r
804                                 double multiplier = consts::dscales[-mDesc.array_desc_scale];\r
805                                 for (int i = 0; i < mElemCount; i++)\r
806                                 {\r
807                                         *(int64_t*)dst =\r
808                                                 (int64_t)floor(*(double*)src * multiplier + 0.5);\r
809                                         src += sizeof(double);\r
810                                         dst += mElemSize;\r
811                                 }\r
812                         }\r
813                         else\r
814                                 throw LogicExceptionImpl("Array::WriteFrom",\r
815                                         _("Incompatible types (blr_int64 and ADT %d)."), (int)adtype);\r
816                         break;\r
817 \r
818                 case blr_float :\r
819                         if (adtype != IBPP::adFloat || mDesc.array_desc_scale != 0)\r
820                                 throw LogicExceptionImpl("Array::WriteFrom", _("Incompatible types."));\r
821                         for (int i = 0; i < mElemCount; i++)\r
822                         {\r
823                                 *(float*)dst = *(float*)src;\r
824                                 src += sizeof(float);\r
825                                 dst += mElemSize;\r
826                         }\r
827                         break;\r
828 \r
829                 case blr_double :\r
830                         if (adtype != IBPP::adDouble) throw LogicExceptionImpl("Array::WriteFrom",\r
831                                                                                 _("Incompatible types."));\r
832                         if (mDesc.array_desc_scale != 0)\r
833                         {\r
834                                 // Round to scale of NUMERIC(x,y)\r
835                                 double multiplier = consts::dscales[-mDesc.array_desc_scale];\r
836                                 for (int i = 0; i < mElemCount; i++)\r
837                                 {\r
838                                         *(double*)dst =\r
839                                                 floor(*(double*)src * multiplier + 0.5) / multiplier;\r
840                                         src += sizeof(double);\r
841                                         dst += mElemSize;\r
842                                 }\r
843                         }\r
844                         else\r
845                         {\r
846                                 for (int i = 0; i < mElemCount; i++)\r
847                                 {\r
848                                         *(double*)dst = *(double*)src;\r
849                                         src += sizeof(double);\r
850                                         dst += mElemSize;\r
851                                 }\r
852                         }\r
853                         break;\r
854 \r
855                 case blr_timestamp :\r
856                         if (adtype != IBPP::adTimestamp) throw LogicExceptionImpl("Array::ReadTo",\r
857                                                                                                 _("Incompatible types."));\r
858                         for (int i = 0; i < mElemCount; i++)\r
859                         {\r
860                                 encodeTimestamp(*(ISC_TIMESTAMP*)dst, *(IBPP::Timestamp*)src);\r
861                                 src += sizeof(IBPP::Timestamp);\r
862                                 dst += mElemSize;\r
863                         }\r
864                         break;\r
865 \r
866                 case blr_sql_date :\r
867                         if (adtype != IBPP::adDate) throw LogicExceptionImpl("Array::ReadTo",\r
868                                                                                                 _("Incompatible types."));\r
869                         for (int i = 0; i < mElemCount; i++)\r
870                         {\r
871                                 encodeDate(*(ISC_DATE*)dst, *(IBPP::Date*)src); \r
872                                 src += sizeof(IBPP::Date);\r
873                                 dst += mElemSize;\r
874                         }\r
875                         break;\r
876 \r
877                 case blr_sql_time :\r
878                         if (adtype != IBPP::adTime) throw LogicExceptionImpl("Array::ReadTo",\r
879                                                                                                 _("Incompatible types."));\r
880                         for (int i = 0; i < mElemCount; i++)\r
881                         {\r
882                                 encodeTime(*(ISC_TIME*)dst, *(IBPP::Time*)src);\r
883                                 src += sizeof(IBPP::Time);\r
884                                 dst += mElemSize;\r
885                         }\r
886                         break;\r
887 \r
888                 default :\r
889                         throw LogicExceptionImpl("Array::WriteFrom", _("Unknown sql type."));\r
890         }\r
891 \r
892         IBS status;\r
893         ISC_LONG lenbuf = mBufferSize;\r
894         (*gds.Call()->m_array_put_slice)(status.Self(), mDatabase->GetHandlePtr(),\r
895                 mTransaction->GetHandlePtr(), &mId, &mDesc, mBuffer, &lenbuf);\r
896         if (status.Errors())\r
897                 throw SQLExceptionImpl(status, "Array::WriteFrom", _("isc_array_put_slice failed."));\r
898         if (lenbuf != mBufferSize)\r
899                 throw SQLExceptionImpl(status, "Array::WriteFrom", _("Internal buffer size discrepancy."));\r
900 }\r
901 \r
902 IBPP::Database ArrayImpl::DatabasePtr() const\r
903 {\r
904         if (mDatabase == 0) throw LogicExceptionImpl("Array::DatabasePtr",\r
905                         _("No Database is attached."));\r
906         return mDatabase;\r
907 }\r
908 \r
909 IBPP::Transaction ArrayImpl::TransactionPtr() const\r
910 {\r
911         if (mTransaction == 0) throw LogicExceptionImpl("Array::TransactionPtr",\r
912                         _("No Transaction is attached."));\r
913         return mTransaction;\r
914 }\r
915 \r
916 IBPP::IArray* ArrayImpl::AddRef()\r
917 {\r
918         ASSERTION(mRefCount >= 0);\r
919         ++mRefCount;\r
920         return this;\r
921 }\r
922 \r
923 void ArrayImpl::Release()\r
924 {\r
925         // Release cannot throw, except in DEBUG builds on assertion\r
926         ASSERTION(mRefCount >= 0);\r
927         --mRefCount;\r
928         try { if (mRefCount <= 0) delete this; }\r
929                 catch (...) { }\r
930 }\r
931 \r
932 //      (((((((( OBJECT INTERNAL METHODS ))))))))\r
933 \r
934 void ArrayImpl::Init()\r
935 {\r
936         ResetId();\r
937         mDescribed = false;\r
938         mDatabase = 0;\r
939         mTransaction = 0;\r
940         mBuffer = 0;\r
941         mBufferSize = 0;\r
942 }\r
943 \r
944 void ArrayImpl::SetId(ISC_QUAD* quad)\r
945 {\r
946         if (quad == 0)\r
947                 throw LogicExceptionImpl("ArrayImpl::SetId", _("Null Id reference detected."));\r
948 \r
949         memcpy(&mId, quad, sizeof(mId));\r
950         mIdAssigned = true;\r
951 }\r
952 \r
953 void ArrayImpl::GetId(ISC_QUAD* quad)\r
954 {\r
955         if (quad == 0)\r
956                 throw LogicExceptionImpl("ArrayImpl::GetId", _("Null Id reference detected."));\r
957 \r
958         memcpy(quad, &mId, sizeof(mId));\r
959 }\r
960 \r
961 void ArrayImpl::ResetId()\r
962 {\r
963         memset(&mId, 0, sizeof(mId));\r
964         mIdAssigned = false;\r
965 }\r
966 \r
967 void ArrayImpl::AllocArrayBuffer()\r
968 {\r
969         // Clean previous buffer if any\r
970         if (mBuffer != 0) delete [] (char*)mBuffer;\r
971         mBuffer = 0;\r
972 \r
973         // Computes total number of elements in the array or slice\r
974         mElemCount = 1;\r
975         for (int i = 0; i < mDesc.array_desc_dimensions; i++)\r
976         {\r
977                 mElemCount = mElemCount *\r
978                         (mDesc.array_desc_bounds[i].array_bound_upper -\r
979                                 mDesc.array_desc_bounds[i].array_bound_lower + 1);\r
980         }\r
981 \r
982         // Allocates a buffer for this count of elements\r
983         mElemSize = mDesc.array_desc_length;\r
984         if (mDesc.array_desc_dtype == blr_varying) mElemSize += 2;\r
985         else if (mDesc.array_desc_dtype == blr_cstring) mElemSize += 1;\r
986         mBufferSize = mElemSize * mElemCount;\r
987         mBuffer = (void*) new char[mBufferSize];\r
988 }\r
989 \r
990 void ArrayImpl::AttachDatabaseImpl(DatabaseImpl* database)\r
991 {\r
992         if (database == 0) throw LogicExceptionImpl("Array::AttachDatabase",\r
993                         _("Can't attach a 0 Database object."));\r
994 \r
995         if (mDatabase != 0) mDatabase->DetachArrayImpl(this);\r
996         mDatabase = database;\r
997         mDatabase->AttachArrayImpl(this);\r
998 }\r
999 \r
1000 void ArrayImpl::AttachTransactionImpl(TransactionImpl* transaction)\r
1001 {\r
1002         if (transaction == 0) throw LogicExceptionImpl("Array::AttachTransaction",\r
1003                         _("Can't attach a 0 Transaction object."));\r
1004 \r
1005         if (mTransaction != 0) mTransaction->DetachArrayImpl(this);\r
1006         mTransaction = transaction;\r
1007         mTransaction->AttachArrayImpl(this);\r
1008 }\r
1009 \r
1010 void ArrayImpl::DetachDatabaseImpl()\r
1011 {\r
1012         if (mDatabase == 0) return;\r
1013 \r
1014         mDatabase->DetachArrayImpl(this);\r
1015         mDatabase = 0;\r
1016 }\r
1017 \r
1018 void ArrayImpl::DetachTransactionImpl()\r
1019 {\r
1020         if (mTransaction == 0) return;\r
1021 \r
1022         mTransaction->DetachArrayImpl(this);\r
1023         mTransaction = 0;\r
1024 }\r
1025 \r
1026 ArrayImpl::ArrayImpl(DatabaseImpl* database, TransactionImpl* transaction)\r
1027         : mRefCount(0)\r
1028 {\r
1029         Init();\r
1030         AttachDatabaseImpl(database);\r
1031         if (transaction != 0) AttachTransactionImpl(transaction);\r
1032 }\r
1033 \r
1034 ArrayImpl::~ArrayImpl()\r
1035 {\r
1036         try { if (mTransaction != 0) mTransaction->DetachArrayImpl(this); }\r
1037                 catch (...) {}\r
1038         try { if (mDatabase != 0) mDatabase->DetachArrayImpl(this); }\r
1039                 catch (...) {}\r
1040         try { if (mBuffer != 0) delete [] (char*)mBuffer; }\r
1041                 catch (...) {}\r
1042 }\r
1043 \r
1044 //\r
1045 //      EOF\r
1046 //\r