X-Git-Url: https://git.stg.codes/stg.git/blobdiff_plain/8c6fa3fbaccc22127280bf77a48fab5a3ee0716e..46b0747592074017ff0ea4b33d4a7194235886e5:/libs/ibpp/array.cpp diff --git a/libs/ibpp/array.cpp b/libs/ibpp/array.cpp new file mode 100644 index 00000000..2dc7ab27 --- /dev/null +++ b/libs/ibpp/array.cpp @@ -0,0 +1,1046 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// File : $Id: array.cpp,v 1.2 2009/03/19 20:00:28 faust Exp $ +// Subject : IBPP, Array class implementation +// +/////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright 2000-2006 T.I.P. Group S.A. and the IBPP Team (www.ibpp.org) +// +// The contents of this file are subject to the IBPP License (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at http://www.ibpp.org or in the 'license.txt' +// file which must have been distributed along with this file. +// +// This software, distributed under the License, is distributed on an "AS IS" +// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +// License for the specific language governing rights and limitations +// under the License. +// +/////////////////////////////////////////////////////////////////////////////// +// +// COMMENTS +// * Tabulations should be set every four characters when editing this file. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifdef _MSC_VER +#pragma warning(disable: 4786 4996) +#ifndef _DEBUG +#pragma warning(disable: 4702) +#endif +#endif + +#include "_ibpp.h" + +#ifdef HAS_HDRSTOP +#pragma hdrstop +#endif + +#include +#include + +using namespace ibpp_internals; + +// (((((((( OBJECT INTERFACE IMPLEMENTATION )))))))) + +void ArrayImpl::Describe(const std::string& table, const std::string& column) +{ + //if (mIdAssigned) + // throw LogicExceptionImpl("Array::Lookup", _("Array already in use.")); + if (mDatabase == 0) + throw LogicExceptionImpl("Array::Lookup", _("No Database is attached.")); + if (mTransaction == 0) + throw LogicExceptionImpl("Array::Lookup", _("No Transaction is attached.")); + + ResetId(); // Re-use this array object if was previously assigned + + IBS status; + (*gds.Call()->m_array_lookup_bounds)(status.Self(), mDatabase->GetHandlePtr(), + mTransaction->GetHandlePtr(), const_cast(table.c_str()), + const_cast(column.c_str()), &mDesc); + if (status.Errors()) + throw SQLExceptionImpl(status, "Array::Lookup", + _("isc_array_lookup_bounds failed.")); + + AllocArrayBuffer(); + + mDescribed = true; +} + +void ArrayImpl::SetBounds(int dim, int low, int high) +{ + if (! mDescribed) + throw LogicExceptionImpl("Array::SetBounds", _("Array description not set.")); + if (mDatabase == 0) + throw LogicExceptionImpl("Array::SetBounds", _("No Database is attached.")); + if (mTransaction == 0) + throw LogicExceptionImpl("Array::SetBounds", _("No Transaction is attached.")); + if (dim < 0 || dim > mDesc.array_desc_dimensions-1) + throw LogicExceptionImpl("Array::SetBounds", _("Invalid dimension.")); + if (low > high || + low < mDesc.array_desc_bounds[dim].array_bound_lower || + low > mDesc.array_desc_bounds[dim].array_bound_upper || + high > mDesc.array_desc_bounds[dim].array_bound_upper || + high < mDesc.array_desc_bounds[dim].array_bound_lower) + throw LogicExceptionImpl("Array::SetBounds", + _("Invalid bounds. You can only narrow the bounds.")); + + mDesc.array_desc_bounds[dim].array_bound_lower = short(low); + mDesc.array_desc_bounds[dim].array_bound_upper = short(high); + + AllocArrayBuffer(); +} + +IBPP::SDT ArrayImpl::ElementType() +{ + if (! mDescribed) + throw LogicExceptionImpl("Array::ElementType", + _("Array description not set.")); + + IBPP::SDT value; + switch (mDesc.array_desc_dtype) + { + case blr_text : value = IBPP::sdString; break; + case blr_varying : value = IBPP::sdString; break; + case blr_cstring : value = IBPP::sdString; break; + case blr_short : value = IBPP::sdSmallint; break; + case blr_long : value = IBPP::sdInteger; break; + case blr_int64 : value = IBPP::sdLargeint; break; + case blr_float : value = IBPP::sdFloat; break; + case blr_double : value = IBPP::sdDouble; break; + case blr_timestamp : value = IBPP::sdTimestamp; break; + case blr_sql_date : value = IBPP::sdDate; break; + case blr_sql_time : value = IBPP::sdTime; break; + default : throw LogicExceptionImpl("Array::ElementType", + _("Found an unknown sqltype !")); + } + + return value; +} + +int ArrayImpl::ElementSize() +{ + if (! mDescribed) + throw LogicExceptionImpl("Array::ElementSize", + _("Array description not set.")); + + return mDesc.array_desc_length; +} + +int ArrayImpl::ElementScale() +{ + if (! mDescribed) + throw LogicExceptionImpl("Array::ElementScale", + _("Array description not set.")); + + return mDesc.array_desc_scale; +} + +int ArrayImpl::Dimensions() +{ + if (! mDescribed) + throw LogicExceptionImpl("Array::Dimensions", + _("Array description not set.")); + + return mDesc.array_desc_dimensions; +} + +void ArrayImpl::Bounds(int dim, int* low, int* high) +{ + if (! mDescribed) + throw LogicExceptionImpl("Array::Bounds", _("Array description not set.")); + if (dim < 0 || dim > mDesc.array_desc_dimensions-1) + throw LogicExceptionImpl("Array::Bounds", _("Invalid dimension.")); + if (low == 0 || high == 0) + throw LogicExceptionImpl("Array::Bounds", _("Null reference detected.")); + + *low = mDesc.array_desc_bounds[dim].array_bound_lower; + *high = mDesc.array_desc_bounds[dim].array_bound_upper; +} + +/* + +COMMENTS + +1) +For an array column of type CHAR(X), the internal type returned or expected is blr_text. +In such case, the byte array received or submitted to get/put_slice is formatted in +elements of X bytes, which correspond to what is reported in array_desc_length. +The elements are not '\0' terminated but are right-padded with spaces ' '. + +2) +For an array column of type VARCHAR(X), the internal type is blr_varying. +The underlying format is rather curious and different than what is used in XSQLDA. +The element size is reported in array_desc_length as X. +Yet each element of the byte array is expected to be of size X+2 (just as if we were +to stuff a short in the first 2 bytes to store the length (as is done with XSQLDA). +No. The string of X characters maximum has to be stored in the chunks of X+2 bytes as +a zero-terminated c-string. Note that the buffer is indeed one byte too large. +Internally, the API probably convert in-place in these chunks the zero-terminated string +to a variable-size string with a short in front and the string data non zero-terminated +behind. + +3) +With Interbase 6.0 and Firebird 1.0 (initial april 2002 release), the int64 support is +broken regarding the arrays. It is not possible to work on arrays using a datatype stored +in an int64, as for instance any NUMERIC(x,0) where x is equal or greater than 10. That's +a bug in the engine, not in IBPP, which has been fixed in june 2002. Engines compiled from +the current Firebird CVS code as of july 2002 are okay. As will be the 1.01 Firebird version. +We have no idea if this is fixed or not in Interbase 6.5 though. + +*/ + +void ArrayImpl::ReadTo(IBPP::ADT adtype, void* data, int datacount) +{ + if (! mIdAssigned) + throw LogicExceptionImpl("Array::ReadTo", _("Array Id not read from column.")); + if (! mDescribed) + throw LogicExceptionImpl("Array::ReadTo", _("Array description not set.")); + if (mDatabase == 0) + throw LogicExceptionImpl("Array::ReadTo", _("No Database is attached.")); + if (mTransaction == 0) + throw LogicExceptionImpl("Array::ReadTo", _("No Transaction is attached.")); + if (datacount != mElemCount) + throw LogicExceptionImpl("Array::ReadTo", _("Wrong count of array elements")); + + IBS status; + ISC_LONG lenbuf = mBufferSize; + (*gds.Call()->m_array_get_slice)(status.Self(), mDatabase->GetHandlePtr(), + mTransaction->GetHandlePtr(), &mId, &mDesc, mBuffer, &lenbuf); + if (status.Errors()) + throw SQLExceptionImpl(status, "Array::ReadTo", _("isc_array_get_slice failed.")); + if (lenbuf != mBufferSize) + throw SQLExceptionImpl(status, "Array::ReadTo", _("Internal buffer size discrepancy.")); + + // Now, convert the types and copy values to the user array... + int len; + char* src = (char*)mBuffer; + char* dst = (char*)data; + + switch (mDesc.array_desc_dtype) + { + case blr_text : + if (adtype == IBPP::adString) + { + for (int i = 0; i < mElemCount; i++) + { + strncpy(dst, src, mElemSize); + dst[mElemSize] = '\0'; + src += mElemSize; + dst += (mElemSize + 1); + } + } + else if (adtype == IBPP::adBool) + { + for (int i = 0; i < mElemCount; i++) + { + if (*src == 't' || *src == 'T' || *src == 'y' || *src == 'Y' || *src == '1') + *(bool*)dst = true; + else *(bool*)dst = false; + src += mElemSize; + dst += sizeof(bool); + } + } + else throw LogicExceptionImpl("Array::ReadTo", _("Incompatible types.")); + break; + + case blr_varying : + if (adtype == IBPP::adString) + { + for (int i = 0; i < mElemCount; i++) + { + len = (int)strlen(src); + if (len > mElemSize-2) len = mElemSize-2; + strncpy(dst, src, len); + dst[len] = '\0'; + src += mElemSize; + dst += (mElemSize - 2 + 1); + } + } + else if (adtype == IBPP::adBool) + { + for (int i = 0; i < mElemCount; i++) + { + if (*src == 't' || *src == 'T' || *src == 'y' || *src == 'Y' || *src == '1') + *(bool*)dst = true; + else *(bool*)dst = false; + src += mElemSize; + dst += sizeof(bool); + } + } + else throw LogicExceptionImpl("Array::ReadTo", _("Incompatible types.")); + break; + + case blr_short : + if (adtype == IBPP::adBool) + { + for (int i = 0; i < mElemCount; i++) + { + *(bool*)dst = (*(short*)src != 0) ? true : false; + src += mElemSize; + dst += sizeof(bool); + } + } + else if (adtype == IBPP::adInt16) + { + for (int i = 0; i < mElemCount; i++) + { + *(short*)dst = *(short*)src; + src += mElemSize; + dst += sizeof(short); + } + } + else if (adtype == IBPP::adInt32) + { + for (int i = 0; i < mElemCount; i++) + { + *(int*)dst = (int)*(short*)src; + src += mElemSize; + dst += sizeof(int); + } + } + else if (adtype == IBPP::adInt64) + { + for (int i = 0; i < mElemCount; i++) + { + *(int64_t*)dst = (int64_t)*(short*)src; + src += mElemSize; + dst += sizeof(int64_t); + } + } + else if (adtype == IBPP::adFloat) + { + // This SQL_SHORT is a NUMERIC(x,y), scale it ! + double divisor = consts::dscales[-mDesc.array_desc_scale]; + for (int i = 0; i < mElemCount; i++) + { + *(float*)dst = (float)(*(short*)src / divisor); + src += mElemSize; + dst += sizeof(float); + } + } + else if (adtype == IBPP::adDouble) + { + // This SQL_SHORT is a NUMERIC(x,y), scale it ! + double divisor = consts::dscales[-mDesc.array_desc_scale]; + for (int i = 0; i < mElemCount; i++) + { + *(double*)dst = (double)(*(short*)src / divisor); + src += mElemSize; + dst += sizeof(double); + } + } + else throw LogicExceptionImpl("Array::ReadTo", _("Incompatible types.")); + break; + + case blr_long : + if (adtype == IBPP::adBool) + { + for (int i = 0; i < mElemCount; i++) + { + *(bool*)dst = (*(long*)src != 0) ? true : false; + src += mElemSize; + dst += sizeof(bool); + } + } + else if (adtype == IBPP::adInt16) + { + for (int i = 0; i < mElemCount; i++) + { + if (*(long*)src < consts::min16 || *(long*)src > consts::max16) + throw LogicExceptionImpl("Array::ReadTo", + _("Out of range numeric conversion !")); + *(short*)dst = short(*(long*)src); + src += mElemSize; + dst += sizeof(short); + } + } + else if (adtype == IBPP::adInt32) + { + for (int i = 0; i < mElemCount; i++) + { + *(long*)dst = *(long*)src; + src += mElemSize; + dst += sizeof(long); + } + } + else if (adtype == IBPP::adInt64) + { + for (int i = 0; i < mElemCount; i++) + { + *(int64_t*)dst = (int64_t)*(long*)src; + src += mElemSize; + dst += sizeof(int64_t); + } + } + else if (adtype == IBPP::adFloat) + { + // This SQL_SHORT is a NUMERIC(x,y), scale it ! + double divisor = consts::dscales[-mDesc.array_desc_scale]; + for (int i = 0; i < mElemCount; i++) + { + *(float*)dst = (float)(*(long*)src / divisor); + src += mElemSize; + dst += sizeof(float); + } + } + else if (adtype == IBPP::adDouble) + { + // This SQL_SHORT is a NUMERIC(x,y), scale it ! + double divisor = consts::dscales[-mDesc.array_desc_scale]; + for (int i = 0; i < mElemCount; i++) + { + *(double*)dst = (double)(*(long*)src / divisor); + src += mElemSize; + dst += sizeof(double); + } + } + else throw LogicExceptionImpl("Array::ReadTo", _("Incompatible types.")); + break; + + case blr_int64 : + if (adtype == IBPP::adBool) + { + for (int i = 0; i < mElemCount; i++) + { + *(bool*)dst = (*(int64_t*)src != 0) ? true : false; + src += mElemSize; + dst += sizeof(bool); + } + } + else if (adtype == IBPP::adInt16) + { + for (int i = 0; i < mElemCount; i++) + { + if (*(int64_t*)src < consts::min16 || *(int64_t*)src > consts::max16) + throw LogicExceptionImpl("Array::ReadTo", + _("Out of range numeric conversion !")); + *(short*)dst = short(*(int64_t*)src); + src += mElemSize; + dst += sizeof(short); + } + } + else if (adtype == IBPP::adInt32) + { + for (int i = 0; i < mElemCount; i++) + { + if (*(int64_t*)src < consts::min32 || *(int64_t*)src > consts::max32) + throw LogicExceptionImpl("Array::ReadTo", + _("Out of range numeric conversion !")); + *(long*)dst = (long)*(int64_t*)src; + src += mElemSize; + dst += sizeof(long); + } + } + else if (adtype == IBPP::adInt64) + { + for (int i = 0; i < mElemCount; i++) + { + *(int64_t*)dst = *(int64_t*)src; + src += mElemSize; + dst += sizeof(int64_t); + } + } + else if (adtype == IBPP::adFloat) + { + // This SQL_SHORT is a NUMERIC(x,y), scale it ! + double divisor = consts::dscales[-mDesc.array_desc_scale]; + for (int i = 0; i < mElemCount; i++) + { + *(float*)dst = (float)(*(int64_t*)src / divisor); + src += mElemSize; + dst += sizeof(float); + } + } + else if (adtype == IBPP::adDouble) + { + // This SQL_SHORT is a NUMERIC(x,y), scale it ! + double divisor = consts::dscales[-mDesc.array_desc_scale]; + for (int i = 0; i < mElemCount; i++) + { + *(double*)dst = (double)(*(int64_t*)src / divisor); + src += mElemSize; + dst += sizeof(double); + } + } + else throw LogicExceptionImpl("Array::ReadTo", _("Incompatible types.")); + break; + + case blr_float : + if (adtype != IBPP::adFloat || mDesc.array_desc_scale != 0) + throw LogicExceptionImpl("Array::ReadTo", _("Incompatible types.")); + for (int i = 0; i < mElemCount; i++) + { + *(float*)dst = *(float*)src; + src += mElemSize; + dst += sizeof(float); + } + break; + + case blr_double : + if (adtype != IBPP::adDouble) throw LogicExceptionImpl("Array::ReadTo", + _("Incompatible types.")); + if (mDesc.array_desc_scale != 0) + { + // Round to scale of NUMERIC(x,y) + double divisor = consts::dscales[-mDesc.array_desc_scale]; + for (int i = 0; i < mElemCount; i++) + { + *(double*)dst = (double)(*(double*)src / divisor); + src += mElemSize; + dst += sizeof(double); + } + } + else + { + for (int i = 0; i < mElemCount; i++) + { + *(double*)dst = *(double*)src; + src += mElemSize; + dst += sizeof(double); + } + } + break; + + case blr_timestamp : + if (adtype != IBPP::adTimestamp) throw LogicExceptionImpl("Array::ReadTo", + _("Incompatible types.")); + for (int i = 0; i < mElemCount; i++) + { + decodeTimestamp(*(IBPP::Timestamp*)dst, *(ISC_TIMESTAMP*)src); + src += mElemSize; + dst += sizeof(IBPP::Timestamp); + } + break; + + case blr_sql_date : + if (adtype != IBPP::adDate) throw LogicExceptionImpl("Array::ReadTo", + _("Incompatible types.")); + for (int i = 0; i < mElemCount; i++) + { + decodeDate(*(IBPP::Date*)dst, *(ISC_DATE*)src); + src += mElemSize; + dst += sizeof(IBPP::Date); + } + break; + + case blr_sql_time : + if (adtype != IBPP::adTime) throw LogicExceptionImpl("Array::ReadTo", + _("Incompatible types.")); + for (int i = 0; i < mElemCount; i++) + { + decodeTime(*(IBPP::Time*)dst, *(ISC_TIME*)src); + src += mElemSize; + dst += sizeof(IBPP::Time); + } + break; + + default : + throw LogicExceptionImpl("Array::ReadTo", _("Unknown sql type.")); + } +} + +void ArrayImpl::WriteFrom(IBPP::ADT adtype, const void* data, int datacount) +{ + if (! mDescribed) + throw LogicExceptionImpl("Array::WriteFrom", _("Array description not set.")); + if (mDatabase == 0) + throw LogicExceptionImpl("Array::WriteFrom", _("No Database is attached.")); + if (mTransaction == 0) + throw LogicExceptionImpl("Array::WriteFrom", _("No Transaction is attached.")); + if (datacount != mElemCount) + throw LogicExceptionImpl("Array::ReadTo", _("Wrong count of array elements")); + + // Read user data and convert types to the mBuffer + int len; + char* src = (char*)data; + char* dst = (char*)mBuffer; + + switch (mDesc.array_desc_dtype) + { + case blr_text : + if (adtype == IBPP::adString) + { + for (int i = 0; i < mElemCount; i++) + { + len = (int)strlen(src); + if (len > mElemSize) len = mElemSize; + strncpy(dst, src, len); + while (len < mElemSize) dst[len++] = ' '; + src += (mElemSize + 1); + dst += mElemSize; + } + } + else if (adtype == IBPP::adBool) + { + for (int i = 0; i < mElemCount; i++) + { + *dst = *(bool*)src ? 'T' : 'F'; + len = 1; + while (len < mElemSize) dst[len++] = ' '; + src += sizeof(bool); + dst += mElemSize; + } + } + else throw LogicExceptionImpl("Array::WriteFrom", _("Incompatible types.")); + break; + + case blr_varying : + if (adtype == IBPP::adString) + { + for (int i = 0; i < mElemCount; i++) + { + len = (int)strlen(src); + if (len > mElemSize-2) len = mElemSize-2; + strncpy(dst, src, len); + dst[len] = '\0'; + src += (mElemSize - 2 + 1); + dst += mElemSize; + } + } + else if (adtype == IBPP::adBool) + { + for (int i = 0; i < mElemCount; i++) + { + *(short*)dst = (short)1; + dst[2] = *(bool*)src ? 'T' : 'F'; + src += sizeof(bool); + dst += mElemSize; + } + } + else throw LogicExceptionImpl("Array::WriteFrom", _("Incompatible types.")); + break; + + case blr_short : + if (adtype == IBPP::adBool) + { + for (int i = 0; i < mElemCount; i++) + { + *(short*)dst = short(*(bool*)src ? 1 : 0); + src += sizeof(bool); + dst += mElemSize; + } + } + else if (adtype == IBPP::adInt16) + { + for (int i = 0; i < mElemCount; i++) + { + *(short*)dst = *(short*)src; + src += sizeof(short); + dst += mElemSize; + } + } + else if (adtype == IBPP::adInt32) + { + for (int i = 0; i < mElemCount; i++) + { + if (*(long*)src < consts::min16 || *(long*)src > consts::max16) + throw LogicExceptionImpl("Array::WriteFrom", + _("Out of range numeric conversion !")); + *(short*)dst = (short)*(int*)src; + src += sizeof(int); + dst += mElemSize; + } + } + else if (adtype == IBPP::adInt64) + { + for (int i = 0; i < mElemCount; i++) + { + if (*(int64_t*)src < consts::min16 || *(int64_t*)src > consts::max16) + throw LogicExceptionImpl("Array::WriteFrom", + _("Out of range numeric conversion !")); + *(short*)dst = (short)*(int64_t*)src; + src += sizeof(int64_t); + dst += mElemSize; + } + } + else if (adtype == IBPP::adFloat) + { + // This SQL_SHORT is a NUMERIC(x,y), scale it ! + double multiplier = consts::dscales[-mDesc.array_desc_scale]; + for (int i = 0; i < mElemCount; i++) + { + *(short*)dst = + (short)floor(*(float*)src * multiplier + 0.5); + src += sizeof(float); + dst += mElemSize; + } + } + else if (adtype == IBPP::adDouble) + { + // This SQL_SHORT is a NUMERIC(x,y), scale it ! + double multiplier = consts::dscales[-mDesc.array_desc_scale]; + for (int i = 0; i < mElemCount; i++) + { + *(short*)dst = + (short)floor(*(double*)src * multiplier + 0.5); + src += sizeof(double); + dst += mElemSize; + } + } + else throw LogicExceptionImpl("Array::WriteFrom", _("Incompatible types.")); + break; + + case blr_long : + if (adtype == IBPP::adBool) + { + for (int i = 0; i < mElemCount; i++) + { + *(long*)dst = *(bool*)src ? 1 : 0; + src += sizeof(bool); + dst += mElemSize; + } + } + else if (adtype == IBPP::adInt16) + { + for (int i = 0; i < mElemCount; i++) + { + *(long*)dst = *(short*)src; + src += sizeof(short); + dst += mElemSize; + } + } + else if (adtype == IBPP::adInt32) + { + for (int i = 0; i < mElemCount; i++) + { + *(long*)dst = *(long*)src; + src += sizeof(long); + dst += mElemSize; + } + } + else if (adtype == IBPP::adInt64) + { + for (int i = 0; i < mElemCount; i++) + { + if (*(int64_t*)src < consts::min32 || *(int64_t*)src > consts::max32) + throw LogicExceptionImpl("Array::WriteFrom", + _("Out of range numeric conversion !")); + *(long*)dst = (long)*(int64_t*)src; + src += sizeof(int64_t); + dst += mElemSize; + } + } + else if (adtype == IBPP::adFloat) + { + // This SQL_INT is a NUMERIC(x,y), scale it ! + double multiplier = consts::dscales[-mDesc.array_desc_scale]; + for (int i = 0; i < mElemCount; i++) + { + *(long*)dst = + (long)floor(*(float*)src * multiplier + 0.5); + src += sizeof(float); + dst += mElemSize; + } + } + else if (adtype == IBPP::adDouble) + { + // This SQL_INT is a NUMERIC(x,y), scale it ! + double multiplier = consts::dscales[-mDesc.array_desc_scale]; + for (int i = 0; i < mElemCount; i++) + { + *(long*)dst = + (long)floor(*(double*)src * multiplier + 0.5); + src += sizeof(double); + dst += mElemSize; + } + } + else throw LogicExceptionImpl("Array::WriteFrom", _("Incompatible types.")); + break; + + case blr_int64 : + if (adtype == IBPP::adBool) + { + for (int i = 0; i < mElemCount; i++) + { + *(int64_t*)dst = *(bool*)src ? 1 : 0; + src += sizeof(bool); + dst += mElemSize; + } + } + else if (adtype == IBPP::adInt16) + { + for (int i = 0; i < mElemCount; i++) + { + *(int64_t*)dst = *(short*)src; + src += sizeof(short); + dst += mElemSize; + } + } + else if (adtype == IBPP::adInt32) + { + for (int i = 0; i < mElemCount; i++) + { + *(int64_t*)dst = *(long*)src; + src += sizeof(long); + dst += mElemSize; + } + } + else if (adtype == IBPP::adInt64) + { + for (int i = 0; i < mElemCount; i++) + { + *(int64_t*)dst = *(int64_t*)src; + src += sizeof(int64_t); + dst += mElemSize; + } + } + else if (adtype == IBPP::adFloat) + { + // This SQL_INT is a NUMERIC(x,y), scale it ! + double multiplier = consts::dscales[-mDesc.array_desc_scale]; + for (int i = 0; i < mElemCount; i++) + { + *(int64_t*)dst = + (int64_t)floor(*(float*)src * multiplier + 0.5); + src += sizeof(float); + dst += mElemSize; + } + } + else if (adtype == IBPP::adDouble) + { + // This SQL_INT is a NUMERIC(x,y), scale it ! + double multiplier = consts::dscales[-mDesc.array_desc_scale]; + for (int i = 0; i < mElemCount; i++) + { + *(int64_t*)dst = + (int64_t)floor(*(double*)src * multiplier + 0.5); + src += sizeof(double); + dst += mElemSize; + } + } + else + throw LogicExceptionImpl("Array::WriteFrom", + _("Incompatible types (blr_int64 and ADT %d)."), (int)adtype); + break; + + case blr_float : + if (adtype != IBPP::adFloat || mDesc.array_desc_scale != 0) + throw LogicExceptionImpl("Array::WriteFrom", _("Incompatible types.")); + for (int i = 0; i < mElemCount; i++) + { + *(float*)dst = *(float*)src; + src += sizeof(float); + dst += mElemSize; + } + break; + + case blr_double : + if (adtype != IBPP::adDouble) throw LogicExceptionImpl("Array::WriteFrom", + _("Incompatible types.")); + if (mDesc.array_desc_scale != 0) + { + // Round to scale of NUMERIC(x,y) + double multiplier = consts::dscales[-mDesc.array_desc_scale]; + for (int i = 0; i < mElemCount; i++) + { + *(double*)dst = + floor(*(double*)src * multiplier + 0.5) / multiplier; + src += sizeof(double); + dst += mElemSize; + } + } + else + { + for (int i = 0; i < mElemCount; i++) + { + *(double*)dst = *(double*)src; + src += sizeof(double); + dst += mElemSize; + } + } + break; + + case blr_timestamp : + if (adtype != IBPP::adTimestamp) throw LogicExceptionImpl("Array::ReadTo", + _("Incompatible types.")); + for (int i = 0; i < mElemCount; i++) + { + encodeTimestamp(*(ISC_TIMESTAMP*)dst, *(IBPP::Timestamp*)src); + src += sizeof(IBPP::Timestamp); + dst += mElemSize; + } + break; + + case blr_sql_date : + if (adtype != IBPP::adDate) throw LogicExceptionImpl("Array::ReadTo", + _("Incompatible types.")); + for (int i = 0; i < mElemCount; i++) + { + encodeDate(*(ISC_DATE*)dst, *(IBPP::Date*)src); + src += sizeof(IBPP::Date); + dst += mElemSize; + } + break; + + case blr_sql_time : + if (adtype != IBPP::adTime) throw LogicExceptionImpl("Array::ReadTo", + _("Incompatible types.")); + for (int i = 0; i < mElemCount; i++) + { + encodeTime(*(ISC_TIME*)dst, *(IBPP::Time*)src); + src += sizeof(IBPP::Time); + dst += mElemSize; + } + break; + + default : + throw LogicExceptionImpl("Array::WriteFrom", _("Unknown sql type.")); + } + + IBS status; + ISC_LONG lenbuf = mBufferSize; + (*gds.Call()->m_array_put_slice)(status.Self(), mDatabase->GetHandlePtr(), + mTransaction->GetHandlePtr(), &mId, &mDesc, mBuffer, &lenbuf); + if (status.Errors()) + throw SQLExceptionImpl(status, "Array::WriteFrom", _("isc_array_put_slice failed.")); + if (lenbuf != mBufferSize) + throw SQLExceptionImpl(status, "Array::WriteFrom", _("Internal buffer size discrepancy.")); +} + +IBPP::Database ArrayImpl::DatabasePtr() const +{ + if (mDatabase == 0) throw LogicExceptionImpl("Array::DatabasePtr", + _("No Database is attached.")); + return mDatabase; +} + +IBPP::Transaction ArrayImpl::TransactionPtr() const +{ + if (mTransaction == 0) throw LogicExceptionImpl("Array::TransactionPtr", + _("No Transaction is attached.")); + return mTransaction; +} + +IBPP::IArray* ArrayImpl::AddRef() +{ + ASSERTION(mRefCount >= 0); + ++mRefCount; + return this; +} + +void ArrayImpl::Release() +{ + // Release cannot throw, except in DEBUG builds on assertion + ASSERTION(mRefCount >= 0); + --mRefCount; + try { if (mRefCount <= 0) delete this; } + catch (...) { } +} + +// (((((((( OBJECT INTERNAL METHODS )))))))) + +void ArrayImpl::Init() +{ + ResetId(); + mDescribed = false; + mDatabase = 0; + mTransaction = 0; + mBuffer = 0; + mBufferSize = 0; +} + +void ArrayImpl::SetId(ISC_QUAD* quad) +{ + if (quad == 0) + throw LogicExceptionImpl("ArrayImpl::SetId", _("Null Id reference detected.")); + + memcpy(&mId, quad, sizeof(mId)); + mIdAssigned = true; +} + +void ArrayImpl::GetId(ISC_QUAD* quad) +{ + if (quad == 0) + throw LogicExceptionImpl("ArrayImpl::GetId", _("Null Id reference detected.")); + + memcpy(quad, &mId, sizeof(mId)); +} + +void ArrayImpl::ResetId() +{ + memset(&mId, 0, sizeof(mId)); + mIdAssigned = false; +} + +void ArrayImpl::AllocArrayBuffer() +{ + // Clean previous buffer if any + if (mBuffer != 0) delete [] (char*)mBuffer; + mBuffer = 0; + + // Computes total number of elements in the array or slice + mElemCount = 1; + for (int i = 0; i < mDesc.array_desc_dimensions; i++) + { + mElemCount = mElemCount * + (mDesc.array_desc_bounds[i].array_bound_upper - + mDesc.array_desc_bounds[i].array_bound_lower + 1); + } + + // Allocates a buffer for this count of elements + mElemSize = mDesc.array_desc_length; + if (mDesc.array_desc_dtype == blr_varying) mElemSize += 2; + else if (mDesc.array_desc_dtype == blr_cstring) mElemSize += 1; + mBufferSize = mElemSize * mElemCount; + mBuffer = (void*) new char[mBufferSize]; +} + +void ArrayImpl::AttachDatabaseImpl(DatabaseImpl* database) +{ + if (database == 0) throw LogicExceptionImpl("Array::AttachDatabase", + _("Can't attach a 0 Database object.")); + + if (mDatabase != 0) mDatabase->DetachArrayImpl(this); + mDatabase = database; + mDatabase->AttachArrayImpl(this); +} + +void ArrayImpl::AttachTransactionImpl(TransactionImpl* transaction) +{ + if (transaction == 0) throw LogicExceptionImpl("Array::AttachTransaction", + _("Can't attach a 0 Transaction object.")); + + if (mTransaction != 0) mTransaction->DetachArrayImpl(this); + mTransaction = transaction; + mTransaction->AttachArrayImpl(this); +} + +void ArrayImpl::DetachDatabaseImpl() +{ + if (mDatabase == 0) return; + + mDatabase->DetachArrayImpl(this); + mDatabase = 0; +} + +void ArrayImpl::DetachTransactionImpl() +{ + if (mTransaction == 0) return; + + mTransaction->DetachArrayImpl(this); + mTransaction = 0; +} + +ArrayImpl::ArrayImpl(DatabaseImpl* database, TransactionImpl* transaction) + : mRefCount(0) +{ + Init(); + AttachDatabaseImpl(database); + if (transaction != 0) AttachTransactionImpl(transaction); +} + +ArrayImpl::~ArrayImpl() +{ + try { if (mTransaction != 0) mTransaction->DetachArrayImpl(this); } + catch (...) {} + try { if (mDatabase != 0) mDatabase->DetachArrayImpl(this); } + catch (...) {} + try { if (mBuffer != 0) delete [] (char*)mBuffer; } + catch (...) {} +} + +// +// EOF +//