blob: c4b21c5efc2432a2b9e1b2c5f79c65f4a4cae37b [file] [log] [blame]
/**********************************************************************
// @@@ START COPYRIGHT @@@
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
// @@@ END COPYRIGHT @@@
**********************************************************************/
/* -*-C++-*-
*****************************************************************************
*
* File: ExpConvMxcs.cpp
* Description:
*
* Created: 8/30/2007
* Language: C++
*
*
*
*
*****************************************************************************
*/
#include <string.h>
#include <limits.h>
#include <windows.h> // added by ODBC
#ifdef NA_MXCS
#undef NA_LITTLE_ENDIAN
#else
#include "Platform.h"
#endif
//#if defined (NA_LITTLE_ENDIAN) || defined(NA_MXCS) // commented out by ODBC
#ifndef NSK_PLATFORM
#include <math.h>
#define MathPow(op1, op2, err) pow(op1, op2)
#else
//#ifdef NA_NSK
#include "ieeemath.h"
#endif //NA_NSK
#define EXE_CONVERT_STRING_ERROR -8413
#define EXE_NUMERIC_OVERFLOW -8411
//#define EXE_UNSIGNED_OVERFLOW -8432 // commented out by ODBC
#define EXE_STRING_OVERFLOW -8402
#define EXE_CONVERT_INTERVAL_ERROR -8422
#define SQL_SMALL_SIZE 2
#define SQL_INT_SIZE 4
#define SQL_LARGE_SIZE 8
#define REC_DATE_YEAR 1
#define REC_DATE_MONTH 2
#define REC_DATE_DAY 3
#define REC_DATE_HOUR 4
#define REC_DATE_MINUTE 5
#define REC_DATE_SECOND 6
// interval datatypes
#define REC_MIN_INTERVAL 195
#define REC_INT_YEAR 195
#define REC_INT_MONTH 196
#define REC_INT_YEAR_MONTH 197
#define REC_INT_DAY 198
#define REC_INT_HOUR 199
#define REC_INT_DAY_HOUR 200
#define REC_INT_MINUTE 201
#define REC_INT_HOUR_MINUTE 202
#define REC_INT_DAY_MINUTE 203
#define REC_INT_SECOND 204
#define REC_INT_MINUTE_SECOND 205
#define REC_INT_HOUR_SECOND 206
#define REC_INT_DAY_SECOND 207
#define REC_MAX_INTERVAL 208
#define REC_NUM_BIG_UNSIGNED 155
#define REC_NUM_BIG_SIGNED 156
#define MSB_CLR_MSK 0x7F
#define MSB_SET_MSK 0x80
#if defined (NA_LITTLE_ENDIAN) || defined(NA_MXCS)
typedef _int64 Int64;
#else // added by ODBC
typedef long long int Int64; // ditto
#endif // ditto
#ifndef NSK_PLATFORM // ditto
#define BIGN_CLR_SIGN(bignum, len) (((char*)bignum)[len - 1] &= MSB_CLR_MSK);
#define BIGN_SET_SIGN(bignum, len) (((char*)bignum)[len - 1] |= MSB_SET_MSK);
#define BIGN_GET_SIGN(bignum, len) \
((char)(((char*)bignum)[len - 1] & MSB_SET_MSK));
#else
//typedef long long int Int64; // commented out by ODBC
#define BIGN_CLR_SIGN(bignum, len) (((char*)bignum)[len - 2] &= MSB_CLR_MSK);
#define BIGN_SET_SIGN(bignum, len) (((char*)bignum)[len - 2] |= MSB_SET_MSK);
#define BIGN_GET_SIGN(bignum, len) \
((char)(((char*)bignum)[len - 2] & MSB_SET_MSK));
#endif
#ifndef NSK_PLATFORM
#ifndef LLONG_MAX
#define LLONG_MAX _I64_MAX
#endif
// The .NET 2003 headers changed the way LLONG_MIN is defined, which
// causes problems when LLONG_MIN is cast to a double. Previously
// LLONG_MIN was defined to I64_MIN, which works correctly. Redefine
// it here so things work correctly. This only affects NT builds.
#undef LLONG_MIN
#define LLONG_MIN _I64_MIN
#else
#ifndef LLONG_MAX
#define LLONG_MAX 9223372036854775807
#endif
#ifndef LLONG_MIN
#define LLONG_MIN (-9223372036854775807-1)
#endif
#endif
static const unsigned short powersOfTen[] = {10, // 10^1
100, // 10^2
1000, // 10^3
10000}; // 10^4
// The following method converts a given BCD string representation (without sign,
// and with the more significant decimal digits in the lower addresses)
// into its equivalent Big Num representation.
static short BigNumHelper_ConvBcdToBigNumHelper(long sourceLength,
long targetLength,
char * sourceData,
char * targetData)
{
// Recast from bytes to unsigned shorts.
long targetLengthInShorts = targetLength/2;
unsigned short * targetDataInShorts = (unsigned short *) targetData;
// Initialize the Big Num to zero.
int i = 0;
for (i = 0; i < targetLengthInShorts; i++)
targetDataInShorts[i] = 0;
long finalTargetLengthInShorts = 1;
// Ignore leading zeros in BCD. If all zeros, return.
long zeros = 0;
while (zeros < sourceLength && !sourceData[zeros])
zeros++;
if (zeros == sourceLength)
return 0;
int actualSourceLength = sourceLength - zeros;
char * actualSourceData = sourceData + zeros;
//#if defined (NA_LITTLE_ENDIAN) || defined(NA_MXCS) // commented out by ODBC
#ifndef NSK_PLATFORM // added by ODBC
union {
unsigned int temp;
struct {
unsigned short remainder;
unsigned short carry;
} tempParts;
};
#else
union {
unsigned int temp;
struct {
unsigned short carry;
unsigned short remainder;
} tempParts;
};
#endif
// Compute the Big Num as follows. First, chunk up the BCD
// string into chunks of 4-digit unsigned short numbers. Iterate
// over these chunks, and at each iteration, multiply the current
// Big Num by 10^4 and then add the numeric value of this chunk
// to it.
for (i = 0; i < actualSourceLength; i += 4) {
// Compute the numeric value of the next 4-digit chunk.
unsigned short temp1 = 0;
int j = 0;
while ((j < 4) && (i+j < actualSourceLength)) {
temp1 = temp1*10 + actualSourceData[i+j];
j++;
}
unsigned short power = powersOfTen[j - 1];
// Multiply the previous Big Num by 10^4 and add the value
// of the current chunk to it. It is more efficient to insert the
// multiplication and addition code here than to call the Helper()
// methods.
temp = ((unsigned long) targetDataInShorts[0]) * power + temp1;
targetDataInShorts[0] = tempParts.remainder;
for (j = 1; j < finalTargetLengthInShorts; j++) {
temp = ((unsigned long) targetDataInShorts[j]) * power + tempParts.carry;
targetDataInShorts[j] = tempParts.remainder;
}
if (tempParts.carry) {
if (finalTargetLengthInShorts >= targetLengthInShorts)
return -1;
finalTargetLengthInShorts++;
targetDataInShorts[finalTargetLengthInShorts - 1] = tempParts.carry;
}
}
return 0;
}
// The following method converts a given BCD string representation
// (with sign, and with the more significant decimal digits in the lower
// addresses) into its equivalent Big Num representation.
static short BigNumHelper_ConvBcdToBigNumWithSignHelper(long sourceLength,
long targetLength,
char * sourceData,
char * targetData)
{
short result = BigNumHelper_ConvBcdToBigNumHelper(sourceLength - 1,
targetLength,
sourceData + 1,
targetData);
// Set up sign.
if (sourceData[0] == '-')
BIGN_SET_SIGN(targetData, targetLength);
return result;
}
// The following method converts a given Big Num (with sign) into
// its equivalent BCD string representation (with the more significant
// decimal digits in the lower addresses).
// The following method converts a given Big Num (without sign) into
// its equivalent BCD string representation (with the more significant decimal
// digits in the lower addresses).
static short BigNumHelper_ConvBigNumToBcdHelper(long sourceLength,
long targetLength,
char * sourceData,
char * targetData)
{
// Recast from bytes to unsigned shorts.
long sourceLengthInShorts = sourceLength/2;
unsigned short * sourceDataInShorts = (unsigned short *) sourceData;
unsigned short * tempSourceDataInShorts = new unsigned short [sourceLength/2];
int i = 0;
for (i = 0; i < sourceLengthInShorts; i++)
tempSourceDataInShorts[i] = sourceDataInShorts[i];
// Initialize the BCD to zero.
for (i = 0; i < targetLength; i++)
targetData[i] = 0;
char * finalTargetData = targetData + targetLength - 1;
long finalTargetLength = 1;
// Ignore trailing zeros in the Big Num. If all zeros, return.
long actualSourceLengthInShorts = sourceLengthInShorts;
while (!tempSourceDataInShorts[actualSourceLengthInShorts - 1] && actualSourceLengthInShorts > 0)
actualSourceLengthInShorts--;
if (!actualSourceLengthInShorts) {
delete tempSourceDataInShorts;
return 0;
}
union {
//unsigned int temp; // commented out by ODBC
unsigned long temp; // added by ODBC
unsigned short temp1[2];
};
unsigned short remainder = 0;
// Compute the final BCD as follows. Keep dividing the Big Num by 10^4
// until the quotient becomes less than 10^4. At each iteration, compute
// the 4-digit BCD representation of the remainder, and append it to the
// left of the final BCD. For the final remainder, compute its BCD
// representation (which may be less than 4-digits) and append it to the
// left of the final BCD.
finalTargetData++;
finalTargetLength = 0;
while ((actualSourceLengthInShorts != 1) ||
(tempSourceDataInShorts[actualSourceLengthInShorts - 1] >= 10000)) {
// Divide the Big Num by 10^4. It is more efficient to insert
// the division code than to call SimpleDivideHelper();
int j = 0;
for (j = actualSourceLengthInShorts - 1; j >= 0; j--) {
//#if defined (NA_LITTLE_ENDIAN) || defined(NA_MXCS) //commented out by ODBC
#ifndef NSK_PLATFORM // added by ODBC
temp1[0] = tempSourceDataInShorts[j];
temp1[1] = remainder;
#else
temp1[1] = tempSourceDataInShorts[j];
temp1[0] = remainder;
#endif
tempSourceDataInShorts[j] = (unsigned short) (temp / 10000);
remainder = (unsigned short) (temp % 10000);
}
if (!tempSourceDataInShorts[actualSourceLengthInShorts - 1])
actualSourceLengthInShorts--;
// Compute the BCD representation of the remainder and append to the
// left of the final BCD.
for (j = 0; j < 4; j++) {
if (finalTargetLength >= targetLength) {
delete tempSourceDataInShorts;
return -1;
}
finalTargetData--;
finalTargetLength++;
finalTargetData[0] = remainder % 10;
remainder = remainder / 10;
}
}
// Compute the BCD representation of the final remainder and append to the
// left of the final BCD.
remainder = tempSourceDataInShorts[0];
while (remainder) {
if (finalTargetLength >= targetLength) {
delete tempSourceDataInShorts;
return -1;
}
finalTargetData--;
finalTargetLength++;
finalTargetData[0] = remainder % 10;
remainder = remainder / 10;
}
delete tempSourceDataInShorts;
return 0;
}
static short BigNumHelper_ConvBigNumWithSignToBcdHelper(long sourceLength,
long targetLength,
char * sourceData,
char * targetData)
{
char sign = BIGN_GET_SIGN(sourceData, sourceLength);
// Set up sign.
targetData[0] = (sign) ? '-' : '+';
// Temporarily clear source sign
BIGN_CLR_SIGN(sourceData, sourceLength);
short result = BigNumHelper_ConvBigNumToBcdHelper(sourceLength,
targetLength - 1,
sourceData,
targetData + 1);
// Restore sign
if (sign)
BIGN_SET_SIGN(sourceData, sourceLength);
return result;
}
static short convInt64ToDecMxcs(char *target, long targetLen, Int64 source)
{
unsigned long flags = 0;
bool negative = (source < 0);
long currPos = targetLen - 1;
char offset = '0';
if (negative && (currPos >= 0)) {
if (source == LLONG_MIN) {
// before we can convert this to a positive number, we have to
// work on the first digit. Otherwise, we would cause an overflow.
int temp = (int)(source % 10);
target[currPos--] = (char)('0' + (temp < 0 ? -temp: temp));
source /= 10;
}
// now make source positive
source = -source;
}
while (source != 0) {
if (currPos < 0) {
// target is not long enough - overflow
return EXE_STRING_OVERFLOW;
}
//#pragma nowarn(1506) // warning elimination
target[currPos--] = /*'0' + */(char)(source % 10); // NOTE - ODBC MUST not have '0'
//#pragma warn(1506) // warning elimination
source /= 10;
}
// zero pad the leading spaces
while (currPos >= 0)
target[currPos--] = '0' - offset;
if (negative)
target[0] |= 0200;
return 0;
}
static short safe_add_digit_to_double(double dvalue, // Original value
int digit, // Single digit to be added
double * result
)
{
*result = dvalue * 10 + digit;
return 0;
}
static short convAsciiToFloat64Mxcs(char * target, char *source, long sourceLen)
{
short err = 0;
double ptarget;
// skip leading and trailing blanks and adjust source and sourceLen
// accordingly
long i = 0;
for (; i < sourceLen && *source == ' '; i++)
source++;
if (i == sourceLen) {
// string contains only blanks.
return EXE_CONVERT_STRING_ERROR;
}
sourceLen -= i;
// we know that we found at least one non-blank character.
// so we just decrement sourceLen till we scanned all
// trailing blanks
while (source[sourceLen - 1] == ' ') {
sourceLen--;
}
enum State {START, SKIP_BLANKS, MANT_BEFORE_SIGN, MANT_AFTER_SIGN,
EXP_START, EXPONENT, xxERROR}; // ODBC changed ERROR to xxERROR
int cp = 0;
double mantissa = 0;
double exponent = 0;
bool negMantissa = false;
bool negExponent = false;
int numScale = 0;
bool validNum = false;
State state = START;
bool skipChar;
while (cp < sourceLen)
{
skipChar = false;
// cout << "state = " << state << endl;
switch (state)
{
case START:
if ((source[cp] == '+') || (source[cp] == '-'))
{
if (source[cp] == '-')
negMantissa = true;
state = SKIP_BLANKS;
skipChar = true;
}
else
state = MANT_BEFORE_SIGN;
break;
case SKIP_BLANKS:
if (source[cp] != ' ')
state = MANT_BEFORE_SIGN;
else
skipChar = true;
break;
case MANT_BEFORE_SIGN:
if (source[cp] == '.')
{
state = MANT_AFTER_SIGN;
}
else if ((source[cp] == 'E') || (source[cp] == 'e'))
{
validNum = false;
state = EXP_START;
}
else if ((source[cp] < '0') || (source[cp] > '9'))
state = xxERROR;
else
{
if (safe_add_digit_to_double(mantissa,
source[cp] - '0',
&mantissa) != 0)
{
return EXE_NUMERIC_OVERFLOW;
}
validNum = true;
}
skipChar = true;
break;
case MANT_AFTER_SIGN:
if ((source[cp] == 'E') || (source[cp] == 'e'))
{
if (!validNum)
state = xxERROR;
else
{
validNum = false;
state = EXP_START;
}
}
else if ((source[cp] < '0') || (source[cp] > '9'))
state = xxERROR;
else
{
numScale++;
if (safe_add_digit_to_double(mantissa,
source[cp] - '0',
&mantissa) != 0)
{
return EXE_NUMERIC_OVERFLOW;
}
validNum = true;
}
skipChar = true;
break;
case EXP_START:
if ((source[cp] == '+') || (source[cp] == '-'))
{
if (source[cp] == '-')
negExponent = true;
skipChar = true;
}
state = EXPONENT;
break;
case EXPONENT:
if ((source[cp] < '0') || (source[cp] > '9'))
state = xxERROR;
else
{
if (safe_add_digit_to_double(exponent,
source[cp] - '0',
&exponent) != 0)
{
return EXE_NUMERIC_OVERFLOW;
}
validNum = true;
}
skipChar = true;
break;
case xxERROR:
// string contains only blanks.
return EXE_CONVERT_STRING_ERROR;
break;
default:
state = xxERROR;
break;
} // switch state
if ((state != xxERROR) && (skipChar))
cp++;
} // while
if (!validNum)
{
return EXE_CONVERT_STRING_ERROR;
};
double scale = MathPow(10.0, (double)numScale, err);
if (err)
{
return EXE_NUMERIC_OVERFLOW;
}
if (negMantissa)
mantissa = - mantissa;
mantissa = mantissa / scale;
if (negExponent)
exponent = - exponent;
exponent = MathPow(10.0, exponent, err);
if (err)
{
return EXE_NUMERIC_OVERFLOW;
}
ptarget = mantissa * exponent;
if (target)
{
memcpy(target, (char*)&ptarget, sizeof(double));
}
// conversion was OK
return 0;
};
///////////////////////////////////////////////////////////////////
// function to convert an ASCII string to DEC or LARGEDEC
// the first character of a LARGEDEC is a sign and is ignored here.
// In fact, call has to pass in &target[1], targetLen - 1, and
// offset = '0', if the conversion is ASCII -> LARGEDEC.
// if conversion is ASCII -> DEC, caller has to pass
// &target[0], targetLen, and offset = 0.
// Also, this function always sets the sign bit for DEC if the
// source is negative. For LARGEDEC this has to be changed
// accordingly.
// The function assumes that source is at least
// sourceLen long. Trailing '\0' is not recongnized
///////////////////////////////////////////////////////////////////
static short convAsciiToDecMxcs(char *target,
long targetLen,
long targetScale,
char *source,
long sourceLen,
char offset,
BOOL *truncation) { // added by ODBC
short negative = 0; // default is a positive value
long sourceStart = 0; // start of source after skipping 0 and ' '
long sourceScale = 0; // by default the scale is 0
long targetPos = targetLen - 1; // current positon in the target
// skip leading blanks
while ((sourceStart < sourceLen) && (source[sourceStart] == ' '))
sourceStart++;
// only blanks found, error
if (sourceStart == sourceLen) {
return EXE_CONVERT_STRING_ERROR;
};
// skip trailing blanks. We know that we found already some
// non-blank character, thus sourceLen is always > sourceStart
while (source[sourceLen - 1] == ' ')
sourceLen--;
// if the string contains 'e' or 'E' it actually represents
// a float value. Do a ASCII -> DOUBLE -> DEC
// use a for-loop to find the 'e'. If we find one, we do the
// described conversion and return. If we do not find one,
// we go on with this function.
// Also, start at the end of the string, if we have an 'e' it
// is probably closer to the end.
for (long i = sourceLen - 1; i >= sourceStart; i--) {
if (source[i] == 'E' || source[i] == 'e') {
// found a float representation.
double intermediate;
if (convAsciiToFloat64Mxcs((char*)&intermediate,
&source[sourceStart],
sourceLen - sourceStart))
return EXE_CONVERT_STRING_ERROR;
// scale the intermediate
for (long j = 0; j < targetScale; j++) {
intermediate *= 10.0;
if ((intermediate < LLONG_MIN) || (intermediate > LLONG_MAX)) {
return EXE_NUMERIC_OVERFLOW;
};
}
if (convInt64ToDecMxcs(target,
targetLen,
(Int64) intermediate))
return EXE_CONVERT_STRING_ERROR;
return 0;
}
}
// the string does not represent a float value. Go on with
// the processing
long sourcePos = sourceLen - 1; // current position in the string
// check for sign
if (source[sourceStart] == '+')
sourceStart++;
else if (source[sourceStart] == '-') {
negative = 1;
sourceStart++;
};
// skip leading zeros
while((sourceStart < sourceLen) && (source[sourceStart] == '0'))
sourceStart++;
// only zeros found, target is 0
if (sourceStart == sourceLen) {
memset(target, '0' - offset, targetLen);
return 0;
};
// determine the scale of the source
long index = sourceStart;
long pointPos = -1;
while ((index < sourceLen) && pointPos < 0) {
if (source[index] == '.')
pointPos = index;
else
index++;
};
if (pointPos >= 0)
// determine sourceScale
sourceScale = sourceLen - pointPos - 1;
short raiseOverflowWarning = 0;
// procress digits, start from end
while ((sourcePos >= sourceStart) && (targetPos >= 0)) {
// add zeros to adjust scale
if (targetScale > sourceScale) {
target[targetPos--] = '0' - offset;
targetScale--;
}
else if (targetScale < sourceScale) {
// skip source digits
// but at least one of the digits skipped is not a '0', then
// a EXE_NUMERIC_OVERFLOW warning needs to be raised
if (source[sourcePos] != '0')
raiseOverflowWarning = 1;
sourcePos--;
sourceScale--;
}
else if (sourcePos == pointPos) {
// simply skip the decimal point
sourcePos--;
}
else {
// if source is not a digit, we have an error
if (source[sourcePos] < '0' || source[sourcePos] > '9') {
return EXE_CONVERT_STRING_ERROR;
};
// copy source to target
target[targetPos--] = source[sourcePos--] - offset;
};
};
if (raiseOverflowWarning) // added by ODBC
*truncation = TRUE; // added by ODBC
// ExRaiseSqlWarning(heap, diagsArea, EXE_NUMERIC_OVERFLOW);
// we might be right on the decimal point. Skip it before test for
// overflow
if (sourcePos == pointPos)
sourcePos--;
// if we couldn't copy all digits, we had an overflow
if (sourcePos >= sourceStart) {
return EXE_NUMERIC_OVERFLOW;
};
// left pad the target with zeros
while (targetPos >= 0)
target[targetPos--] = '0' - offset;
// add sign
if (negative)
target[0] |= 0200;
return 0;
};
//////////////////////////////////////////////////////////////////
// function to convert BIGNUM to LARGEDEC
///////////////////////////////////////////////////////////////////
static short convBigNumToLargeDecMxcs(char *target,
long targetLen,
char *source,
long sourceLen)
{
// Convert the Big Num (with sign) into a BCD representation by calling
// a BigNumHelper method
short retCode = BigNumHelper_ConvBigNumWithSignToBcdHelper(sourceLen,
targetLen,
source,
target);
if (retCode == -1) {
// target is not long enough - overflow
return EXE_NUMERIC_OVERFLOW;
};
return 0;
}
//////////////////////////////////////////////////////////////////
// function to convert a LARGEDEC to an ASCII string
// Trailing '\0' is not set!
//
// For fixed char target, left pad blanks if leftPad is true, or
// right pad blanks otherwise.
//
// For varchar target, left-justify string without blank pads
// to the right.
//
// convDoIt sets leftPad to true if callers pass in a
// CONV_UNKNOWN_LEFTPAD to convDoIt.
///////////////////////////////////////////////////////////////////
static short convLargeDecToAsciiMxcs(char *target,
long targetLen,
char *source,
long sourceLen,
long sourceScale,
char * varCharLen,
long varCharLenSize,
short leftPad)
{
if(source[0] < 2)
{
unsigned short realSource[256];
memcpy((char *)realSource, source, sourceLen);
if(realSource[0]) source[0] = '-';
else source[0] = '+';
int realLength = 1+(sourceLen-2)/5;
for(int srcPos=sourceLen-1; srcPos; srcPos--)
{
unsigned long r = 0;
for(int i=1; i<=realLength; i++)
{
unsigned long q = (realSource[i] + r) / 10;
r = (r + realSource[i]) - 10 * q;
realSource[i] = (short)q;
r <<= 16;
}
source[srcPos] = (char)(r >>= 16);
}
}
if (targetLen < sourceLen) targetLen = sourceLen; // added by ODBC
if (varCharLenSize)
leftPad = 0;
// skip leading zeros; start with index == 1; index == 0 is sign
// stop looking one position before the decimal point. This
// position is sourceLen - sourceScale - 1
long currPos = 1;
while ((currPos < (sourceLen - sourceScale - 1)) && source[currPos] == 0)
currPos++;
long padLen = targetLen;
if (source[0] == '-')
padLen--;
long requiredDigits = sourceLen - currPos - sourceScale;
padLen -= requiredDigits;
if (padLen < 0)
requiredDigits -= padLen;
if (sourceScale) {
if (padLen > 1) { // fraction exists
padLen--; // for decimal point
sourceScale = (sourceScale < padLen ? sourceScale : padLen);
padLen -= sourceScale;
}
else // no fraction
sourceScale = 0;
}
long targetPos = 0;
// if target is fixed length and leftPad, left pad blanks
// commented out by ODBC
/*
if (leftPad) {
for (; targetPos < padLen; targetPos++)
target[targetPos] = ' ';
}
*/
// commented out by ODBC
// fill in sign
if (source[0] == '-')
target[targetPos++] = '-';
// fill in digits
long i = 0;
for (i = 0; i < requiredDigits; i++, targetPos++, currPos++)
target[targetPos] = source[currPos] + '0';
// if we have a scale, add decimal point and some digits
if (sourceScale) {
target[targetPos++] = '.';
for (i = 0; i < sourceScale; i++, targetPos++, currPos++)
target[targetPos] = source[currPos] + '0';
};
// Right pad blanks for fixed char.
if (!leftPad && !varCharLenSize)
while (targetPos < targetLen)
target[targetPos++] = ' ';
// set length field for variable length targets
if (varCharLenSize) {
if (varCharLenSize == sizeof(long))
memcpy(varCharLen, (char *) &targetPos, sizeof(long));
else {
short VCLen = (short) targetPos;
memcpy(varCharLen, (char *) &VCLen, sizeof(short));
};
};
target[targetPos] = '\0'; // added by ODBC
// make sure that the source fits in the target
// we might skip some digits after the decimal point
// to make it fit
if (padLen < 0) {
// target string is not long enough - overflow
return EXE_STRING_OVERFLOW;
};
return 0;
};
//////////////////////////////////////////////////////////////////////////
// *********************************
//////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////
// function to convert an ASCII string to BIGNUM.
//
// First convert from ASCII to LARGEDEC, then convert from LARGEDEC
// to BIGNUM
///////////////////////////////////////////////////////////////////
static short convAsciiToBigNumMxcs(char *target,
long targetLen,
// long targetType, // commented out by ODBC
long targetPrecision,
long targetScale,
char *source,
long sourceLen,
BOOL *truncation) // added by ODBC
{
short retCode = 0;
// Convert from ASCII to an intermediate LARGEDEC, using the function convAsciiToDec().
// To understand this function call, it will be helpful to review the
// comments in its definition earlier.
char * intermediateLargeDec = new char[targetPrecision + 1];
retCode = convAsciiToDecMxcs(intermediateLargeDec + 1,
targetPrecision,
targetScale,
source,
sourceLen,
'0',
truncation); // added by ODBC
if (retCode)
{
delete intermediateLargeDec;
return retCode;
}
/* // commented out by ODBC
if ((targetType == REC_NUM_BIG_UNSIGNED) &&
(intermediateLargeDec[1] &0200))
{
delete intermediateLargeDec;
return EXE_UNSIGNED_OVERFLOW;
}
*/ // commented out by ODBC
// Set the sign byte in the intermediate LARGEDEC.
if (intermediateLargeDec[1] &0200) {
// reset the bit
intermediateLargeDec[1] &= 0177;
intermediateLargeDec[0] = '-';
}
else
intermediateLargeDec[0] = '+';
// Convert from the intermediate LARGEDEC to BIGNUM using a BigNumHelper method.
retCode = BigNumHelper_ConvBcdToBigNumWithSignHelper(targetPrecision + 1,
targetLen,
intermediateLargeDec,
target);
delete intermediateLargeDec;
if (retCode == -1) {
// target is not long enough - overflow
return EXE_NUMERIC_OVERFLOW;
};
return 0;
}
//////////////////////////////////////////////////////////////////
// function to convert a BIGNUM to an ASCII string
// Trailing '\0' is not set!
//
// This function first converts the BIGNUM to an intermediate
// LARGEDEC, then calls the previous function convLargeDecToAscii()
///////////////////////////////////////////////////////////////////
/*static*/ short convBigNumToAsciiMxcs(char *target,
long targetLen,
char *source,
long sourceLen,
long sourcePrecision,
long sourceScale)
{
char * intermediateLargeDec = new char[sourcePrecision + 1]; // one extra byte for the sign.
short retCode = convBigNumToLargeDecMxcs(intermediateLargeDec,
sourcePrecision + 1,
source,
sourceLen);
if (retCode)
return retCode;
retCode = convLargeDecToAsciiMxcs(target,
targetLen,
intermediateLargeDec,
sourcePrecision + 1,
sourceScale,
0, 0,
/* #ifdef NA_MXCS // commented out by ODBC
0); // ditto
#else */ // ditto
1);
//#endif // ditto
delete intermediateLargeDec;
return retCode;
};
static short getIntervalStartField(long datatype)
{
switch (datatype) {
case REC_INT_YEAR: return REC_DATE_YEAR;
case REC_INT_MONTH: return REC_DATE_MONTH;
case REC_INT_YEAR_MONTH: return REC_DATE_YEAR;
case REC_INT_DAY: return REC_DATE_DAY;
case REC_INT_HOUR: return REC_DATE_HOUR;
case REC_INT_DAY_HOUR: return REC_DATE_DAY;
case REC_INT_MINUTE: return REC_DATE_MINUTE;
case REC_INT_HOUR_MINUTE: return REC_DATE_HOUR;
case REC_INT_DAY_MINUTE: return REC_DATE_DAY;
case REC_INT_SECOND: return REC_DATE_SECOND;
case REC_INT_MINUTE_SECOND: return REC_DATE_MINUTE;
case REC_INT_HOUR_SECOND: return REC_DATE_HOUR;
case REC_INT_DAY_SECOND: return REC_DATE_DAY;
default:
break;
}
return REC_DATE_YEAR;
}
static short getIntervalEndField(long datatype)
{
switch (datatype) {
case REC_INT_YEAR: return REC_DATE_YEAR;
case REC_INT_MONTH: return REC_DATE_MONTH;
case REC_INT_YEAR_MONTH: return REC_DATE_MONTH;
case REC_INT_DAY: return REC_DATE_DAY;
case REC_INT_HOUR: return REC_DATE_HOUR;
case REC_INT_DAY_HOUR: return REC_DATE_HOUR;
case REC_INT_MINUTE: return REC_DATE_MINUTE;
case REC_INT_HOUR_MINUTE: return REC_DATE_MINUTE;
case REC_INT_DAY_MINUTE: return REC_DATE_MINUTE;
case REC_INT_SECOND: return REC_DATE_SECOND;
case REC_INT_MINUTE_SECOND: return REC_DATE_SECOND;
case REC_INT_HOUR_SECOND: return REC_DATE_SECOND;
case REC_INT_DAY_SECOND: return REC_DATE_SECOND;
default:
break;
}
return REC_DATE_YEAR;
}
//////////////////////////////////////////////////////////////////
// This function count the number of digits in a non-negative Int64.
// The function assumes that targetLen is 1 to 19, inclusive.
///////////////////////////////////////////////////////////////////
static long getDigitCount(Int64 value)
{
static const Int64 decValue[] = {0,
9,
99,
999,
9999,
99999,
999999,
9999999,
99999999,
999999999,
9999999999,
99999999999,
999999999999,
9999999999999,
99999999999999,
999999999999999,
9999999999999999,
99999999999999999,
999999999999999999};
for (int i = 4; i <= 16; i += 4)
if (value <= decValue[i]) {
if (value <= decValue[i-3])
return(i-3);
if (value <= decValue[i-2])
return(i-2);
if (value <= decValue[i-1])
return(i-1);
else return i;
}
if (value <= decValue[17])
return 17;
if (value <= decValue[18])
return 18;
return 19;
}
///////////////////////////////////////////////////////////////////
// function to convert interval field ASCII string to Int64
// The function reads at most sourceLen number of characters.
// Or until it encounters a non-digit character and sets the
// sourceLen to the actual length of the string read if it is
// smaller than sourceLen.
// This function is used for ASCII to INTERVAL conversions only.
// Blanks and sign are already removed.
///////////////////////////////////////////////////////////////////
static short convAsciiFieldToInt64Mxcs(Int64 &target,
long targetScale,
char *source,
long &sourceLen,
unsigned long flags)
{
/* // the function is commented out by ODBC
long currPos = 0; // current position in the string
target = 0; // result
while ((currPos < sourceLen) && ((source[currPos] >= '0') &&
(source[currPos] <= '9')))
{
short thisDigit = source[currPos] - '0';
if (target > (LLONG_MAX / 10))
{ // next power of 10 causes an overflow
return EXE_NUMERIC_OVERFLOW;
}
target *= 10;
if (target > LLONG_MAX - thisDigit)
{ // adding this digit causes an overflow
return EXE_NUMERIC_OVERFLOW;
}
target += thisDigit;
currPos++;
}
if (!currPos)
{
// No digits were found
return EXE_CONVERT_STRING_ERROR;
}
// return the number of digits read
sourceLen = currPos;
*/ // the function is commented out by ODBC
return 0;
};
///////////////////////////////////////////////////////////////////
// function to convert an ASCII string to an interval datatype.
///////////////////////////////////////////////////////////////////
static short convAsciiToIntervalMxcs(char *target,
long targetLen,
long targetDatatype,
long leadingPrecision,
long fractionPrecision,
char *source,
long sourceLen,
short allowSignInInterval,
unsigned long flags)
{
short retCode = 0;
// skip leading and trailing blanks and adjust source and sourceLen
// accordingly
while ((sourceLen > 0) && (*source == ' '))
{
source++;
sourceLen--;
}
if (!sourceLen)
{
// string contains no digits
return EXE_CONVERT_INTERVAL_ERROR;
};
short negInterval = 0;
if ((source[0] == '-') || (source[0] == '+')) {
if (! allowSignInInterval)
{
// string starts with a sign - not allowed
return EXE_CONVERT_INTERVAL_ERROR;
}
else
{
if (source[0] == '-')
negInterval = 1;
source += 1;
sourceLen -= 1;
}
}
// we know that we found at least one non-blank character.
// so we just decrement sourceLen till we scanned all
// trailing blanks
while (source[sourceLen - 1] == ' ')
sourceLen--;
char delimiter[] = "^-^:::";
unsigned short maxFieldValue[] = { 0, 11, 0, 23, 59, 59 };
short start = getIntervalStartField(targetDatatype);
short end = getIntervalEndField(targetDatatype);
// convert the first field
Int64 intermediate;
long fieldLen = leadingPrecision;
if (fieldLen > sourceLen)
fieldLen = sourceLen; // so we don't read garbage past end of string
retCode = convAsciiFieldToInt64Mxcs(intermediate,
0, // targetScale
source,
fieldLen,
flags);
if (retCode)
return retCode;
long strIndex = fieldLen;
Int64 fieldValue;
for (int field = start+1; field <= end; field++)
{
int index = field - REC_DATE_YEAR;
// Make sure there is still some string left before we look
// for the delimiter. The '+1' in the test also makes sure
// there is at least one character after the delimiter.
// Without the '+1', we get a strange string conversion
// error, EXE_CONVERT_STRING_ERROR, instead of the more
// reasonable interval conversion error,
// EXE_CONVERT_INTERVAL_ERROR, on interval strings like
// '20-'. Though interval strings like '20-hithere' will
// still get EXE_CONVERT_STRING_ERROR.
if (strIndex + 1 >= sourceLen)
{
return EXE_CONVERT_INTERVAL_ERROR;
}
// get the delimiter preceding this field
if (source[strIndex] != delimiter[index])
{
// HOUR can be preceded by either ':' or ' '
if ((field != REC_DATE_HOUR) || (source[strIndex] != ' '))
{
return EXE_CONVERT_INTERVAL_ERROR;
}
}
strIndex++; // step over delimiter
fieldLen = 2;
if (fieldLen > sourceLen - strIndex)
fieldLen = sourceLen - strIndex; // don't go off end of string
// convert the next field
retCode = convAsciiFieldToInt64Mxcs(fieldValue,
0, // targetScale
&source[strIndex],
fieldLen,
flags);
if (retCode)
return retCode;
if (fieldValue > maxFieldValue[index])
{
return EXE_CONVERT_INTERVAL_ERROR;
}
strIndex += fieldLen;
intermediate = intermediate * (maxFieldValue[index]+1) + fieldValue;
}
// adjust the value for fractionPrecision
for (short i = 0; i < fractionPrecision; i++)
{
intermediate *= 10;
}
// if the end field is SECOND, it may have a fractional part
if ((end == REC_DATE_SECOND) && (source[strIndex] == '.'))
{
long sourcePrecision = (sourceLen - strIndex - 1);
unsigned long fraction = 0;
if (sourcePrecision)
{
Int64 interm;
retCode = convAsciiFieldToInt64Mxcs(interm,
0,
&source[strIndex+1],
sourcePrecision, // sourceLen
flags);
if (retCode)
return retCode;
if (interm < 0)
{
return EXE_NUMERIC_OVERFLOW;
}
else if (interm > ULONG_MAX)
{
return EXE_NUMERIC_OVERFLOW;
}
else
{
*(unsigned int *)target = (unsigned long) interm;
}
};
// scale up or down if necessary
while (sourcePrecision < fractionPrecision)
{
fraction *= 10;
sourcePrecision++;
}
unsigned long oldFraction = fraction;
unsigned long scaleDownValue = 1;
while (sourcePrecision > fractionPrecision)
{
fraction /= 10;
scaleDownValue *= 10;
sourcePrecision--;
}
// Detect if meaningful scale truncation has occured. If so, raise an error
if (oldFraction > (fraction * scaleDownValue)) {
return EXE_CONVERT_INTERVAL_ERROR;
}
intermediate += fraction;
}
else
{
// check if the string is completely converted
if (strIndex != sourceLen)
{
return EXE_CONVERT_INTERVAL_ERROR;
}
}
switch (targetLen)
{
case SQL_SMALL_SIZE:
if ((intermediate < SHRT_MIN) || (intermediate > SHRT_MAX))
{
return EXE_CONVERT_INTERVAL_ERROR;
};
if (negInterval)
*(short *)target = -(short)intermediate;
else
*(short *)target = (short)intermediate;
break;
case SQL_INT_SIZE:
if ((intermediate < INT_MIN) || (intermediate > INT_MAX))
{
return EXE_CONVERT_INTERVAL_ERROR;
};
if (negInterval)
*(int *)target = -(int)intermediate;
else
*(int *)target = (int)intermediate;
break;
case SQL_LARGE_SIZE:
if (negInterval)
*(Int64 *)target = -intermediate;
else
*(Int64 *)target = intermediate;
break;
default:
return EXE_CONVERT_INTERVAL_ERROR;
}
return 0;
};
//////////////////////////////////////////////////////////////////
// function to convert an Int64 to an ASCII string
// Trailing '\0' is not set!
//
// For fixed char targets, left pad fillers if leftPad is
// true, or right pad fillers (default mode) otherwise.
//
// convDoIt sets leftPad to true if callers pass in a
// CONV_UNKNOWN_LEFTPAD to convDoIt.
//
// In only a few cases of fixed char, leftPad will be set to true.
// e.g., caller wants to convert 7 -> 07 as in month -> ascii
// or 8 -> ' 8' as in sqlci display
///////////////////////////////////////////////////////////////////
static short convInt64ToAsciiMxcs(char *target,
long targetLen,
Int64 source,
long scale,
char filler,
short leadingSign,
short leftPad)
{
long digitCnt = 0;
short negative = (source < 0);
short fixRightMost = 0; // true if need to fix the rightmost digit.
long padLen = targetLen;
long requiredDigits = 0;
long leftMost; // leftmost digit.
long rightMost; // rightmost digit.
long sign = 0;
// Int64 newSource = (negative ? -source : source);
Int64 newSource = 0;
if ((negative) && (source == 0x8000000000000000)) // = -2 ** 63
{
newSource = 0x7fffffffffffffff;
// 123456789012345
digitCnt = 19;
fixRightMost = 1;
}
else
{
newSource = (negative ? -source : source);
digitCnt = getDigitCount(newSource);
}
if (leadingSign || negative) {
sign = 1;
padLen--;
}
// No truncation allowed.
requiredDigits = digitCnt;
// Add extra zero's.
if (scale > requiredDigits)
requiredDigits += (scale - requiredDigits);
padLen -= requiredDigits;
if (scale)
padLen--; // decimal point
if (padLen < 0) {
// target string is not long enough - overflow
return EXE_STRING_OVERFLOW;
}
if (leftPad) {
leftMost = padLen + sign;
}
else {
leftMost = sign;
}
long currPos;
// Add filler.
rightMost = currPos = targetLen - 1;
if (padLen) {
long start;
if (leftPad) { // Pad to the left.
start = sign;
}
else { // Pad to the right
start = targetLen - padLen;
rightMost = currPos = start - 1;
}
memset(&target[start], filler, padLen);
}
// Convert the fraction part and add decimal point.
if (scale) {
long low = (currPos - scale);
for (; currPos > low; currPos--) {
target[currPos] = (char)((newSource % 10) + '0');
newSource /= 10;
}
target[currPos--] = '.';
}
// Convert the integer part.
for (; currPos >= leftMost; currPos--) {
target[currPos] = (char)((newSource % 10) + '0');
newSource /= 10;
}
// Add sign.
if (leadingSign) {
if (negative)
target[0] = '-';
else
target[0] = '+';
}
else if (negative)
target[currPos] = '-';
// Fix the rightmost digit for -2 ** 63.
if (fixRightMost && target[rightMost] == '7')
target[rightMost] = '8';
if (newSource != 0 || currPos < -1)
{ // Sanity check fails.
return EXE_STRING_OVERFLOW;
}
return 0;
};
///////////////////////////////////////////////////////////////////
// function to convert a INTERVAL to a string.
///////////////////////////////////////////////////////////////////
static short convIntervalToAsciiMxcs(char *source,
long sourceLen,
long leadingPrecision,
long fractionPrecision,
short sourceType,
char *inTarget,
long inTargetLen,
short leftPad)
{
short retCode = 0;
char delimiter[] = "^-^ ::";
unsigned short maxFieldValue[] = { 0, 11, 0, 23, 59, 59 };
long realTargetLen;
short startField = getIntervalStartField(sourceType);
short endField = getIntervalEndField(sourceType);
Int64 value;
switch (sourceLen)
{
case SQL_SMALL_SIZE:
{
short temp;
memcpy((char *) &temp, source, sourceLen);
value = temp;
break;
}
case SQL_INT_SIZE:
{
long temp;
memcpy((char *) &temp, source, sourceLen);
value = temp;
break;
}
case SQL_LARGE_SIZE:
memcpy((char *) &value, source, sourceLen);
break;
default:
return -1;
}
char sign = '+';
if (value < 0)
{
sign = '-';
value = -value;
}
// 1 for -ve sign, each field is 2 + delimiter
realTargetLen = (sign == '-' ? 1 : 0)
+ leadingPrecision + (endField - startField) * 3;
short targetIndex = (short) realTargetLen;
if (fractionPrecision)
realTargetLen += fractionPrecision + 1; // 1 for '.'
if (inTargetLen < realTargetLen)
{
return EXE_STRING_OVERFLOW;
}
long targetLen = inTargetLen;
char * target = inTarget;
if ((leftPad) && (targetLen > realTargetLen))
{
// left blankpad the target.
for (int i = 0; i < (targetLen - realTargetLen); i++)
{
target[i] = ' ';
}
// switch target and targetLen to be equal to realTargetLen.
target = (char *)((long)inTarget + (targetLen - realTargetLen));
targetLen = realTargetLen;
}
Int64 factor = 1;
Int64 fieldVal;
if (fractionPrecision)
{
// realTargetLen += fractionPrecision + 1; // 1 for '.'
for (unsigned i = fractionPrecision; i > 0; i--)
{
factor *= 10;
}
fieldVal = value;
value = value / factor;
fieldVal -= value * factor;
}
while (targetLen < realTargetLen)
{
// cut fraction till it fits. At this point we know that
// precision = 0 will fit
fieldVal /= 10;
fractionPrecision--;
realTargetLen--;
if (!fractionPrecision)
realTargetLen--; // remove '.'
};
// if target is fixed length, fill target string flushed right
for (int i = 0; i < (targetLen - realTargetLen); i++, target++)
*target = ' ';
// convert fraction part if any. targetIndex was set earlier
if (fractionPrecision)
{
targetIndex += 1; // 1 for '.'
memset(target+targetIndex, '0', fractionPrecision);
retCode = convInt64ToAsciiMxcs(target + targetIndex,
fractionPrecision, // targetLen
(Int64) fieldVal,
0, // scale,
'0', // filler character
0,
1); // leftPad
if (retCode)
return retCode;
targetIndex--;
target[targetIndex] = '.';
};
// Now rest of the fields except leading field...
for (int field = endField; field > startField; field--)
{
int index = field - REC_DATE_YEAR; // zero-based array index!
factor = (short)maxFieldValue[index]+1;
fieldVal = value;
value = value / factor;
fieldVal -= value * factor;
targetIndex -= 2;
retCode = convInt64ToAsciiMxcs(target + targetIndex,
2, // targetLen
(Int64) fieldVal,
0, // scale,
'0', // filler character
0,
1); // leftPad
if (retCode)
return retCode;
targetIndex--;
target[targetIndex] = delimiter[index];
};
// leading field
retCode = convInt64ToAsciiMxcs(target + (sign == '-' ? 1 : 0),
leadingPrecision, // targetLen
(Int64) value,
0, // scale,
' ', // filler character
0,
1); // leftPad
if (retCode)
return retCode;
if (sign == '-')
{
target[0] = sign;
}
// else
// {
// target[0] = ' ';
// }
return 0;
};
/////////////////////////////////////////////////////////////////////////////
//
// This method converts given 'source' to 'target' value.
//
// Following conversions are currently supported:
// Bignum signed or unsigned to fixed ascii string.
// Interval to fixed ascii string.
// Fixed ascii string to BigNum signed or unsigned.
// Fixed ascii string to Interval datatype.
//
// Conversion from sql data to user string for output:
// ===================================================
// source: pointer to buffer containing source value.
// sourceLen: length of data in 'source'
// sourceType: FS datatype of source value.
// FS datatypes are defined in sqlcli.h and are of the
// form: _SQLDT_*.
// These datatype are also returned after describe when
// SQLDESC_ITEM_ID of SQLDESC_TYPE_FS is used.
// sourcePrecision: Returned after describe:
// For numerics and chars:
// value returned when SQLDESC_PRECISION
// ITEM_ID is retrieved
// For intervals: value returned when SQLDESC_INT_LEAD_PREC
// ITEM_ID is retrieved
// sourceScale: Returned after describe:
// For numerics and chars:
// value returned when SQLDESC_SCALE
// ITEM_ID is retrieved
// For intervals: value returned when SQLDESC_PRECISION
// ITEM_ID is retrieved
//
// target: pointer to buffer where target will be moved in ascii form
// targetLen: length of target buffer. Must be big enough to hold the
// returned value in string format.
// For BigNums, targetLen = bignum precision + 1
// targetType: 0
// targetPrecision: 0
// targetScale: 0
//
// flags: 0
//
// Conversion from user string to sql data for input:
// ==================================================
// Save as the previous section with source replacing the target.
//
// ***************************************************************************
//
// MXCS/DBTR callers should set the define NA_MXCS
// before compiling this file.
//
// ***************************************************************************
// Return Code: 0, if conversion was successful.
// <sql-error-code>, in case of an error
//
// ***************************************************************************
//
// This file is maintained by SQL.
// DO NOT MAKE ANY CHANGES TO THIS FILE ON MXCS/DBTR END without updating
// the version that is checked in to SQL.
// Doing that will cause incompatibility and lost code.
//
///////////////////////////////////////////////////////////////////////////////
short convDoItMxcs(char * source,
long sourceLen,
short sourceType,
long sourcePrecision,
long sourceScale,
char * target,
long targetLen,
short targetType,
long targetPrecision,
long targetScale,
long flags,
BOOL *truncation) // added by ODBC
{
short retCode = -1;
if (sourceType == 0)
{
if ((targetType == REC_NUM_BIG_UNSIGNED) || (targetType == REC_NUM_BIG_SIGNED))
{
// ascii to bignum conversion
retCode = convAsciiToBigNumMxcs(target, targetLen,
// targetType, // commented out by ODBC
targetPrecision, targetScale,
source, sourceLen, truncation);
}
else if ((targetType >= REC_MIN_INTERVAL) &&
(targetType <= REC_MAX_INTERVAL))
{
// ascii to interval conversion
retCode = convAsciiToIntervalMxcs(target,
targetLen,
targetType,
targetPrecision,
targetScale,
source,
sourceLen,
1, //allowSignInInterval
0);
}
}
else if (targetType == 0)
{
if ((sourceType == REC_NUM_BIG_UNSIGNED) || (sourceType == REC_NUM_BIG_SIGNED))
{
retCode = convBigNumToAsciiMxcs(target, targetLen,
source, sourceLen,
sourcePrecision, sourceScale);
}
else if ((sourceType >= REC_MIN_INTERVAL) &&
(sourceType <= REC_MAX_INTERVAL))
{
retCode = convIntervalToAsciiMxcs(source,
sourceLen,
sourcePrecision,
sourceScale,
sourceType,
target,
targetLen,
1); //leftPad
}
}
return retCode;
}