blob: 74d6d7a43dfdac4923a0f0a89919e87ec4fc992f [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: IntervalType.C
* Description: Interval type
* Created: 2/27/96
* Language: C++
*
*
**************************************************************************
*/
// -----------------------------------------------------------------------
#include <ctype.h>
#include <limits.h>
#include "DatetimeType.h"
#include "IntervalType.h"
#include "NumericType.h"
#include "str.h"
#include "exp_clause_derived.h"
// ***********************************************************************
// Static data
// ***********************************************************************
static const Int32 IntervalFieldStringSize = 3; // punc plus two digits
static const UInt32 maxITVal[] = { 0, 11, 0, 23, 59, 59 };
static const char precedingITPunc[] = "^-^ ::";
// ^ is placeholder, as no punc precedes YEAR
// and no punc precedes DAY (no interval spans
// month and day).
// ***********************************************************************
//
// getIntervalFields returns the start and end fields for a given interval
// FS datatype. If the given FS datatype is invalid, the routine returns
// a status of -1. Otherwise, it returns a status of 0.
//
// ***********************************************************************
short getIntervalFields(Lng32 fsDatatype,
rec_datetime_field &startField,
rec_datetime_field &endField)
{
switch (fsDatatype) {
case REC_INT_YEAR:
startField = REC_DATE_YEAR;
endField = REC_DATE_YEAR;
break;
case REC_INT_MONTH:
startField = REC_DATE_MONTH;
endField = REC_DATE_MONTH;
break;
case REC_INT_YEAR_MONTH:
startField = REC_DATE_YEAR;
endField = REC_DATE_MONTH;
break;
case REC_INT_DAY:
startField = REC_DATE_DAY;
endField = REC_DATE_DAY;
break;
case REC_INT_HOUR:
startField = REC_DATE_HOUR;
endField = REC_DATE_HOUR;
break;
case REC_INT_DAY_HOUR:
startField = REC_DATE_DAY;
endField = REC_DATE_HOUR;
break;
case REC_INT_MINUTE:
startField = REC_DATE_MINUTE;
endField = REC_DATE_MINUTE;
break;
case REC_INT_HOUR_MINUTE:
startField = REC_DATE_HOUR;
endField = REC_DATE_MINUTE;
break;
case REC_INT_DAY_MINUTE:
startField = REC_DATE_DAY;
endField = REC_DATE_MINUTE;
break;
case REC_INT_SECOND:
startField = REC_DATE_SECOND;
endField = REC_DATE_SECOND;
break;
case REC_INT_MINUTE_SECOND:
startField = REC_DATE_MINUTE;
endField = REC_DATE_SECOND;
break;
case REC_INT_HOUR_SECOND:
startField = REC_DATE_HOUR;
endField = REC_DATE_SECOND;
break;
case REC_INT_DAY_SECOND:
startField = REC_DATE_DAY;
endField = REC_DATE_SECOND;
break;
case REC_INT_FRACTION:
startField = REC_DATE_FRACTION_MP;
endField = REC_DATE_FRACTION_MP;
break;
default:
return -1;
}
return 0;
} // getIntervalFields
// ***********************************************************************
//
// IntervalType : An ancillary static function, and the static class methods
//
// ***********************************************************************
static short getIntervalFSDatatype(rec_datetime_field startField,
rec_datetime_field endField)
{
short fsDatatype = -1;
if (startField > endField)
return fsDatatype;
switch (startField) {
case REC_DATE_YEAR:
if (endField == REC_DATE_YEAR)
fsDatatype = REC_INT_YEAR;
else if (endField == REC_DATE_MONTH)
fsDatatype = REC_INT_YEAR_MONTH;
break;
case REC_DATE_MONTH:
if (endField == REC_DATE_MONTH)
fsDatatype = REC_INT_MONTH;
break;
case REC_DATE_DAY:
if (endField == REC_DATE_DAY)
fsDatatype = REC_INT_DAY;
else if (endField == REC_DATE_HOUR)
fsDatatype = REC_INT_DAY_HOUR;
else if (endField == REC_DATE_MINUTE)
fsDatatype = REC_INT_DAY_MINUTE;
else if (endField == REC_DATE_SECOND)
fsDatatype = REC_INT_DAY_SECOND;
break;
case REC_DATE_HOUR:
if (endField == REC_DATE_HOUR)
fsDatatype = REC_INT_HOUR;
else if (endField == REC_DATE_MINUTE)
fsDatatype = REC_INT_HOUR_MINUTE;
else if (endField == REC_DATE_SECOND)
fsDatatype = REC_INT_HOUR_SECOND;
break;
case REC_DATE_MINUTE:
if (endField == REC_DATE_MINUTE)
fsDatatype = REC_INT_MINUTE;
else if (endField == REC_DATE_SECOND)
fsDatatype = REC_INT_MINUTE_SECOND;
break;
case REC_DATE_SECOND:
if (endField == REC_DATE_SECOND)
fsDatatype = REC_INT_SECOND;
break;
case REC_DATE_FRACTION_MP:
fsDatatype = REC_INT_FRACTION;
break;
default:
break;
}
return fsDatatype;
} // getIntervalFSDatatype ancillary function
NABoolean IntervalType::validate(rec_datetime_field startField,
UInt32 leadingPrecision,
rec_datetime_field endField,
UInt32 fractionPrecision)
{
NABoolean v = TRUE;
if (startField > endField) v = FALSE; // < and == are ok
if (leadingPrecision == 0 && startField < REC_DATE_SECOND) v = FALSE; // ANSI 10.1 SR 5
if (fractionPrecision != 0 && endField < REC_DATE_SECOND) v = FALSE;
if (leadingPrecision > SQLInterval::MAX_LEADING_PRECISION) v = FALSE;
if (fractionPrecision > SQLInterval::MAX_FRACTION_PRECISION) v = FALSE;
if (startField <= REC_DATE_MONTH && endField > REC_DATE_MONTH) v = FALSE;
return v;
} // IntervalType::validate
UInt32 IntervalType::getPrecision(rec_datetime_field startField,
UInt32 leadingPrecision,
rec_datetime_field endField,
UInt32 fractionPrecision)
{
// See also file ../sqludr/sqludr.cpp, method TypeInfo::TypeInfo,
// which implements this algorithm as well.
if (! IntervalType::validate(startField, leadingPrecision,
endField, fractionPrecision))
return 0;
UInt32 precision;
switch (getIntervalFSDatatype(startField, endField)) {
case REC_INT_YEAR:
case REC_INT_MONTH:
case REC_INT_DAY:
case REC_INT_HOUR:
case REC_INT_MINUTE:
case REC_INT_FRACTION:
precision = leadingPrecision;
break;
case REC_INT_YEAR_MONTH:
case REC_INT_DAY_HOUR:
case REC_INT_HOUR_MINUTE:
precision = leadingPrecision + 2;
break;
case REC_INT_DAY_MINUTE:
precision = leadingPrecision + 4;
break;
case REC_INT_SECOND:
precision = leadingPrecision + fractionPrecision;
break;
case REC_INT_MINUTE_SECOND:
precision = leadingPrecision + 2 + fractionPrecision;
break;
case REC_INT_HOUR_SECOND:
precision = leadingPrecision + 4 + fractionPrecision;
break;
case REC_INT_DAY_SECOND:
precision = leadingPrecision + 5 + fractionPrecision;
break;
default:
return 0;
}
return precision;
} // IntervalType::getPrecision
UInt32 IntervalType::computeLeadingPrecision(rec_datetime_field startField,
UInt32 precision,
rec_datetime_field endField,
UInt32 fractionPrecision)
{
UInt32 leadingPrecision;
switch (getIntervalFSDatatype(startField, endField)) {
case REC_INT_YEAR:
case REC_INT_MONTH:
case REC_INT_DAY:
case REC_INT_HOUR:
case REC_INT_MINUTE:
leadingPrecision = precision;
break;
case REC_INT_YEAR_MONTH:
case REC_INT_DAY_HOUR:
case REC_INT_HOUR_MINUTE:
leadingPrecision = precision - 2;
break;
case REC_INT_DAY_MINUTE:
leadingPrecision = precision - 4;
break;
case REC_INT_SECOND:
leadingPrecision = precision - fractionPrecision;
break;
case REC_INT_MINUTE_SECOND:
leadingPrecision = precision - 2 - fractionPrecision;
break;
case REC_INT_HOUR_SECOND:
leadingPrecision = precision - 4 - fractionPrecision;
break;
case REC_INT_DAY_SECOND:
leadingPrecision = precision - 5 - fractionPrecision;
break;
case REC_INT_FRACTION:
leadingPrecision = precision - fractionPrecision;
break;
default:
return 0;
}
return leadingPrecision;
} // IntervalType::getLeadingPrecision
Lng32 IntervalType::getStorageSize(rec_datetime_field startField,
UInt32 leadingPrecision,
rec_datetime_field endField,
UInt32 fractionPrecision)
{
Lng32 size = getBinaryStorageSize(getPrecision(startField,
leadingPrecision,
endField,
fractionPrecision));
// interval datatypes are stored as 2(smallint),4(int) or 8(largeint) bytes.
// If size is tinyint size based on precision, change it to smallint size.
if (size == SQL_TINY_SIZE)
size = SQL_SMALL_SIZE;
return size;
} // IntervalType::getStorageSize
size_t IntervalType::getStringSize(rec_datetime_field startField,
UInt32 leadingPrecision,
rec_datetime_field endField,
UInt32 fractionPrecision)
{
// 1 for sign, 1 for terminating '\0'
size_t result;
if (startField == REC_DATE_FRACTION_MP)
{
result = 1 + 1 + leadingPrecision;
}
else
{
result = 1 + 1 +
leadingPrecision +
IntervalFieldStringSize * (endField - startField);
if (fractionPrecision)
result += fractionPrecision + 1; // 1 for "."
}
return result;
} // IntervalType::getStringSize
// ***********************************************************************
//
// IntervalType : regular member functions
//
// ***********************************************************************
short IntervalType::getFSDatatype() const
{
return getIntervalFSDatatype(getStartField(), getEndField());
}
NAString IntervalType::getIntervalQualifierAsString() const
{
NAString fields;
fields += getFieldName(getStartField());
char precision[10];
if (getStartField() >= REC_DATE_SECOND) {
sprintf(precision, "(%u,%u)", getLeadingPrecision(),getFractionPrecision());
fields += precision;
} else if (getStartField() == getEndField()) {
sprintf(precision, "(%u)", getLeadingPrecision());
fields += precision;
} else {
sprintf(precision, "(%u) TO ", getLeadingPrecision());
fields += precision;
fields += getFieldName(getEndField());
if (getEndField() >= REC_DATE_SECOND) {
sprintf(precision, "(%u)", getFractionPrecision());
fields += precision;
}
}
return fields;
}
NAString IntervalType::getTypeSQLname(NABoolean terse) const
{
NAString name("INTERVAL ");
name += getIntervalQualifierAsString();
getTypeSQLnull(name, terse);
return name;
} // IntervalType::getTypeSQLname
const NAType* IntervalType::synthesizeType(enum NATypeSynthRuleEnum synthRule,
const NAType& operand1,
const NAType& operand2,
CollHeap* h,
UInt32 *flags) const
{
//
// The Interval is unsupported if the start field is FRACTION.
//
if (!operand1.isSupportedType() || !operand2.isSupportedType())
return NULL;
//
// If the second operand's type synthesis rules have higher precedence than
// this operand's rules, use the second operand's rules.
//
if (operand2.getSynthesisPrecedence() > getSynthesisPrecedence())
return operand2.synthesizeType(synthRule, operand1, operand2, h, flags);
//
// Legal: I+I, I-I, I/N, I*N, N*I. ANSI 6.15.
//
switch (synthRule) {
case SYNTH_RULE_ADD:
case SYNTH_RULE_SUB:
case SYNTH_RULE_UNION: {
if (operand1.getTypeQualifier() != NA_INTERVAL_TYPE) return NULL;
if (operand2.getTypeQualifier() != NA_INTERVAL_TYPE) return NULL;
if (! operand1.isCompatible(operand2)) return NULL;
const IntervalType& op1 = (IntervalType&)operand1;
const IntervalType& op2 = (IntervalType&)operand2;
rec_datetime_field startField = MINOF(op1.getStartField(),
op2.getStartField());
UInt32 leadingPrecision = MAXOF(op1.getLeadingPrecision(),
op2.getLeadingPrecision());
if ((synthRule == SYNTH_RULE_ADD) || (synthRule == SYNTH_RULE_SUB))
leadingPrecision = MINOF(leadingPrecision + 1,
SQLInterval::MAX_LEADING_PRECISION);
rec_datetime_field endField = MAXOF(op1.getEndField(), op2.getEndField());
UInt32 fractionPrecision = MAXOF(op1.getFractionPrecision(),
op2.getFractionPrecision());
UInt32 totalPrecision = getPrecision(startField,
leadingPrecision,
endField,
fractionPrecision);
if (totalPrecision > SQLInterval::MAX_LEADING_PRECISION)
leadingPrecision -= totalPrecision - SQLInterval::MAX_LEADING_PRECISION;
return new(h) SQLInterval(h,
op1.supportsSQLnull() || op2.supportsSQLnull(),
startField,
leadingPrecision,
endField,
fractionPrecision);
}
case SYNTH_RULE_DIV: {
if (operand1.getTypeQualifier() != NA_INTERVAL_TYPE) return NULL;
if (operand2.getTypeQualifier() != NA_NUMERIC_TYPE) return NULL;
if (! ((NumericType &) operand2).isExact()) return NULL;
const IntervalType& op1 = (IntervalType&)operand1;
const NumericType& op2 = (NumericType&)operand2;
UInt32 leadingPrecision = (op2.getScale() + op1.getLeadingPrecision());
UInt32 fractionPrecision = op1.getFractionPrecision();
if ((op1.getLeadingPrecision() < leadingPrecision) ||
(op2.supportsSQLnull() && !op1.supportsSQLnull()))
return new(h) SQLInterval (h, op1.supportsSQLnull() || op2.supportsSQLnull(),
op1.getStartField(),
leadingPrecision,
op1.getEndField(),
fractionPrecision);
else
return &operand1;
}
case SYNTH_RULE_MUL: {
const IntervalType* intervalOp;
const NumericType* numericOp;
switch (operand1.getTypeQualifier()) {
case NA_INTERVAL_TYPE:
if (operand2.getTypeQualifier() != NA_NUMERIC_TYPE) return NULL;
intervalOp = (IntervalType*) &operand1;
numericOp = (NumericType*) &operand2;
break;
case NA_NUMERIC_TYPE:
if (operand2.getTypeQualifier() != NA_INTERVAL_TYPE) return NULL;
numericOp = (NumericType*) &operand1;
intervalOp = (IntervalType*) &operand2;
break;
default:
return NULL;
}
if (! numericOp->isExact()) return NULL;
UInt32 leadingPrecision =
MINOF((UInt32) (intervalOp->getLeadingPrecision() +
(numericOp->getMagnitude() + 9) / 10),
SQLInterval::MAX_LEADING_PRECISION);
UInt32 totalPrecision = getPrecision(intervalOp->getStartField(),
leadingPrecision,
intervalOp->getEndField(),
intervalOp->getFractionPrecision());
if (totalPrecision > SQLInterval::MAX_LEADING_PRECISION)
leadingPrecision -= totalPrecision - SQLInterval::MAX_LEADING_PRECISION;
return new(h) SQLInterval(h,
intervalOp->supportsSQLnull() || numericOp->supportsSQLnull(),
intervalOp->getStartField(),
leadingPrecision,
intervalOp->getEndField(),
intervalOp->getFractionPrecision());
}
default:
return NULL;
} // switch
} // synthesizeType()
Lng32 IntervalType::getDisplayLength() const
{
//
// Return the string size excluding the null terminator.
//
return getStringSize() - 1;
}
// ***********************************************************************
// IntervalType : Min and max values, and encoding
// ***********************************************************************
void IntervalType::getRepresentableValue(char sign,
void* bufPtr, Lng32* bufLen,
NAString** stringLiteral,
CollHeap* h) const
{
size_t valueStringLen = getStringSize();
char* valueString = new char[valueStringLen];
char* v = valueString;
char digit;
if (sign == '0') {
sign = '+';
digit = '0';
} else {
*v++ = sign;
digit = '9';
}
Int32 i = getLeadingPrecision();
for (; i > 0; i--) *v++ = digit;
if (getStartField() != REC_DATE_FRACTION_MP) {
for (Int32 field = getStartField() + 1; field <= getEndField(); field++) {
Int32 index = field - REC_DATE_YEAR; // zero-based array index!
sprintf(v, "%c%02u", precedingITPunc[index],
digit == '0' ? 0 : maxITVal[index]);
v += IntervalFieldStringSize;
}
if (i = getFractionPrecision()) {
*v++ = '.';
for ( ; i > 0; i--) *v++ = digit;
}
}
*v = '\0';
i = (digit == '0') ? 0 : 1;
IntervalValue value(&valueString[i], getStartField(), getLeadingPrecision(),
getEndField(), getFractionPrecision(), sign);
assert(value.isValid());
if (bufPtr) {
assert(*bufLen >= value.getValueLen());
*bufLen = value.getValueLen();
str_cpy_all((char*)bufPtr, (char*)value.getValue(), value.getValueLen());
}
if (stringLiteral)
{
*stringLiteral = new (h) NAString("INTERVAL ", h);
if (sign == '-')
**stringLiteral += "-";
**stringLiteral += "\'";
if (sign == '-')
{
// skip the sign
**stringLiteral += &valueString[1];
}
else
**stringLiteral += valueString;
**stringLiteral += "\' ";
**stringLiteral += getIntervalQualifierAsString();
}
delete [] valueString;
}
void IntervalType::getZeroValue(void* bufPtr, Lng32* bufLen,
NAString** stringLiteral,
CollHeap* h) const
{
getRepresentableValue('0', bufPtr, bufLen, stringLiteral, h);
}
void IntervalType::minRepresentableValue(void* bufPtr, Lng32* bufLen,
NAString** stringLiteral,
CollHeap* h) const
{
getRepresentableValue('-', bufPtr, bufLen, stringLiteral, h);
}
void IntervalType::maxRepresentableValue(void* bufPtr, Lng32* bufLen,
NAString** stringLiteral,
CollHeap* h) const
{
getRepresentableValue('+', bufPtr, bufLen, stringLiteral,h);
}
NABoolean IntervalType::createSQLLiteral(const char * buf,
NAString *&stringLiteral,
NABoolean &isNull,
CollHeap *h) const
{
if (NAType::createSQLLiteral(buf, stringLiteral, isNull, h))
return TRUE;
// First step is to convert to ASCII, then add interval syntax around it
const int NUMVAL_LEN = 32;
char numVal[NUMVAL_LEN];
char *numValPtr = numVal;
const char *valPtr = buf + getSQLnullHdrSize();
NABoolean isNegative = FALSE;
unsigned short numValLen = 0;
ComDiagsArea *diags = NULL;
ex_expr::exp_return_type ok =
convDoIt((char *) valPtr,
getNominalSize(),
getFSDatatype(),
getPrecision(),
getFractionPrecision(),
numVal,
NUMVAL_LEN,
REC_BYTE_V_ASCII,
0,
SQLCHARSETCODE_UTF8,
(char *) &numValLen,
sizeof(numValLen),
h,
&diags,
ConvInstruction::CONV_UNKNOWN,
0);
if ( ok != ex_expr::EXPR_OK || numValLen == 0)
return FALSE;
// put the literal together like this:
// INTERVAL [-] '<value converted to ascii w/o sign>' <start field>[<precision>] [to <end-field>][<precision>]
stringLiteral = new(h) NAString("INTERVAL ", h);
// skip leading blanks, converted value is right-aligned
while (*numValPtr == ' ' && numValLen > 0)
{
numValPtr++;
numValLen--;
}
// determine the sign and skip blanks after the sign
if (numValLen && *numValPtr == '-')
{
isNegative = TRUE;
numValPtr++;
numValLen--;
while (*numValPtr == ' ' && numValLen > 0)
{
numValPtr++;
numValLen--;
}
}
if (numValLen <= 0)
return FALSE;
// add [-] '<value converted to ascii w/o sign>'
if (isNegative)
*stringLiteral += "- ' ";
else
*stringLiteral += "'";
stringLiteral->append(numValPtr, numValLen);
*stringLiteral += "' ";
// add start field
*stringLiteral += getFieldName(getStartField());
// add leading precision if not the default of 2
if (getStartField() != REC_DATE_SECOND)
{
if (getLeadingPrecision() != 2)
{
char lp[30];
snprintf(lp, 30, "(%d)", getLeadingPrecision());
*stringLiteral += lp;
}
}
else if (getLeadingPrecision() != 2 || getFractionPrecision() != 6)
{
// this is the form SECOND(<leading prec>, <fraction prec>)
char p[60];
snprintf(p, 60, "(%d, %d)", getLeadingPrecision(), getFractionPrecision());
*stringLiteral += p;
}
// add end field if different from start field
if (getEndField() != getStartField())
{
*stringLiteral += " TO ";
*stringLiteral += getFieldName(getEndField());
}
// add fraction precision for secnds if not the default of 6
if (getEndField() == REC_DATE_SECOND && getFractionPrecision() != 6)
{
char fp[30];
snprintf(fp, 30, "(%d)", getFractionPrecision());
*stringLiteral += fp;
}
return TRUE;
}
double IntervalType::encode (void* bufPtr) const
{
return -1; // for now ...##
} // IntervalType::encode
// ***********************************************************************
// IntervalType : Display/debug
// ***********************************************************************
void IntervalType::print(FILE* ofd, const char* indent) /*const*/
{
fprintf(ofd, "%s ", getTypeSQLname().data());
NAType::print(ofd, indent);
}
// ***********************************************************************
//
// IntervalValue : An interval value
//
// ***********************************************************************
IntervalValue::IntervalValue
( const char* strValue
, rec_datetime_field startField
, UInt32 leadingPrecision
, rec_datetime_field endField
, UInt32 fractionPrecision
, char sign
)
: value_(NULL)
, valueLen_(0)
{
if (sign != '+' && sign != '-')
return;
//
// Check that the qualifier is valid.
//
Lng32 storageSize = IntervalType::getStorageSize(startField,
leadingPrecision,
endField,
fractionPrecision);
if (storageSize == 0) {
SQLInterval triggerErrmsg(NULL, FALSE,
startField, leadingPrecision,
endField, fractionPrecision);
return;
}
//
// Skip leading blanks.
//
while (*strValue == ' ' || *strValue == '\t')
strValue++;
//
// Skip sign if present (e.g., strValue comes from getMin/MaxRepresentable()
// or from print()).
//
if (scanChar(strValue, sign))
while (*strValue == ' ' || *strValue == '\t')
strValue++;
//
// Scan the first field. The maximum number of digits allowed is the
// leading precision.
//
Int64 intervalValue=0; //initialize to zero fix Solution ID 10-020913-1668
if (leadingPrecision > 0)
{
if (! scanField(strValue, leadingPrecision, intervalValue))
return;
}
//
// Scan the remaining fields and the separators preceding them.
//
Lng32 fieldValue;
for (Int32 field = startField + 1; field <= endField; field++) {
Int32 index = field - REC_DATE_YEAR; // zero-based array index!
//
// Scan the punctuation preceding this field.
// We allow colon as well as space to separate days and hours.
//
if (! scanChar(strValue, precedingITPunc[index]))
if (field != REC_DATE_HOUR || (! scanChar(strValue, ':')))
return;
//
// Scan the field. It can have no more than two digits. Check that
// it doesn't exceed the maximum value for that field.
//
if ((! scanField(strValue, 2, fieldValue)) || (UInt32)fieldValue > maxITVal[index])
return;
intervalValue = (intervalValue * (maxITVal[index] + 1)) + fieldValue;
}
//
// If the end field is the seconds field, scan the fraction if present.
//
if (endField >= REC_DATE_SECOND && startField != REC_DATE_FRACTION_MP) {
if ((scanChar(strValue, '.') || (endField == REC_DATE_FRACTION_MP))
&& isdigit(*strValue)) {
const char* fraction = strValue;
if (! scanField(strValue, SQLInterval::MAX_FRACTION_PRECISION, fieldValue))
return;
//
// Convert the fraction to the precision specified in the qualifier.
//
UInt32 currentPrecision = strValue - fraction;
if (currentPrecision < fractionPrecision)
for ( ; currentPrecision++ < fractionPrecision; )
fieldValue *= 10;
else
if (currentPrecision > fractionPrecision) { // illegal
return;
}
} else
fieldValue = 0;
//
// Add the fraction to the interval value.
//
for (UInt32 i = 0; i < fractionPrecision; i++)
intervalValue *= 10;
intervalValue += fieldValue;
}
//
// If the sign is '-', negate the interval value.
//
if (sign == '-')
intervalValue = - intervalValue;
//
// Skip trailing blanks.
//
while (*strValue == ' ' || *strValue == '\t')
strValue++;
//
// If this is the end of the string, the interval value is valid.
//
if (*strValue == '\0')
setValue(intervalValue, storageSize);
} // IntervalValue ctor
IntervalValue::IntervalValue
( const char* value
, Lng32 storageSize
)
: value_(new unsigned char[storageSize])
, valueLen_((unsigned short) storageSize)
{
memcpy(value_, value, valueLen_);
} // IntervalValue ctor
NABoolean IntervalValue::scanField(const char* &strValue,
UInt32 maxFieldLen,
Lng32 &value) const
{
//
// Compute the value of the interval field.
//
value = 0;
UInt32 i = 0;
for (; i < maxFieldLen && isdigit(strValue[i]); i++)
value = value * 10 + (strValue[i] - '0');
//
// If the field has no digits or too many digits, the field is invalid.
//
if (i == 0 || isdigit(strValue[i]))
return FALSE;
strValue += i;
return TRUE;
}
NABoolean IntervalValue::scanField(const char* &strValue,
UInt32 maxFieldLen,
Int64 &value) const
{
//
// Compute the value of the interval field.
//
value = 0;
UInt32 i = 0;
for (; i < maxFieldLen && isdigit(strValue[i]); i++)
value = value * 10 + (strValue[i] - '0');
//
// If the field has no digits or too many digits, the field is invalid.
//
if (i == 0 || isdigit(strValue[i]))
return FALSE;
strValue += i;
return TRUE;
}
void IntervalValue::setValue(Int64 value, Lng32 valueLen)
{
assert(0 <= valueLen && valueLen <= USHRT_MAX);
valueLen_ = (unsigned short)valueLen;
value_ = new unsigned char[valueLen_];
// memcpy(value_, (char *) &value + 8 - valueLen_, valueLen_);
switch (valueLen_) {
case SQL_SMALL_SIZE: {
*(short *)value_ = (short)value;
break;
}
case SQL_INT_SIZE: {
*(Lng32 *)value_ = (Lng32)value;
break;
}
case SQL_LARGE_SIZE:
*(Int64 *)value_ = value;
break;
default:
;
}
}
static void valueToField(Int64& value, NAString& result,
char* buffer, UInt32 prec, UInt32 factor,
char punc)
{
Int64 tmpval = value;
Int64 tmpfactor = (Lng32) factor;
value = value / tmpfactor;
tmpval -= value * tmpfactor;
// unsigned long i = *((unsigned long *) &tmpval + 1);
ULng32 i = (ULng32)tmpval;
sprintf(buffer, "%c%0*u", punc, prec, i);
result.prepend(buffer);
}
NAString IntervalValue::getValueAsString(const IntervalType& dt) const
{
size_t resultlen = dt.getStringSize();
NAString result( (NASize_T) resultlen);
if (! isValid())
return result;
Int64 value;
switch (valueLen_) {
case SQL_SMALL_SIZE: {
short temp;
memcpy((char *) &temp, value_, valueLen_);
value = temp;
break;
}
case SQL_INT_SIZE: {
Lng32 temp;
memcpy((char *) &temp, value_, valueLen_);
value = temp;
break;
}
case SQL_LARGE_SIZE:
memcpy((char *) &value, value_, valueLen_);
break;
default:
return result;
}
char sign = '+';
if (value < 0) {
sign = '-';
value = -value;
}
// make a reasonable size result string
char buffer[25];
if (dt.getFractionPrecision()) {
UInt32 i, factor = 1;
for (i = dt.getFractionPrecision(); i; i--) factor *= 10;
valueToField(value, result, buffer, dt.getFractionPrecision(), factor, '.');
}
for (Int32 field = dt.getEndField(); field > dt.getStartField(); field--) {
Int32 index = field - REC_DATE_YEAR; // zero-based array index!
valueToField(value, result, buffer, IntervalFieldStringSize-1,
maxITVal[index] + 1, precedingITPunc[index]);
}
char leadingField[SQLInterval::MAX_LEADING_PRECISION + 1];
convertInt64ToAscii(value, leadingField);
sprintf(buffer, "%*s", dt.getLeadingPrecision(), leadingField);
result.prepend(buffer);
if (sign == '-')
result.prepend(&sign, 1);
return result;
}
void IntervalValue::print(const IntervalType& dt,
FILE* ofd, const char* indent) const
{
if (isValid())
fprintf(ofd, "%s\n", getValueAsString(dt).data());
else
fprintf(ofd, "%sInvalid %s\n", indent, dt.getTypeSQLname().data());
}
// ***********************************************************************
//
// SQLInterval : Encode
//
// ***********************************************************************
double SQLInterval::encode(void *bufPtr) const
{
char * valPtr = (char *)bufPtr;
double val = 0;
if (supportsSQLnull()) valPtr += getSQLnullHdrSize();
Lng32 size = getStorageSize(getStartField(),
getLeadingPrecision(),
getEndField(),
getFractionPrecision());
switch (size) {
case sizeof(short): {
short temp;
memcpy((char *) &temp, valPtr, sizeof(short));
val = temp;
break;
}
case sizeof(Int32): {
Int32 temp;
memcpy((char *) &temp, valPtr, sizeof(Int32));
val = temp;
break;
}
case sizeof(Int64): {
Int64 temp;
memcpy((char *) &temp, valPtr, sizeof(Int64));
val = convertInt64ToDouble(temp);
break;
}
default:
assert(FALSE);
}
return val;
}
// 10-031022-0617 -begin
// --------------------------------------------------------------
// Special implementation of encode, which depending upon the type
// of the interval. This converts the value into seconds. This should
// finally be merged with the above encode method.
// --------------------------------------------------------------
double SQLInterval::getValueInSeconds(double valInSecs)
{
// First get the double value using encode()
rec_datetime_field startField = getStartField();
unsigned short i = 0;
switch (startField) {
case REC_DATE_YEAR:
valInSecs = valInSecs * 365 * 24 * 60 * 60;
break;
case REC_DATE_MONTH:
valInSecs = valInSecs * 30 * 24 * 60 * 60;
break;
case REC_DATE_DAY:
valInSecs = valInSecs * 24 * 60 * 60;
break;
case REC_DATE_HOUR:
valInSecs = valInSecs * 60 * 60;
break;
case REC_DATE_MINUTE:
valInSecs = valInSecs * 60;
break;
case REC_DATE_SECOND:
for (i = 0; i < getFractionPrecision(); i++)
valInSecs /= 10;
break;
default:
break;
}
return valInSecs;
}
//10-031022-0617 -end