blob: 7e39da5a7dbcf5b714d6dc5373ae509891ac9919 [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: CliExpExchange.cpp
* Description: Move data in and out of user host variables
*
*
* Created: 7/10/95
* Language: C++
*
*
*
*
*****************************************************************************
*/
#include "Platform.h"
#ifdef _DEBUG
#include <assert.h>
#endif
#include "exp_stdh.h"
#include "exp_clause_derived.h"
#include "ex_stdh.h"
#include "cli_stdh.h"
#include "exp_datetime.h"
#include "exp_interval.h"
#include "exp_expr.h"
#include "ExRLE.h"
// defined in exp_eval.C
void computeDataPtr(char * start_data_ptr, // start of data row
Lng32 field_num, // field number whose address is to be
// computed. Zero based.
ExpTupleDesc * td,// describes this row
char ** opdata,
char ** nulldata,
char ** varlendata);
static Lng32 getIntervalCode(short datatype)
{
switch (datatype) {
case REC_INT_YEAR: return SQLINTCODE_YEAR;
case REC_INT_MONTH: return SQLINTCODE_MONTH;
case REC_INT_YEAR_MONTH: return SQLINTCODE_YEAR_MONTH;
case REC_INT_DAY: return SQLINTCODE_DAY;
case REC_INT_HOUR: return SQLINTCODE_HOUR;
case REC_INT_DAY_HOUR: return SQLINTCODE_DAY_HOUR;
case REC_INT_MINUTE: return SQLINTCODE_MINUTE;
case REC_INT_HOUR_MINUTE: return SQLINTCODE_HOUR_MINUTE;
case REC_INT_DAY_MINUTE: return SQLINTCODE_DAY_MINUTE;
case REC_INT_SECOND: return SQLINTCODE_SECOND;
case REC_INT_MINUTE_SECOND: return SQLINTCODE_MINUTE_SECOND;
case REC_INT_HOUR_SECOND: return SQLINTCODE_HOUR_SECOND;
case REC_INT_DAY_SECOND: return SQLINTCODE_DAY_SECOND;
default:
break;
}
return 0;
}
static void setRowNumberInCli(ComDiagsArea * diagsArea, Lng32 rowNum, Lng32 rowsetSize)
{
if ((rowsetSize > 0) && diagsArea) {
diagsArea->setAllRowNumber(rowNum);
}
}
ex_expr::exp_return_type InputOutputExpr::describeOutput(void * output_desc_,
UInt32 flags)
{
NABoolean isOdbc = isODBC(flags);
NABoolean isDbtr = isDBTR(flags);
NABoolean isIFIO = isInternalFormatIO(flags);
ex_clause *clause = getClauses();
Descriptor * output_desc = (Descriptor *)output_desc_;
short entry = 0;
#ifdef _DEBUG
short tempAtp = -1; // For testing
#endif
Lng32 prevEntryStartOffset = 0;
Lng32 currAlignedOffset = 0;
Lng32 firstEntryStartAlignment = 0;
Lng32 length = -1;
NABoolean firstEntryProcessed = FALSE;
while (clause)
{
if (clause->getType() == ex_clause::INOUT_TYPE)
{
Attributes * operand = clause->getOperand(0);
if(isCall() && output_desc->isDescTypeWide())
entry = ((ex_inout_clause *)clause)->getParamIdx();
else
entry++;
output_desc->setDescItem(entry, SQLDESC_TYPE_FS, operand->getDatatype(),
0);
length = operand->getLength();
if ((operand->getDatatype() >= REC_MIN_INTERVAL) &&
(operand->getDatatype() <= REC_MAX_INTERVAL)) {
//
// According to ANSI, interval info is stored in the following
// descriptor items:
//
// DATETIME_INTERVAL_CODE = qualifier code
// PRECISION = fractional precision
// DATETIME_INTERVAL_PRECISION = leading precision
//
output_desc->setDescItem(entry,
SQLDESC_DATETIME_CODE,
getIntervalCode(operand->getDatatype()),
0);
output_desc->setDescItem(entry,
SQLDESC_PRECISION,
operand->getScale(),
0);
output_desc->setDescItem(entry, SQLDESC_SCALE, 0, 0);
output_desc->setDescItem(entry,
SQLDESC_INT_LEAD_PREC,
operand->getPrecision(),
0);
if (NOT isIFIO)
{
Lng32 displaySize =
ExpInterval::getDisplaySize(operand->getDatatype(),
operand->getPrecision(),
operand->getScale());
length = displaySize;
}
} else if (operand->getDatatype() == REC_DATETIME) {
//
// According to ANSI, datetime info is stored in the following
// descriptor items:
//
// DATETIME_INTERVAL_CODE = qualifier code
// PRECISION = fractional precision
//
output_desc->setDescItem(entry,
SQLDESC_DATETIME_CODE,
operand->getPrecision(),
0);
output_desc->setDescItem(entry,
SQLDESC_PRECISION,
operand->getScale(),
0);
output_desc->setDescItem(entry, SQLDESC_SCALE, 0, 0);
output_desc->setDescItem(entry, SQLDESC_INT_LEAD_PREC, 0, 0);
if (((NOT isOdbc) || (output_desc->rowwiseRowsetV2() == FALSE)) &&
(NOT isIFIO))
{
Lng32 displaySize =
ExpDatetime::getDisplaySize(operand->getPrecision(),
operand->getScale());
length = displaySize;
}
} else {
output_desc->setDescItem(entry, SQLDESC_DATETIME_CODE, 0, 0);
output_desc->setDescItem(entry,
SQLDESC_PRECISION,
operand->getPrecision(),
0);
output_desc->setDescItem(entry,
SQLDESC_SCALE,
operand->getScale(),
0);
output_desc->setDescItem(entry, SQLDESC_INT_LEAD_PREC, 0, 0);
}
// Use SQLDESC_CHAR_SET_NAM (one-part name) for charset
if ( DFS2REC::isAnyCharacter(operand->getDatatype()) ||
(operand->getDatatype() == REC_BLOB) ||
(operand->getDatatype() == REC_CLOB ))
{
output_desc->setDescItem(entry, SQLDESC_CHAR_SET_NAM, 0,
(char*)CharInfo::getCharSetName(operand->getCharSet()));
// reset the length for Unicode
if ( operand->getCharSet() == CharInfo::UNICODE ||
CharInfo::is_NCHAR_MP(operand->getCharSet())
)
{
length = operand->getLength()/SQL_DBCHAR_SIZE;
}
if (operand->isCaseinsensitive())
output_desc->setDescItem(entry, SQLDESC_CASEINSENSITIVE, 1, 0);
if (operand->getCollation() != CharInfo::DefaultCollation)
output_desc->setDescItem(entry, SQLDESC_COLLATION,
operand->getCollation(), 0);
}
output_desc->setDescItem(entry, SQLDESC_NULLABLE,
operand->getNullFlag(), 0);
output_desc->setDescItem(entry, SQLDESC_NAME, 0,
((ex_inout_clause *)clause)->getName());
if (((ex_inout_clause *)clause)->getHeading() != 0)
output_desc->setDescItem(entry, SQLDESC_HEADING, 0,
((ex_inout_clause *)clause)->getHeading());
if (((ex_inout_clause *)clause)->getTableName() != 0)
output_desc->setDescItem(entry, SQLDESC_TABLE_NAME, 0,
((ex_inout_clause *)clause)->getTableName());
if (((ex_inout_clause *)clause)->getSchemaName() != 0)
output_desc->setDescItem(entry, SQLDESC_SCHEMA_NAME, 0,
((ex_inout_clause *)clause)->getSchemaName());
if (((ex_inout_clause *)clause)->getCatalogName() != 0)
output_desc->setDescItem(entry, SQLDESC_CATALOG_NAME, 0,
((ex_inout_clause *)clause)->getCatalogName());
output_desc->setDescItem(entry, SQLDESC_VAR_PTR, 0, 0);
output_desc->setDescItem(entry, SQLDESC_IND_PTR, 0, 0);
output_desc->setDescItem(entry, SQLDESC_VAR_DATA, 0, 0);
output_desc->setDescItem(entry, SQLDESC_IND_DATA, 0, 0);
if (operand->getRowsetSize() > 0) {
output_desc->setDescItem(0, SQLDESC_ROWSET_SIZE,
operand->getRowsetSize(), 0);
output_desc->setDescItem(entry, SQLDESC_ROWSET_VAR_LAYOUT_SIZE,
operand->getLength(), 0);
output_desc->setDescItem(entry, SQLDESC_ROWSET_IND_LAYOUT_SIZE,
operand->getNullIndicatorLength(),
0);
}
else {
output_desc->setDescItem(0, SQLDESC_ROWSET_SIZE,
0, 0);
output_desc->setDescItem(entry, SQLDESC_ROWSET_VAR_LAYOUT_SIZE,
0, 0);
output_desc->setDescItem(entry, SQLDESC_ROWSET_IND_LAYOUT_SIZE,
0,
0);
}
short paramMode = ((ex_inout_clause *)clause)->getParamMode();
short paramIdx = ((ex_inout_clause *)clause)->getParamIdx();
short ordPos = ((ex_inout_clause *)clause)->getOrdPos();
BriefAssertion((paramMode == PARAMETER_MODE_INOUT) ||
(paramMode == PARAMETER_MODE_OUT)||
(paramMode == PARAMETER_MODE_UNDEFINED),
"invalid param mode");
BriefAssertion(paramIdx >= 0, "invalid param index");
BriefAssertion(ordPos >= 0, "invalid param ordinal position");
// handle old plan
if(paramMode == PARAMETER_MODE_UNDEFINED){
paramMode = PARAMETER_MODE_OUT;
}
output_desc->setDescItem(entry, SQLDESC_PARAMETER_MODE, paramMode, 0);
output_desc->setDescItem(entry, SQLDESC_PARAMETER_INDEX, paramIdx, 0);
output_desc->setDescItem(entry, SQLDESC_ORDINAL_POSITION, ordPos, 0);
if(isCall()){
short paramMode = ((ex_inout_clause *)clause)->getParamMode();
short paramIdx = ((ex_inout_clause *)clause)->getParamIdx();
short ordPos = ((ex_inout_clause *)clause)->getOrdPos();
output_desc->setDescItem(entry, SQLDESC_PARAMETER_MODE,
paramMode, 0);
output_desc->setDescItem(entry, SQLDESC_PARAMETER_INDEX,
paramIdx, 0);
output_desc->setDescItem(entry, SQLDESC_ORDINAL_POSITION,
ordPos, 0);
}
else{
output_desc->setDescItem(entry, SQLDESC_PARAMETER_MODE,
PARAMETER_MODE_OUT, 0);
output_desc->setDescItem(entry, SQLDESC_PARAMETER_INDEX,
entry, 0);
output_desc->setDescItem(entry, SQLDESC_ORDINAL_POSITION,
0, 0);
}
output_desc->setDescItem(entry, SQLDESC_LENGTH, length, 0);
// Set up length of null indicator and varcharlen indicator values
// in the descriptor.
//
output_desc->setDescItem(entry, SQLDESC_IND_LENGTH,
operand->getNullIndicatorLength(), 0);
output_desc->setDescItem(entry, SQLDESC_VC_IND_LENGTH,
operand->getVCIndicatorLength(), 0);
// Get offsets to data, null indicator and varchar len indicator
// in the actual row and set them in the descriptor.
//
// This is being done for sqlark_exploded_format only.
// The offsets that are set in descriptor assume that the returned
// row to user is a single contiguous aligned row even though internally
// that row could be represented by multiple fragments, each with
// different atp index.
//
// These values are used by caller(mxcs) to set up input and output
// data pointers so that they are aligned and set up the same way
// as the actual row in cli.
//
// These values cannot be set by external callers.
//
Lng32 dataOffset = -1;
Lng32 nullIndOffset = -1;
Lng32 currEntryStartOffset = -1;
Lng32 currEntryStartAlignment = -1;
Lng32 outputDatalen = -1;
if (output_desc->getDescItem(entry, SQLDESC_OCTET_LENGTH, &outputDatalen, NULL, 0, NULL, 0, NULL, 0) == ERROR)
return ex_expr::EXPR_ERROR;
Lng32 alignment = 1;
if (operand->getNullIndOffset() >= 0)
{
if (operand->getTupleFormat() == ExpTupleDesc::SQLARK_EXPLODED_FORMAT)
alignment = operand->getNullIndicatorLength();
currAlignedOffset = ADJUST(currAlignedOffset, alignment);
nullIndOffset = currAlignedOffset;
currEntryStartOffset = currAlignedOffset;
currEntryStartAlignment = alignment;
currAlignedOffset += operand->getNullIndicatorLength();
}
if (operand->getVCLenIndOffset() >= 0)
{
if (operand->getTupleFormat() == ExpTupleDesc::SQLARK_EXPLODED_FORMAT)
alignment = operand->getVCIndicatorLength();
currAlignedOffset = ADJUST(currAlignedOffset, alignment);
dataOffset = currAlignedOffset;
if (currEntryStartOffset == -1)
{
currEntryStartOffset = currAlignedOffset;
currEntryStartAlignment = alignment;
}
currAlignedOffset += operand->getVCIndicatorLength() + outputDatalen;
}
else
{
if (operand->getTupleFormat() == ExpTupleDesc::SQLARK_EXPLODED_FORMAT)
alignment = operand->getDataAlignmentSize();
currAlignedOffset = ADJUST(currAlignedOffset, alignment);
dataOffset = currAlignedOffset;
if (currEntryStartOffset == -1)
{
currEntryStartOffset = currAlignedOffset;
currEntryStartAlignment = alignment;
}
currAlignedOffset += outputDatalen;
}
if (NOT firstEntryProcessed)
{
firstEntryProcessed = TRUE;
firstEntryStartAlignment = currEntryStartAlignment;
}
output_desc->setDescItemInternal(entry, SQLDESC_DATA_OFFSET,
dataOffset, 0);
output_desc->setDescItemInternal(entry, SQLDESC_NULL_IND_OFFSET,
nullIndOffset, 0);
output_desc->setDescItemInternal(entry-1, SQLDESC_ALIGNED_LENGTH,
currEntryStartOffset - prevEntryStartOffset, 0);
prevEntryStartOffset = currEntryStartOffset;
#ifdef _DEBUG
// Start testing logic *****
//
// Set up the descriptor rowwise offsets to enable bulk move when possible.
// The offset is only valid for one tuple. Set the values to -1 for all
// the other tuples.
//
if (tempAtp == -1)
tempAtp = operand->getAtpIndex();
if (tempAtp == operand->getAtpIndex()){
output_desc->setDescItem(entry, SQLDESC_ROWWISE_VAR_OFFSET, operand->getOffset(), 0);
output_desc->setDescItem(entry, SQLDESC_ROWWISE_IND_OFFSET, operand->getNullIndOffset(), 0);
}
else{
output_desc->setDescItem(entry, SQLDESC_ROWWISE_VAR_OFFSET, -1, 0);
output_desc->setDescItem(entry, SQLDESC_ROWWISE_IND_OFFSET, -1, 0);
}
// End testing logic *****
#endif
}
clause = clause->getNextClause();
}
if (firstEntryProcessed)
{
currAlignedOffset = ADJUST(currAlignedOffset, firstEntryStartAlignment);
output_desc->setDescItemInternal(entry, SQLDESC_ALIGNED_LENGTH,
currAlignedOffset - prevEntryStartOffset, 0);
}
if(!(isCall() && output_desc->isDescTypeWide())
|| (entry > output_desc->getUsedEntryCount()))
output_desc->setUsedEntryCount(entry);
return ex_expr::EXPR_OK;
}
//
// This method will evaluate the characteristics of the operand and descriptor
// to determine if a value can be bulk moved given rowwise rowset version 1
// bulk move rules. The method also has the side affect of setting the varPtr
// parameter to the proper location in the descriptor's data buffer for this
// items bulk move start address.
//
Descriptor::BulkMoveStatus Descriptor::checkBulkMoveStatusV1(
short entry, Attributes * op,
Long &varPtr,
NABoolean isInputDesc,
NABoolean isRWRS,
NABoolean isInternalFormatIO)
{
#ifdef _DEBUG
// if (! getenv("DOSLOWBULKMOVE"))
// return BULK_MOVE_OFF;
if (getenv("BULKMOVEOFF"))
return BULK_MOVE_OFF;
else if (getenv("BULKMOVEDISALLOWED"))
return BULK_MOVE_DISALLOWED;
#endif
desc_struct &descItem = desc[entry - 1]; // Zero base
// if any of the first set of conditions is true, bulk move cannot
// be done for any value of this row. Turn off bulk move completely.
if ((rowsetSize > 0 ) ||
// ((NOT rowwiseRowset()) &&
(descItem.var_ptr == 0) ||
(op->getVCIndicatorLength() > 0) ||
(DFS2REC::isAnyVarChar(op->getDatatype())) ||
(((DFS2REC::isDateTime(op->getDatatype())) ||
(DFS2REC::isInterval(op->getDatatype()))) &&
(NOT isInternalFormatIO)) ||
(op->isAddedCol()) ||
(!op->isBulkMoveable())
)
return BULK_MOVE_OFF;
// if any of these conditions are true, the bulk move for this
// value cannot be done. Turn off bulk move for this value.
//
// If descriptor item is binary with precision > 0, which means that
// it was declared as a NUMERIC datatype, then it needs to
// be validated at input and output time. Cannot do bulk move in
// that case.
// At input, always validate it.
// At output, only validate if descItem's precision is greater than
// zero, and less than operand's precision or op's precision is zero.
//
// Bulk move for char datatype is only done if both source and
// target are single byte charsets and the target doesn't enforce
// character limits.
else if ((descItem.datatype != op->getDatatype()) ||
((DFS2REC::isAnyCharacter(op->getDatatype())) &&
((descItem.charset != op->getCharSet()) ||
(NOT CharInfo::isSingleByteCharSet((CharInfo::CharSet)descItem.charset)) ||
op->getPrecision() /*char limit*/ > 0)) ||
((NOT DFS2REC::isAnyCharacter(op->getDatatype())) &&
(descItem.scale != op->getScale())) ||
(descItem.length != op->getLength()) ||
((descItem.datatype >= REC_MIN_BINARY) &&
(descItem.datatype <= REC_MAX_BINARY) &&
(((isInputDesc) &&
(descItem.precision > 0)) ||
((NOT isInputDesc) &&
(descItem.precision > 0) &&
((op->getPrecision() == 0) ||
(op->getPrecision() > descItem.precision))))) ||
(op->getNullFlag()) ||
(descItem.nullable != 0))
return BULK_MOVE_DISALLOWED;
// bulk move could be done for this value.
else
{
varPtr = descItem.var_ptr;
return BULK_MOVE_OK;
}
} // end Descriptor::checkBulkMoveStatusV1
//
// This method will return the proper location in the
// descriptor's data buffer for this entry based on
//
Long Descriptor::getRowsetVarPtr(short entry)
{
Long varPtr = 0;
desc_struct &descItem = desc[entry - 1]; // Zero base
//
// Set the varPtr to the appropriate place.
//
if (descItem.nullable != 0)
varPtr = descItem.ind_ptr;
else
varPtr = descItem.var_ptr;
return varPtr;
} // end getRowsetVarPtr
//
// This method will evaluate the characteristics of the operand and descriptor
// to determine if a value can be bulk moved given rowwise rowset version 2
// bulk move rules. The method also has the side affect of setting the varPtr
// parameter to the proper location in the descriptor's data buffer for this
// items bulk move start address.
//
Descriptor::BulkMoveStatus Descriptor::checkBulkMoveStatusV2(
short entry, Attributes * op,
Long &varPtr,
NABoolean isInputDesc,
NABoolean isOdbc,
NABoolean isRWRS,
NABoolean isInternalFormatIO)
{
#ifdef _DEBUG
// if (! getenv("DOSLOWBULKMOVE"))
// return BULK_MOVE_OFF;
if (getenv("BULKMOVEOFF"))
return BULK_MOVE_OFF;
else if (getenv("BULKMOVEDISALLOWED"))
return BULK_MOVE_DISALLOWED;
#endif
desc_struct &descItem = desc[entry - 1]; // Zero base
// if any of the first set of conditions is true, bulk move cannot
// be done for any value of this row. Turn off bulk move completely.
if ((NOT isRWRS) &&
((rowsetSize > 0 ) ||
(descItem.var_ptr == 0) ||
(op->isAddedCol()))
)
return BULK_MOVE_OFF;
// If any of these conditions are true, bulk move for this
// value cannot be done. Turn off bulk move for this value.
//
else if (
// Check to make sure the descriptor and operand have the same data type.
(descItem.datatype != op->getDatatype())
// Check to make sure the descriptor and operand character sets are the same.
|| ((DFS2REC::isAnyCharacter(op->getDatatype())) && (descItem.charset != op->getCharSet()))
// Check to make sure the descriptor and operand scale is the same.
|| ((NOT DFS2REC::isAnyCharacter(op->getDatatype()))
&& (NOT DFS2REC::isDateTime(op->getDatatype()))
&& (NOT DFS2REC::isInterval(op->getDatatype()))
&& (descItem.scale != op->getScale()))
// Check special case where descriptor is data/time. In that case the scale is stored in the precision.
// Check to make sure the descriptor and operand scale is the same.
|| ((DFS2REC::isDateTime(op->getDatatype())
|| DFS2REC::isInterval(op->getDatatype()))
&& (descItem.precision != op->getScale()))
// Check to make sure the descriptor and operand lengths are the same.
|| (descItem.length != op->getLength())
// Check if descriptor item is binary with precision > 0, which means that
// it was declared as a NUMERIC datatype, then it needs to be validated
// at input and output time. Cannot do bulk move in that case. At input,
// always validate it. At output, only validate if descItem's precision
// is greater than zero, and less than operand's precision or op's
// precision is zero.
||
((descItem.datatype >= REC_MIN_BINARY) &&
(descItem.datatype <= REC_MAX_BINARY) &&
(((isInputDesc) &&
(NOT isRWRS) &&
(descItem.precision > 0)) ||
((NOT isInputDesc) && (descItem.precision > 0)
&& ((op->getPrecision() == 0) || (op->getPrecision() > descItem.precision)))
)
)
// Check to make sure the descriptor and operand both have null flags.
||
(op->getNullFlag() && !descItem.ind_ptr)
// Check to make sure the descriptor and operand both do not have null flags.
||
(!op->getNullFlag() && descItem.ind_ptr)
// Check to make sure the descriptor and operand both not nullable if this is for input values.
// ||
// (isInputDesc && (op->getNullFlag() || descItem.nullable != 0))
// Check to make sure the operand data type is not a binary precision integer.
||
(op->getDatatype() == REC_BPINT_UNSIGNED)
// Check to make sure the operand is bulk movable.
||
(op->isBulkMoveable() == FALSE)
// Check to make sure if the data type is date/time that the request is coming from odbc.
||
((DFS2REC::isDateTime(op->getDatatype())) && (isOdbc == FALSE) && (NOT isInternalFormatIO))
// Check to make sure the the operand is not an interval
||
((DFS2REC::isInterval(op->getDatatype())) && (NOT isInternalFormatIO))
// Check to make sure that if the descriptor data type is varcha, then it is an ANSI varchar.
||
((DFS2REC::isAnyVarChar(descItem.datatype))
&& !(descItem.datatype == REC_BYTE_V_ASCII || descItem.datatype == REC_NCHAR_V_UNICODE))
)
{
// MXCS must always propertly set up the descriptor to allow bulk move unless the
// data type is interval. An interval datatype is returned in external format.
//BriefAssertion( !((isOdbc == TRUE) && ((DFS2REC::isInterval(op->getDatatype()) == FALSE)))
// , "Only bulk move allowed for rowwise rowsets type 2 and MXCS");
return BULK_MOVE_DISALLOWED;
} // end else if
//
// If nullable, make sure null indicator offsets match
//
Int32 opIndOffset = 0;
Int32 descIndOffset = 0;
if (descItem.nullable != 0)
{
if (op->getVCIndicatorLength())
opIndOffset = op->getNullIndOffset() - op->getVCLenIndOffset();
else
opIndOffset = op->getNullIndOffset() - op->getOffset();
descIndOffset = descItem.ind_ptr - descItem.var_ptr;
if (opIndOffset != descIndOffset)
{
// MXCS must always set up the descriptor such that the indicator and var pointers
// correctly aligned.
//BriefAssertion((isOdbc == FALSE) , "Indicator/var pointer alignment problem for rowwise rowset type 2 with MXCS");
if ((NOT isRWRS) || (NOT isOdbc))
return BULK_MOVE_DISALLOWED;
}
}
//
// Bulk move can be done for this value.
// Set the varPtr to the appropriate place.
//
if (descItem.nullable != 0)
varPtr = descItem.ind_ptr;
else
varPtr = descItem.var_ptr;
return BULK_MOVE_OK;
} // end checkBulkMoveStatusV2
void InputOutputExpr::setupBulkMoveInfo(void * desc_, CollHeap * heap,
NABoolean isInputDesc,
UInt32 flags)
{
NABoolean isOdbc = isODBC(flags);
NABoolean isDbtr = isDBTR(flags);
NABoolean isRwrs = isRWRS(flags);
NABoolean isIFIO = (isInternalFormatIO(flags) ||
(isDbtr && isRwrs));
ex_clause * clause = getClauses();
Descriptor * desc = (Descriptor *)desc_;
short entry = 0;
Long firstDescStartPtr = -1;
Long currDescStartPtr = 0;
Long currExeStartOffset = 0;
short currFirstEntryNum = 0;
Long descVarPtr;
Lng32 currLength = 0;
Long opOffset = 0;
short currAtpIndex = 0;
short bmEntry = 0;
NABoolean currIsVarchar = FALSE;
NABoolean currIsNullable = FALSE;
if (desc->bulkMoveDisabled())
return;
if (getNumEntries() == 0)
{
desc->setBulkMoveDisabled(TRUE);
return;
}
// bulk move disabled for wide descriptors.
if (isCall() && desc->isDescTypeWide())
{
desc->setBulkMoveDisabled(TRUE);
return;
}
desc->setBulkMoveDisabled(FALSE);
desc->setDoSlowMove(FALSE);
NABoolean bulkMoveInfoAllocated = FALSE;
while (clause)
{
if (clause->getType() == ex_clause::INOUT_TYPE)
{
entry++;
Attributes * op = clause->getOperand(0);
if (((ex_inout_clause*)clause)->excludeFromBulkMove())
{
// this clause has been excluded from bulk move.
// It could be used to get rowwise rowset specific
// information, like rowset buffer length, max size, etc.
// Skip this clause.
// move to the next entry
goto next_clause;
}
// firstDescStartPtr - Represents a pointer to the first entry
if (entry == 1)
firstDescStartPtr = desc->getRowsetVarPtr(entry);
Descriptor::BulkMoveStatus bms =
desc->checkBulkMoveStatus(entry, op, descVarPtr, isInputDesc,
isOdbc, isRwrs, isIFIO);
if (bms == Descriptor::BULK_MOVE_OFF)
{ // It is not possible to do a bulk move
desc->deallocBulkMoveInfo();
desc->setBulkMoveDisabled(TRUE);
return;
}
else if (bms == Descriptor::BULK_MOVE_DISALLOWED)
{
// Bulk move is not allowed for this case, do slow move using convDoIt.
// Mark the descriptor indicating slow move. This indicator
// will be used in input and outputValues method to call
// convDoIt.
desc->setDoSlowMove(entry, TRUE);
// Also set this flag in the descriptor.
desc->setDoSlowMove(TRUE);
// move to the next entry
goto next_clause;
}
else
{
// bulk move could be done. Reset 'slow move' flag.
desc->setDoSlowMove(entry, FALSE);
bmEntry++;
// if first time, allocate bulkMoveInfo.
if (NOT bulkMoveInfoAllocated)
{
desc->allocBulkMoveInfo();
bulkMoveInfoAllocated = TRUE;
}
}
NABoolean opIsVarchar = FALSE;
if (op->getDatatype() >= REC_MIN_V_CHAR_H &&
op->getDatatype() <= REC_NCHAR_V_UNICODE)
opIsVarchar = TRUE;
if (op->getNullFlag() != 0)
opOffset = (Long)op->getNullIndOffset();
else
{
if (opIsVarchar)
opOffset = (Long)op->getVCLenIndOffset();
else
opOffset = (Long)op->getOffset();
}
if (op->getAtpIndex() == 0) // constant
opOffset += (Long)getConstantsArea();
else if (op->getAtpIndex() == 1) // temp
opOffset += (Long)getTempsArea();
if ((isInputDesc) && (isOdbc) && (isRwrs))
{
descVarPtr = opOffset;
if (firstDescStartPtr < 0)
firstDescStartPtr = descVarPtr;
}
//
// The following code builds "blocks" of entries that can be bulk moved. That is,
// one or move entries are grouped together in blocks if the entries are adjacent
// to each other in both descriptor and operand.
//
// The variables involved and their purpose are as follows:
//
// bmEntry - Keeps track of the number of operands that have been added
// to the bulk move list
// opOffset - Holds the offset value of the current bulk move entry
// firstDescStartPtr - Represents a pointer to the first entry.
// When data is moved, the offset to the bulk move entry
// in the descriptor's data will be calculated as an
// offset from this pointer.
// currDescStartPtr - Represents a pointer to the current bulk move entry
// descriptor var pointer.
// currExeStartOffset - Holds the offset to the current bulk move block of entries.
// currLength - Holds the length of the current move block of entries.
// currfirstEntryNum - Represents the entry number of the first entry in the current
// block of entries.
//
// After all entries have been processed, the logic detects the last bulk move,
// and adds it to the list of bulk moves.
//
if (bmEntry == 1) // first time
{
currDescStartPtr = descVarPtr;
currAtpIndex = op->getAtpIndex();
currExeStartOffset = opOffset;
if (op->getNullFlag() != 0)
currLength = (op->getOffset() - op->getNullIndOffset()) + op->getLength();
else if (opIsVarchar)
currLength = op->getLength() + op->getVCIndicatorLength();
else
currLength = op->getLength();
currFirstEntryNum = 1;
if (!isInputDesc)
{
currIsVarchar = opIsVarchar;
currIsNullable = op->getNullFlag();
}
}
else if ((op->getAtpIndex() != currAtpIndex) ||
(opOffset < currExeStartOffset) ||
(descVarPtr < currDescStartPtr) ||
((descVarPtr - currDescStartPtr) !=
(opOffset - currExeStartOffset)) ||
(!isInputDesc && (opIsVarchar || currIsVarchar)))
// (descVarPtr - currDescStartPtr) != currLength)
{
desc->bulkMoveInfo()->
addEntry(currLength,
(((desc->rowwiseRowset()) &&
(NOT desc->rowwiseRowsetDisabled())) ?
// this one is an offset
(char*)(currDescStartPtr - firstDescStartPtr) :
(char*)currDescStartPtr),
currAtpIndex,
(currAtpIndex <= 1 ? TRUE : FALSE),
currExeStartOffset,
currFirstEntryNum,
(short)(bmEntry-1),
currIsVarchar,
currIsNullable);
currDescStartPtr = descVarPtr;
currAtpIndex = op->getAtpIndex();
currExeStartOffset = opOffset;
if (op->getNullFlag() != 0)
currLength = (op->getOffset() - op->getNullIndOffset()) + op->getLength();
else if (opIsVarchar)
currLength = op->getLength() + op->getVCIndicatorLength();
else
currLength = op->getLength();
currFirstEntryNum = bmEntry;
if (!isInputDesc)
{
currIsVarchar = opIsVarchar;
currIsNullable = op->getNullFlag();
}
}
else
{
if (op->getNullFlag() != 0)
currLength = opOffset - currExeStartOffset +
(op->getOffset() - op->getNullIndOffset()) + op->getLength();
else if (opIsVarchar)
currLength = opOffset - currExeStartOffset + op->getLength() + op->getVCIndicatorLength();
else
currLength = opOffset - currExeStartOffset + op->getLength();
}
}
next_clause:
clause = clause->getNextClause();
} // loop over clauses
if ((desc->bulkMoveInfo()) &&
(bmEntry > 0)) // found at least one entry which does bulk move.
desc->bulkMoveInfo()->
addEntry(currLength,
(((desc->rowwiseRowset()) &&
(NOT desc->rowwiseRowsetDisabled())) ?
// this one is an offset
(char*)(currDescStartPtr - firstDescStartPtr) :
(char*)currDescStartPtr),
currAtpIndex,
(currAtpIndex <= 1 ? TRUE : FALSE),
currExeStartOffset,
currFirstEntryNum, bmEntry,
currIsVarchar,
currIsNullable);
desc->setBulkMoveSetup(TRUE);
if (getenv("BULKMOVE") && getenv("BULKMOVEINFO") &&
desc->bulkMoveInfo())
{
if (isInputDesc)
cout << "InputDesc, Used Entries " <<
desc->bulkMoveInfo()->usedEntries() << endl;
else
cout << "OutputDesc, Used Entries " <<
desc->bulkMoveInfo()->usedEntries() << endl;
for (UInt32 i = 0; i < desc->bulkMoveInfo()->usedEntries(); i++)
{
cout << "Entry #" << i+1 << endl;
cout << "desc ptr = " <<
desc->bulkMoveInfo()->getDescDataPtr(i)
<< ", exe offset = " << desc->bulkMoveInfo()->getExeOffset(i)
<< ", length = " << desc->bulkMoveInfo()->getLength(i)
<< ", atpindex = " << desc->bulkMoveInfo()->getExeAtpIndex(i)
<< endl;
cout << "firstEntryNum = " <<
desc->bulkMoveInfo()->getFirstEntryNum(i) <<
", lastEntryNum = " <<
desc->bulkMoveInfo()->getLastEntryNum(i) << endl;
cout << endl;
}
}
}
ex_expr::exp_return_type InputOutputExpr::doBulkMove(atp_struct * atp,
void * desc_,
char * tgtRowPtr,
NABoolean isInputDesc)
{
Descriptor * desc = (Descriptor *)desc_;
BulkMoveInfo * bmi = desc->bulkMoveInfo();
if (!bmi)
return ex_expr::EXPR_OK;
for (Int32 i = 0; i < (Lng32)bmi->usedEntries(); i++)
{
char * dataPtr;
if (NOT bmi->isExeDataPtr(i))
{
// compute ptr from offset
if ((isInputDesc) && (tgtRowPtr))
dataPtr = tgtRowPtr + bmi->getExeOffset(i);
else
dataPtr =
atp->getTupp(bmi->getExeAtpIndex(i)).getDataPointer() +
bmi->getExeOffset(i);
}
else
// it is already a pointer. Use it.
dataPtr = bmi->getExeDataPtr(i);
if (isInputDesc)
{
char * srcPtr = NULL;
if (desc->rowwiseRowset())
srcPtr = (char*)(desc->getCurrRowPtrInRowwiseRowset() + bmi->getDescDataPtr(i));
else
srcPtr = bmi->getDescDataPtr(i);
str_cpy_all(dataPtr, srcPtr, bmi->getLength(i));
}
else
{
char *destPtr = NULL;
if ((desc->rowwiseRowset()) && (NOT desc->rowwiseRowsetDisabled()))
destPtr = (char*)(desc->getCurrRowPtrInRowwiseRowset() + bmi->getDescDataPtr(i));
else
destPtr = bmi->getDescDataPtr(i);
str_cpy_all(destPtr, dataPtr, (Lng32)bmi->getLength(i));
}
}
return ex_expr::EXPR_OK;
}
ex_expr::exp_return_type
InputOutputExpr::outputValues(atp_struct *atp,
void * output_desc_,
CollHeap *heap,
UInt32 flags)
{
NABoolean isOdbc = isODBC(flags);
NABoolean isIFIO = isInternalFormatIO(flags);
ex_clause * clause = getClauses();
Descriptor * output_desc = (Descriptor *)output_desc_;
NABoolean useParamIdx = isCall() && output_desc->isDescTypeWide();
Attributes * operand = 0;
ComDiagsArea * diagsArea = atp->getDiagsArea();
char * source = 0;
char * sourceVCLenInd = 0;
char * sourceNullInd = 0;
Lng32 sourceLen = 0; // Avoid possible uninitialized variable when saving in savedSourceLen.
Lng32 sourceNumProcessed = -1;
Lng32 sourceCompiledWithRowsets = FALSE; // True if there is a SELECT .. INTO <rowset> only
Lng32 tempSource;
char * target;
char * realTarget; // Added to show real start position of target,
// varchars adjust this position
short targetType;
Lng32 targetLen;
Lng32 targetRowsetSize; // Greater than zero if there is an output rowset in the descriptor,
// this can happen in a SELECT .. INTO <rowset> and FETCH INTO <rowset>
Lng32 targetScale;
Lng32 targetPrecision;
char * targetVarPtr = NULL;
char * targetIndPtr = NULL;
char * targetVCLen = NULL;
short targetVCLenSize = 0;
// Size of an entry of the indicator array
Lng32 indSize;
Long tempTarget = 0;
char * dataPtr = 0;
short entry = 0;
NABoolean byPassCheck = output_desc->isDescTypeWide();
if (!byPassCheck &&
(getNumEntries() != output_desc->getUsedEntryCount()) &&
(!output_desc->thereIsACompoundStatement()))
{
ExRaiseSqlError(heap, &diagsArea, CLI_STMT_DESC_COUNT_MISMATCH);
if (diagsArea != atp->getDiagsArea())
atp->setDiagsArea(diagsArea);
return ex_expr::EXPR_ERROR;
}
// If bulk move has not been disabled before, then check to see if bulk
// move could be done. This is done by comparing attributes of the
// executor items and the descriptor entries. See method
// Descriptor::checkBulkMoveStatus() for details on when bulk move will
// be disabled.
// If bulk move is possible then set up bulk move info, if not already
// done.
if (NOT output_desc->bulkMoveDisabled())
{
if (NOT output_desc->bulkMoveSetup())
{
if (output_desc->rowwiseRowset())
output_desc->setRowwiseRowsetDisabled(FALSE);
#ifdef _DEBUG
if (getenv("DISABLE_ROWWISE_ROWSET"))
output_desc->setRowwiseRowsetDisabled(TRUE);
#endif
setupBulkMoveInfo(output_desc, heap, FALSE /* output desc*/, flags);
if (
// rowwise rowsets V1 are only supported if bulk move
// is being done for all items.
(((output_desc->rowwiseRowset()) &&
((output_desc->bulkMoveDisabled()) ||
(output_desc->doSlowMove()))
)
&& (output_desc->rowwiseRowsetV1() == TRUE)
)
||
( (output_desc->rowwiseRowset())
&&
((output_desc->bulkMoveDisabled()))
&&
(output_desc->rowwiseRowsetV2() == TRUE)
)
)
{
output_desc->setRowwiseRowsetDisabled(TRUE);
if (NOT output_desc->bulkMoveDisabled()) // do slow move
{
// now set up bulk move as a non-rowwise rowset move
setupBulkMoveInfo(output_desc, heap, FALSE /* output desc*/,
flags);
}
else
{
output_desc->deallocBulkMoveInfo();
output_desc->setBulkMoveDisabled(TRUE);
}
} // rowwise rowset
}
if (NOT output_desc->bulkMoveDisabled())
{
// bulk move is enabled and setup has been done.
// Do bulk move now.
doBulkMove(atp, output_desc, NULL, FALSE /* output desc */);
if (NOT output_desc->doSlowMove())
return ex_expr::EXPR_OK;
}
}
// Note that every output operands must participate in rowsets, or none of
// them. That is, you cannot mix rowset host variables and simple host
// variables for output purposes (INTO clause).
Int32 totalNumOfRowsets = 0;
// Number of warnings found so far. This variable keeps track of the total number of
// warnings reported so far and is used to set up the indicator if
// we find a string overflow warning reported by the latest condDoit() call
// for the output.
Lng32 numOfWarningsFoundSoFar = 0;
while (clause) {
if (clause->getType() == ex_clause::INOUT_TYPE) {
entry++;
// TEMP TEMP TEMP
if(useParamIdx){
entry = ((ex_inout_clause *)clause)->getParamIdx();
}
// End of TEMP
if ((NOT output_desc->bulkMoveDisabled()) &&
(NOT output_desc->doSlowMove(entry)))
goto next_clause;
if(useParamIdx){
entry = ((ex_inout_clause *)clause)->getParamIdx();
}
operand = clause->getOperand(0);
sourceCompiledWithRowsets = (operand->getRowsetSize() > 0);
if (sourceCompiledWithRowsets) {
totalNumOfRowsets++;
}
switch (operand->getAtpIndex()) {
case 0:
// constant
source = getConstantsArea() + operand->getOffset();
sourceNullInd = getConstantsArea() + operand->getNullIndOffset();
sourceVCLenInd = getConstantsArea() + operand->getVCLenIndOffset();
break;
case 1:
// temp
source = getTempsArea() + operand->getOffset();
sourceNullInd = getTempsArea() + operand->getNullIndOffset();
sourceVCLenInd = getTempsArea() + operand->getVCLenIndOffset();
if (operand->getNullFlag() &&
ExpTupleDesc::isNullValue( sourceNullInd,
operand->getNullBitIndex(),
operand->getTupleFormat() ))
{
sourceNullInd = 0; // it is a null value
}
break;
default:
dataPtr = (atp->getTupp(operand->getAtpIndex())).getDataPointer();
if (operand->getOffset() < 0) {
// Offset is negative. This indicates that this offset
// is the negative of field number in a base table
// and this field follows one or more varchar fields.
// True for SQL/MP tables only. Needs special logic
// to compute the actual address of this field.
computeDataPtr(dataPtr,
- ((Lng32) operand->getOffset()),
atp->getCriDesc()->getTupleDescriptor(operand->getAtpIndex()),
&(source),
&(sourceNullInd),
&(sourceVCLenInd));
}
else {
if (dataPtr) {
source = dataPtr + operand->getOffset();
if (operand->getNullFlag()) {
sourceNullInd = dataPtr + operand->getNullIndOffset();
if (ExpTupleDesc::isNullValue( sourceNullInd,
operand->getNullBitIndex(),
operand->getTupleFormat() ))
{
sourceNullInd = 0; // it is a null value
}
}
if (operand->getVCIndicatorLength())
sourceVCLenInd = dataPtr + operand->getVCLenIndOffset();
}
else
{
sourceNullInd = 0;
sourceVCLenInd = 0;
}
}
} // switch atpIndex
// For rowsets, here we extract the number of elements in it. This
// only happens for the SELECT .. INTO clause
if (source && sourceCompiledWithRowsets) {
if (::convDoIt(source,
sizeof(Lng32),
REC_BIN32_SIGNED,
0,
0,
(char *)&tempSource,
sizeof(Lng32),
REC_BIN32_SIGNED,
0,
0,
0,
0,
heap,
&diagsArea) != ex_expr::EXPR_OK)
{
if (diagsArea != atp->getDiagsArea())
atp->setDiagsArea(diagsArea);
return ex_expr::EXPR_ERROR;
}
// Increment the location for the next element.
source += sizeof(Lng32);
// The offsets for rowset SQLVarChar attributes are set as follows.
// offset_ points to the start of the rowset info followed by four bytes
// of the rowset size. nullIndOffset_ (if nullIndicatorLength_ is not set
// to 0) points to (offset_+4). vcLenIndOffset_ (if vcIndicatorLength_
// is not set to 0) points to (offset_+nullIndicatorLength_). The first
// data value stars at (offset_+nullIndicatorLength_+vcIndicatorLength_)
// or (offset_+vcIndicatorLength_) depending on whether nullIndicatorLength_
// is valid.
// Note vcIndicatorLength_ is set to sizeof(short) for rowset SQLVarChars.
sourceNullInd = source;
if (operand->getNullFlag()) {
source += operand->getNullIndicatorLength();
}
if (operand->getVCIndicatorLength() > 0) {
source += operand->getVCIndicatorLength();
}
//Now source points to the start of the first data value of the rowset.
if (tempSource < 0) {
tempSource = 1;
}
// We have determined the number of elements in the rowset
sourceNumProcessed = tempSource;
} else {
// No rowset currently being processed, i.e. only one value to process
sourceNumProcessed = 1;
}
// Get the size of an entry of the indicator array
output_desc->getDescItem(entry, SQLDESC_ROWSET_IND_LAYOUT_SIZE, &indSize,
0, 0, 0, 0);
output_desc->getDescItem(entry, SQLDESC_TYPE_FS, &tempTarget, 0, 0, 0, 0);
targetType = (short)tempTarget;
output_desc->getDescItem(entry, SQLDESC_ROWSET_SIZE, &targetRowsetSize, 0, 0, 0, 0);
targetLen = 0;
output_desc->getDescItem(entry, SQLDESC_OCTET_LENGTH, &targetLen, 0, 0, 0, 0);
if ((targetType >= REC_MIN_INTERVAL) &&
(targetType <= REC_MAX_INTERVAL)) {
output_desc->getDescItem(entry, SQLDESC_INT_LEAD_PREC,
&targetPrecision, 0, 0, 0, 0);
// SQLDESC_PRECISION gets the fractional precision.
output_desc->getDescItem(entry, SQLDESC_PRECISION,
&targetScale, 0, 0, 0, 0);
}
else if (targetType == REC_DATETIME) {
output_desc->getDescItem(entry, SQLDESC_DATETIME_CODE,
&targetPrecision, 0, 0, 0, 0);
output_desc->getDescItem(entry, SQLDESC_PRECISION,
&targetScale, 0, 0, 0, 0);
} else {
output_desc->getDescItem(entry, SQLDESC_PRECISION,
&targetPrecision, 0, 0, 0, 0);
output_desc->getDescItem(entry, SQLDESC_SCALE,
&targetScale, 0, 0, 0, 0);
}
CharInfo::CharSet targetCharSet = CharInfo::UnknownCharSet;
NABoolean treatAnsivByteAsWideChar = FALSE;
if ((targetType >= REC_MIN_CHARACTER ) &&
(targetType <= REC_MAX_CHARACTER)) {
// charset can be retrieved as a long INTERNALLY
// using programatic interface by calling getDescItem(),
// and can only be retrieved as a character string EXTERNALLY
// using "get descriptor" syntax.
Lng32 temp_char_set;
output_desc->getDescItem(entry, SQLDESC_CHAR_SET,
&temp_char_set, 0, 0, 0, 0);
targetCharSet = (CharInfo::CharSet)temp_char_set;
if ( targetType == REC_BYTE_V_ANSI &&
CharInfo::is_NCHAR_MP(targetCharSet))
treatAnsivByteAsWideChar = TRUE;
// store charset in scale for convDoIt call
targetScale = targetCharSet;
}
// Now convert each element
for (Lng32 RowNum = 0; RowNum < sourceNumProcessed; RowNum++) {
// save current source and target datatype attributes since
// they may get changed later in this method.
// Restore them for the next iteration of this for loop.
// Do this only for rowsets, for non-rowsets this loop is executed
// only once.
Lng32 savedSourceLen;
short savedTargetType = 0;
Lng32 savedTargetPrecision = 0;
Lng32 savedTargetScale = 0;
Lng32 savedTargetLen = 0;
if (targetRowsetSize > 0)
{
savedSourceLen = sourceLen;
savedTargetType = targetType;
savedTargetPrecision = targetPrecision;
savedTargetScale = targetScale;
savedTargetLen = targetLen;
}
char * rowsetSourcePosition = source;
if (sourceCompiledWithRowsets)
output_desc->setDescItem(0, SQLDESC_ROWSET_HANDLE, RowNum, 0);
output_desc->getDescItem(entry, SQLDESC_IND_PTR, &tempTarget,
0, 0, 0, 0);
// Need to decide if we are in rowwise rowsets and adjust the temptarget appropriately
if (tempTarget && output_desc->rowwiseRowset() && (NOT output_desc->rowwiseRowsetDisabled()))
tempTarget = tempTarget + output_desc->getCurrRowOffsetInRowwiseRowset();
targetIndPtr = (char *)tempTarget;
if ((operand->getVCIndicatorLength()) && (sourceVCLenInd)) {
if (operand->getVCIndicatorLength() == sizeof(short))
sourceLen = *(short*)sourceVCLenInd;
else
sourceLen = *(Lng32 *)sourceVCLenInd;
}
else
sourceLen = operand->getLength();
output_desc->getDescItem(entry, SQLDESC_VAR_PTR,
&tempTarget, 0, 0, 0, 0);
// Need to decide if we are in rowwise rowsets and adjust the temptarget appropriately
if (tempTarget && output_desc->rowwiseRowset() && (NOT output_desc->rowwiseRowsetDisabled()))
tempTarget = tempTarget + output_desc->getCurrRowOffsetInRowwiseRowset();
NABoolean nullMoved = FALSE;
if (!tempTarget)
{
if (operand->getNullFlag() &&
((! sourceNullInd) ||
ExpTupleDesc::isNullValue( sourceNullInd,
operand->getNullBitIndex(),
operand->getTupleFormat() )))
{
output_desc->setDescItem(entry, SQLDESC_IND_DATA, -1, NULL);
nullMoved = TRUE;
}
else
output_desc->setDescItem(entry, SQLDESC_IND_DATA, 0, NULL);
}
else {
if (targetIndPtr) { // null indicator specified in target
target = targetIndPtr;
realTarget = target;
if (operand->getNullFlag() &&
((! sourceNullInd) ||
ExpTupleDesc::isNullValue( sourceNullInd,
operand->getNullBitIndex(),
operand->getTupleFormat() )))
{
// sourceNullInd is missing. This indicates a null value.
// Move null to the indicator.
if (targetRowsetSize > 0) {
for (Int32 i = 0; i < indSize; i++) {
targetIndPtr[i] = '\377';
}
}
else {
targetIndPtr[0] = '\377';
targetIndPtr[1] = '\377';
}
nullMoved = TRUE;
}
else {
// move 'no null' to target indicator
if (targetRowsetSize > 0) {
for (Int32 i = 0; i < indSize; i++) {
targetIndPtr[i] = 0;
}
}
else {
targetIndPtr[0] = 0;
targetIndPtr[1] = 0;
}
}
}
else {
// no indicator specified.
// Return error if a null value.
if (operand->getNullFlag() && (!sourceNullInd))
{
ExRaiseSqlError(heap, &diagsArea,
EXE_MISSING_INDICATOR_VARIABLE);
if (diagsArea != atp->getDiagsArea())
atp->setDiagsArea(diagsArea);
return ex_expr::EXPR_ERROR;
}
}
}
if (! nullMoved) {
targetVarPtr = (char *)tempTarget;
if (targetVarPtr) {
// bound column. Move value to user area.
target = targetVarPtr;
realTarget = target;
}
else {
// unbound column, move into temp buffer to add VC length
target = new (heap) char[targetLen + 10]; // 10 extra bytes should do
realTarget = target;
}
Lng32 tempDataConversionErrorFlag = ex_conv_clause::CONV_RESULT_OK;
if ((DFS2REC::isSQLVarChar(targetType)) ||
(DFS2REC::isLOB(targetType))) {
targetVCLen = target;
if (targetVarPtr) {
#ifdef _DEBUG
assert(SQL_VARCHAR_HDR_SIZE == sizeof(short));
#endif
// user host vars have VC len of 4 if the targetLen is
// more than 32768. Else, VC len is 2.
if (targetLen & 0xFFFF8000)
targetVCLenSize = sizeof(Lng32);
else
targetVCLenSize = sizeof(short);
}
else
targetVCLenSize = sizeof(Lng32); // desc entries have VC len 4
target = &target[targetVCLenSize];
}
else
targetVCLenSize = 0;
ex_expr::exp_return_type rc = ex_expr::EXPR_OK;
NABoolean implicitConversion = FALSE;
short sourceType = operand->getDatatype();
CharInfo::CharSet sourceCharSet = operand->getCharSet();
// if the source and target are not compatible, then do implicit
// conversion if skipTypeCheck is set.
// Furthermore, if restrictedSkipTypeCheck is set, then only
// convert for the conversions that are externally supported.
// See usage of default IMPLICIT_HOSTVAR_CONVERSION in generator
// (GenExpGenerator.cpp) for restricted conversions.
if (NOT areCompatible(sourceType, targetType))
{
if ((NOT skipTypeCheck()) ||
((restrictedSkipTypeCheck()) &&
(NOT implicitConversionSupported(sourceType,targetType))))
{
ExRaiseSqlError(heap, &diagsArea,EXE_CONVERT_NOT_SUPPORTED);
if (diagsArea != atp->getDiagsArea())
atp->setDiagsArea(diagsArea);
return ex_expr::EXPR_ERROR;
}
implicitConversion = TRUE;
} else {
if ( DFS2REC::isAnyCharacter(sourceType) &&
NOT CharInfo::isAssignmentCompatible(targetCharSet, sourceCharSet)
)
{
ExRaiseSqlError(heap, &diagsArea, CLI_ASSIGN_INCOMPATIBLE_CHARSET);
if (diagsArea != atp->getDiagsArea())
atp->setDiagsArea(diagsArea);
*diagsArea
<< DgString0(CharInfo::getCharSetName((CharInfo::CharSet)targetCharSet))
<< DgString1(CharInfo::getCharSetName((CharInfo::CharSet)sourceCharSet));
return ex_expr::EXPR_ERROR;
}
}
// if the target is REC_DECIMAL_LS and the source is NOT
// REC_DECIMAL_LSE, convert the source to REC_DECIMAL_LSE
// first. This conversion already took place during compilation
// for rowsets
char * intermediate = NULL;
Lng32 sourceScale = operand->getScale();
Lng32 sourcePrecision = operand->getPrecision();
if ((targetType == REC_DECIMAL_LS) &&
(operand->getDatatype() != REC_DECIMAL_LSE) &&
(targetRowsetSize == 0)) {
if ( source ) {
intermediate = new (heap) char[targetLen - 1];
if (::convDoIt(source,
sourceLen,
sourceType,
sourcePrecision,
sourceScale,
intermediate,
targetLen - 1,
REC_DECIMAL_LSE,
targetPrecision,
targetScale,
NULL,
0,
heap,
&diagsArea) != ex_expr::EXPR_OK) {
if (diagsArea != atp->getDiagsArea())
atp->setDiagsArea(diagsArea);
NADELETEBASIC(intermediate, heap);
if (! targetVarPtr)
NADELETEBASIC(realTarget, heap);
return ex_expr::EXPR_ERROR;
};
source = intermediate;
sourceLen = targetLen - 1;
}
sourceType = REC_DECIMAL_LSE;
}
// 5/18/98: added to handle SJIS encoding charset case.
// This is used in ODBC where the target character set is still
// ASCII but the actual character set used is SJIS.
//
if ( (sourceType == REC_NCHAR_F_UNICODE ||
sourceType == REC_NCHAR_V_UNICODE )
)
{
if ( targetCharSet == CharInfo::SJIS )
{
switch ( targetType ) {
case REC_BYTE_F_ASCII:
targetType = REC_MBYTE_F_SJIS;
break;
case REC_BYTE_V_ANSI:
case REC_BYTE_V_ASCII_LONG:
case REC_BYTE_V_ASCII:
targetType = REC_MBYTE_V_SJIS;
break;
default:
break;
}
}
}
//////////////////////////////////////////////////////////////////
// Conversion for exact numerics is done before doing scaling.
// The scale is implicit so a value 1234.56 is stored as 123456.
// While converting a value that does downscaling, we may run
// into overflow errors which would not be there after scaling.
// For example,
// a NUMERIC(6,2) type with value 1234.56 is stored as 123456.
// Converting this to a target type of SMALLINT will return an
// overflow error since 123456 will not fit in 2 bytes. However
// after scaling and truncation, this value will become 1234
// that will fit into SMALLINT. ANSI allows truncated results to
// be returned.
// So...if the source scale is greater than target scale for an
// exact numeric conversion, then first convert the source to
// largeint(this is the biggest exact numeric type that we
// support) and downscale it there. Then convert this scaled
// largeint to the target type.
//////////////////////////////////////////////////////////////////
if ((sourceScale > targetScale) &&
(isExactNumeric(sourceType)) &&
(isExactNumeric(targetType)))
{
if (DFS2REC::isBigNum(sourceType))
// max numeric precision of 128 is stored as 57 bytes.
intermediate = new (heap) char[57];
else
intermediate = new (heap) char[8];
if ( source )
{
// convert to largeint
if (convDoIt(source,
sourceLen,
sourceType,
sourcePrecision,
sourceScale,
intermediate,
(DFS2REC::isBigNum(sourceType) ? 57 : 8),
(DFS2REC::isBigNum(sourceType)
? REC_NUM_BIG_SIGNED : REC_BIN64_SIGNED),
targetPrecision,
targetScale,
NULL,
0,
heap,
&diagsArea) != ex_expr::EXPR_OK)
{
if (diagsArea != atp->getDiagsArea())
atp->setDiagsArea(diagsArea);
NADELETEBASIC(intermediate, heap);
return ex_expr::EXPR_ERROR;
}
// scale it
if (scaleDoIt(
intermediate,
(DFS2REC::isBigNum(sourceType) ? 57 : 8),
(DFS2REC::isBigNum(sourceType)
? REC_NUM_BIG_SIGNED : REC_BIN64_SIGNED),
sourceScale,
targetScale,
sourceType,
heap) != ex_expr::EXPR_OK)
{
ExRaiseSqlError(heap, &diagsArea, EXE_NUMERIC_OVERFLOW);
if (diagsArea != atp->getDiagsArea())
atp->setDiagsArea(diagsArea);
NADELETEBASIC(intermediate, heap);
return ex_expr::EXPR_ERROR;
}
}
// if the target is REC_DECIMAL_LS, convert the source to
// REC_DECIMAL_LSE first. Conversion to LS is currently
// only supported if source is LSE.
if (targetType == REC_DECIMAL_LS)
{
if ( source )
{
char * intermediate2 = new (heap) char[targetLen - 1];
if (::convDoIt(intermediate,
8,
REC_BIN64_SIGNED,
targetPrecision,
targetScale,
intermediate2,
targetLen - 1,
REC_DECIMAL_LSE,
targetPrecision,
targetScale,
NULL,
0,
heap,
&diagsArea) != ex_expr::EXPR_OK) {
if (diagsArea != atp->getDiagsArea())
atp->setDiagsArea(diagsArea);
NADELETEBASIC(intermediate, heap);
NADELETEBASIC(intermediate2, heap);
return ex_expr::EXPR_ERROR;
};
NADELETEBASIC(intermediate, heap);
intermediate = intermediate2;
sourceLen = targetLen - 1;
}
sourceType = REC_DECIMAL_LSE;
} // targetType == REC_DECIMAL_LS
else
{
sourceLen = (DFS2REC::isBigNum(sourceType) ? 57 : 8);
sourceType = (DFS2REC::isBigNum(sourceType)
? REC_NUM_BIG_SIGNED : REC_BIN64_SIGNED);
}
if ( source )
source = intermediate;
// no more scaling is to be done.
sourceScale = 0;
targetScale = 0;
} // exact numeric with scaling
// if incompatible conversions are to be done,
// and the target is REC_BYTE_V_ANSI,
// and the source is not a string type, convert the source to
// REC_BYTE_V_ASCII first. Conversion to V_ANSI is currently
// only supported if source is a string type.
if (implicitConversion)
{
if ((targetType == REC_BYTE_V_ANSI) &&
((sourceType < REC_MIN_CHARACTER) ||
(sourceType > REC_MAX_CHARACTER)))
{
if ( source )
{
intermediate = new (heap) char[targetLen];
short intermediateLen;
if (::convDoIt(source,
sourceLen,
sourceType,
sourcePrecision,
sourceScale,
intermediate,
targetLen,
REC_BYTE_V_ASCII,
targetPrecision,
targetScale,
(char*)&intermediateLen,
sizeof(short),
heap,
&diagsArea) != ex_expr::EXPR_OK) {
if (diagsArea != atp->getDiagsArea())
atp->setDiagsArea(diagsArea);
NADELETEBASIC(intermediate, heap);
return ex_expr::EXPR_ERROR;
};
sourceType = REC_BYTE_F_ASCII;
sourceLen = intermediateLen;
sourceScale = 0;
source = intermediate;
}
} // targetType == REC_BYTE_V_ANSI
if (((targetType == REC_DATETIME) ||
(DFS2REC::isInterval(targetType))) &&
(DFS2REC::isAnyCharacter(sourceType)))
{
targetType = REC_BYTE_F_ASCII;
targetScale = 0;
targetPrecision = 0;
}
} // implicit conversion
ConvInstruction case_index = CONV_UNKNOWN;
ULng32 convFlags = 0;
if ((source) &&
(NOT implicitConversion) &&
(((sourceType == REC_DATETIME) ||
((sourceType >= REC_MIN_INTERVAL) &&
(sourceType <= REC_MAX_INTERVAL))) &&
(NOT isIFIO)))
{
// this is a datetime or interval conversion.
// Convert the internal datetime/interval to its
// external string format. The conversion follows the
// same rules as "CAST(datetime/interval TO CHAR)".
// If the two interval types are not the same, then first
// convert source to target interval type, and then convert
// to its string external representation.
if (sourceType == REC_DATETIME)
{
if ((sourcePrecision != targetPrecision) ||
(sourceScale != targetScale))
{
// source and target must have the same datetime_code,
// they must both be date, time or timestamp.
ExRaiseSqlError(heap, &diagsArea,
EXE_CONVERT_NOT_SUPPORTED);
if (diagsArea != atp->getDiagsArea())
atp->setDiagsArea(diagsArea);
return ex_expr::EXPR_ERROR;
}
else
{
// For CALL stmts with wide desc, don't change target
// type to ASCII if rowwise rowsets are set by caller.
// Caller expects the value in internal format even though
// we do not use bulk move for copying.
// Also applicable to ODBC calls.
if ( ((NOT isOdbc) &&
(! isCall() ||
! output_desc->isDescTypeWide() ||
! output_desc->rowwiseRowsetV2())) ||
(isOdbc &&
! output_desc->rowwiseRowsetV1() &&
! output_desc->rowwiseRowsetV2()) )
{
targetType = REC_BYTE_F_ASCII;
targetPrecision = 0; // TBD $$$$ add target max chars later
targetScale = SQLCHARSETCODE_ISO88591; // assume target charset is ASCII-compatible
}
}
}
else if ((sourceType == targetType) &&
(sourcePrecision == targetPrecision) &&
(sourceScale == targetScale))
{
// 'interval' source and target match all attributes.
// Convert source(internal) to string target(external) format
targetType = REC_BYTE_F_ASCII;
targetPrecision = 0; // TBD $$$$ add target max chars later
targetScale = SQLCHARSETCODE_ISO88591; // assume target charset is ASCII compatible
}
else
{
// interval type and source/target do not match.
// Convert source to target interval type.
short intermediateLen = SQL_LARGE_SIZE;
intermediate = new (heap) char[intermediateLen];
if (::convDoIt(source,
sourceLen,
sourceType,
sourcePrecision,
sourceScale,
intermediate,
intermediateLen,
targetType,
targetPrecision,
targetScale,
NULL,
sizeof(short),
heap,
&diagsArea) != ex_expr::EXPR_OK)
{
if (diagsArea != atp->getDiagsArea())
atp->setDiagsArea(diagsArea);
NADELETEBASIC(intermediate, heap);
return ex_expr::EXPR_ERROR;
};
sourceType = targetType;
sourceLen = intermediateLen;
sourceScale = targetScale;
sourcePrecision = targetPrecision;
source = intermediate;
targetType = REC_BYTE_F_ASCII;
targetScale = 0;
targetPrecision = 0;
}
// datetime and interval values are left blank padded.
convFlags |= CONV_LEFT_PAD;
}
if ( source )
{
if ( treatAnsivByteAsWideChar == TRUE )
{
// We substract one here so that the first byte in the
// last wide char can be filled with single-byte
// null by convDoIt().
//
// Note the target length records the number of octets
// in target hostvar. The value is even because
// KANJI/KSC hostvars are wide characters.
targetLen--;
}
rc = convDoIt (source,
sourceLen,
sourceType,
sourcePrecision,
sourceScale,
target,
targetLen,
targetType,
targetPrecision,
targetScale,
targetVCLen,
targetVCLenSize,
heap,
&diagsArea,
case_index,
0,
convFlags);
if (rc == ex_expr::EXPR_ERROR) {
if (diagsArea != atp->getDiagsArea())
atp->setDiagsArea(diagsArea);
NADELETEBASIC(intermediate, heap);
if (!targetVarPtr)
NADELETEBASIC(realTarget, heap);
return ex_expr::EXPR_ERROR;
}
if ( treatAnsivByteAsWideChar == TRUE )
{
// Make the second byte in the last wide char
// a null so that the last wide char contains a wide-char
// null.
target[strlen(target)+1] = 0;
}
if (diagsArea ) {
// Indicator var is also used to test for truncated output value.
// The Indicator variable is set to the length of the string in the
// database if the character value is truncated..
// numOfWarningsFoundSoFar is the number of warnings we know exists
// before the above convDoIt() is called. If warning EXE_STRING_OVERFLOW
// exists as a result of the convDoIt() call, it will be recorded in
// diagsArea->warnings_ array, in the range
// [numOfStringOverflowWarningsFoundSoFar, warnings_.entries()). The
// method diagsArea->containsWarning(x, y) will tell us if it is true or
// not. If it is true, then we update the indicator.
if ( DFS2REC::isAnyCharacter(targetType) &&
DFS2REC::isAnyCharacter(operand->getDatatype()) &&
diagsArea -> containsWarning( numOfWarningsFoundSoFar,
EXE_STRING_OVERFLOW))
{
if (targetIndPtr)
{
short * tempVCInd = (short *) targetIndPtr;
if (DFS2REC::isANSIVarChar(sourceType)) {
if ( CharInfo::maxBytesPerChar(sourceCharSet) == 2 ) {
*tempVCInd = (short) NAWstrlen((NAWchar *)source);
// we are guaranteed that source is
// null-terminated as the previous call to convdoit()
//would have raised an error otherwise
}
else {
*tempVCInd = (short) strlen(source);
}
} // if ANSIVarChar
else {
*tempVCInd = (short)sourceLen;
if ( CharInfo::maxBytesPerChar(sourceCharSet) == 2 )
*tempVCInd /= SQL_DBCHAR_SIZE;
}
}
if (diagsArea != atp->getDiagsArea())
atp->setDiagsArea(diagsArea);
} // character datatype
numOfWarningsFoundSoFar = diagsArea->getNumber(DgSqlCode::WARNING_);
}
NADELETEBASIC(intermediate, heap);
// up- or down-scale target.
if (targetType >= REC_MIN_NUMERIC &&
targetType <= REC_MAX_NUMERIC &&
sourceScale != targetScale &&
::scaleDoIt(
target,
targetLen,
targetType,
sourceScale,
targetScale,
sourceType,
heap) != ex_expr::EXPR_OK)
{
ExRaiseSqlError(heap, &diagsArea, EXE_NUMERIC_OVERFLOW);
if (diagsArea != atp->getDiagsArea())
atp->setDiagsArea(diagsArea);
if (!targetVarPtr)
NADELETEBASIC(realTarget, heap);
return ex_expr::EXPR_ERROR;
}
if (!targetVarPtr) {
// unbound column. Remember it in executor memory.
// This value will(probably) be retrieved later by
// a call to GetDescItem with SQLDESC_VAR_DATA.
Lng32 length;
if (targetVCLenSize > 0)
length = 0;
else
length = targetLen;
output_desc->setDescItem(entry, SQLDESC_VAR_DATA,
length , realTarget);
//sourceLen+targetVCLenSize, realTarget);
NADELETEBASIC(realTarget, heap);
}
}
} // not NULL moved
if ( source != NULL ) {
// Increment the location for the next element.
if (targetRowsetSize > 0) {
source = rowsetSourcePosition;
}
source += operand->getStorageLength();
if ((operand->getVCIndicatorLength() > 0) && (sourceVCLenInd))
sourceVCLenInd += operand->getStorageLength();
if ((operand->getNullFlag()) && (sourceNullInd))
sourceNullInd += operand->getStorageLength();
}
if (targetRowsetSize > 0)
{
// restore the original datatype attributes.
sourceLen = savedSourceLen;
targetType = savedTargetType;
targetPrecision = savedTargetPrecision;
targetScale = savedTargetScale;
targetLen = savedTargetLen;
}
} // end of for (RowNum < sourceNumProcessed) loop
} // clause is INOUT type
next_clause:
clause = clause->getNextClause();
} // loop over clauses
if (totalNumOfRowsets)
output_desc->setDescItem(0, SQLDESC_ROWSET_NUM_PROCESSED,
sourceNumProcessed, 0);
return ex_expr::EXPR_OK;
}
ex_expr::exp_return_type InputOutputExpr::describeInput(void * input_desc_,
void * rwrs_info,
UInt32 flags)
{
NABoolean isOdbc = isODBC(flags);
NABoolean isDbtr = isDBTR(flags);
NABoolean isRwrs = isRWRS(flags);
NABoolean isIFIO = (isInternalFormatIO(flags) ||
(isDbtr && isRwrs));
RWRSInfo * rwrsInfo = (RWRSInfo *)rwrs_info;
ex_clause *clause = getClauses();
Descriptor * input_desc = (Descriptor *)input_desc_;
short entry = 0;
input_desc->setDescItem(0, SQLDESC_ROWSET_SIZE, // ROWSET_SIZE is a header field and
0, 0); // occurs once per descriptor, not once per
// desc item. Its value is 0 if no rowsets are present, otherwise its value is
// rowset size of any one of the all rowset input variables in the descriptor.
// For dynamic SQl, where describe is used, rowsets sizes must all be the same
// so the MIN of all input sizes is not calculated.
short dataType;
Lng32 prevEntryStartOffset = 0;
Lng32 currAlignedOffset = 0;
Lng32 firstEntryStartAlignment = 0;
Lng32 length = -1;
NABoolean firstEntryProcessed = FALSE;
while (clause)
{
if (clause->getType() == ex_clause::INOUT_TYPE)
{
Attributes * operand = clause->getOperand(0);
dataType = operand->getDatatype();
if(isCall() && input_desc->isDescTypeWide())
entry = ((ex_inout_clause *)clause)->getParamIdx();
else
entry++;
input_desc->setDescItem(entry, SQLDESC_TYPE_FS, dataType, 0);
length = operand->getLength();
// Record the display length for DATETIME or INTERVAL operand since either is
// represented as a character array externally. The value of this variable is
// used to set the SQLDESC_LENGTH field for the descriptor. The same value
// will be used to set the SQLDESC_ROWSET_VAR_LAYOUT_SIZE field if the input
// is a rowset and its element type is DATETIME or INTERVAL.
Lng32 displayLength = 0;
if ((dataType >= REC_MIN_INTERVAL) &&
(dataType <= REC_MAX_INTERVAL)) {
//
// According to ANSI, interval info is stored in the following
// descriptor items:
//
// DATETIME_INTERVAL_CODE = qualifier code
// PRECISION = fractional precision
// DATETIME_INTERVAL_PRECISION = leading precision
//
input_desc->setDescItem(entry,
SQLDESC_DATETIME_CODE,
getIntervalCode(dataType),
0);
input_desc->setDescItem(entry,
SQLDESC_PRECISION,
operand->getScale(),
0);
input_desc->setDescItem(entry, SQLDESC_SCALE, 0, 0);
input_desc->setDescItem(entry,
SQLDESC_INT_LEAD_PREC,
operand->getPrecision(),
0);
if (NOT isIFIO)
{
displayLength =
ExpInterval::getDisplaySize(dataType,
operand->getPrecision(),
operand->getScale());
length = displayLength;
}
} else if (dataType == REC_DATETIME) {
//
// According to ANSI, datetime info is stored in the following
// descriptor items:
//
// DATETIME_INTERVAL_CODE = qualifier code
// PRECISION = fractional precision
//
input_desc->setDescItem(entry,
SQLDESC_DATETIME_CODE,
operand->getPrecision(),
0);
input_desc->setDescItem(entry,
SQLDESC_PRECISION,
operand->getScale(),
0);
input_desc->setDescItem(entry, SQLDESC_SCALE, 0, 0);
input_desc->setDescItem(entry, SQLDESC_INT_LEAD_PREC, 0, 0);
if (NOT isIFIO)
{
displayLength =
ExpDatetime::getDisplaySize(operand->getPrecision(),
operand->getScale());
length = displayLength;
}
} else {
input_desc->setDescItem(entry, SQLDESC_DATETIME_CODE, 0, 0);
input_desc->setDescItem(entry,
SQLDESC_PRECISION,
operand->getPrecision(),
0);
input_desc->setDescItem(entry,
SQLDESC_SCALE,
operand->getScale(),
0);
input_desc->setDescItem(entry, SQLDESC_INT_LEAD_PREC, 0, 0);
}
// Use SQLDESC_CHAR_SET_NAM (one-part name) for charset
if (((dataType >= REC_MIN_CHARACTER) &&
(dataType <= REC_MAX_CHARACTER)) )
{
input_desc->setDescItem(entry, SQLDESC_CHAR_SET_NAM, 0,
(char*)CharInfo::getCharSetName(operand->getCharSet()));
// For Unicode/KSC5601/KANJI, set the length in number of characters
if ( CharInfo::maxBytesPerChar(operand->getCharSet()) == SQL_DBCHAR_SIZE )
length = operand->getLength()/SQL_DBCHAR_SIZE;
}
input_desc->setDescItem(entry, SQLDESC_NULLABLE,
operand->getNullFlag(), 0);
input_desc->setDescItem(entry, SQLDESC_NAME, 0,
((ex_inout_clause *)clause)->getName());
if (((ex_inout_clause *)clause)->getHeading() != 0)
input_desc->setDescItem(entry, SQLDESC_HEADING, 0,
((ex_inout_clause *)clause)->getHeading());
if (((ex_inout_clause *)clause)->getTableName() != 0)
input_desc->setDescItem(entry, SQLDESC_TABLE_NAME, 0,
((ex_inout_clause *)clause)->getTableName());
if (((ex_inout_clause *)clause)->getSchemaName() != 0)
input_desc->setDescItem(entry, SQLDESC_SCHEMA_NAME, 0,
((ex_inout_clause *)clause)->getSchemaName());
if (((ex_inout_clause *)clause)->getCatalogName() != 0)
input_desc->setDescItem(entry, SQLDESC_CATALOG_NAME, 0,
((ex_inout_clause *)clause)->getCatalogName());
input_desc->setDescItem(entry, SQLDESC_VAR_PTR, 0, 0);
input_desc->setDescItem(entry, SQLDESC_IND_PTR, 0, 0);
input_desc->setDescItem(entry, SQLDESC_VAR_DATA, 0, 0);
input_desc->setDescItem(entry, SQLDESC_IND_DATA, 0, 0);
if (operand->getRowsetSize() > 0) {
input_desc->setDescItem(0, SQLDESC_ROWSET_SIZE,
operand->getRowsetSize(), 0);
// Use the operand length if the operand is not INTERVAL/DATETIME.
if ( displayLength == 0 )
displayLength = operand->getLength();
// Describe Input will set values for VarLayoutSize that are compatible
// with C type (i.e null-terminated) hostvariables. For fixed length chars
// ANSI varchars, decimal, datetime and interval types
// varLayoutSize will be set to OCTET_LENGTH + 1 (+2 for unicode).
// Users are expected to override this value, if their arraysize is different.
if (DFS2REC::isAnyCharacter(dataType) && !DFS2REC::isSQLVarChar(dataType)) {
// all charcter types other than tandem varchar
displayLength += CharInfo::maxBytesPerChar(operand->getCharSet());
}
else if ((dataType >= REC_MIN_DECIMAL && dataType <= REC_MAX_DECIMAL) ||
(dataType >= REC_MIN_INTERVAL && dataType <= REC_MAX_INTERVAL_MP) ||
(dataType == REC_DATETIME)) {
// We do not expect to see dataType == REC_DECIMAL_LS (with first byte for sign)
// as the compiler currently generates REC_DECIMAL_LSE (leading sign embedded) as
// the type for signed decimal columns. If the compiler ever changes is default type
// for decimal signed columns to REC_DECIMAL_LS, then the following must be true
// RowsetVarLayoutSize = Precison + 2;
// If Length = Precision + 1; then the following assertion can be removed and
// the behaviour will be correct. If Length = Precision, then when removing
// the following assertion add one to displayLength for the REC_DECIMAL_LS datatype.
BriefAssertion(dataType != REC_DECIMAL_LS, "RowsetVarLayoutSize must account for the sign byte");
displayLength++;
}
input_desc->setDescItem(entry, SQLDESC_ROWSET_VAR_LAYOUT_SIZE,
displayLength, 0);
input_desc->setDescItem(entry, SQLDESC_ROWSET_IND_LAYOUT_SIZE,
operand->getNullIndicatorLength(), 0);
}
else {
input_desc->setDescItem(entry, SQLDESC_ROWSET_VAR_LAYOUT_SIZE,
0, 0);
input_desc->setDescItem(entry, SQLDESC_ROWSET_IND_LAYOUT_SIZE,
0, 0);
}
short paramMode = ((ex_inout_clause *)clause)->getParamMode();
short paramIdx = ((ex_inout_clause *)clause)->getParamIdx();
short ordPos = ((ex_inout_clause *)clause)->getOrdPos();
BriefAssertion((paramMode == PARAMETER_MODE_IN)||
(paramMode == PARAMETER_MODE_INOUT)||
(paramMode == PARAMETER_MODE_UNDEFINED),
"invalid param mode");
BriefAssertion(paramIdx >= 0, "invalid param index");
BriefAssertion(ordPos >= 0, "invalid param ordinal position");
// handle old plan
if(paramMode == PARAMETER_MODE_UNDEFINED){
paramMode = PARAMETER_MODE_IN;
}
if(paramIdx == 0){
paramIdx = entry;
}
if(ordPos == 0){
ordPos = entry;
}
input_desc->setDescItem(entry, SQLDESC_PARAMETER_MODE, paramMode, 0);
input_desc->setDescItem(entry, SQLDESC_PARAMETER_INDEX, paramIdx, 0);
input_desc->setDescItem(entry, SQLDESC_ORDINAL_POSITION, ordPos, 0);
// End
if(isCall()){
short paramMode = ((ex_inout_clause *)clause)->getParamMode();
short paramIdx = ((ex_inout_clause *)clause)->getParamIdx();
short ordPos = ((ex_inout_clause *)clause)->getOrdPos();
input_desc->setDescItem(entry, SQLDESC_PARAMETER_MODE,
paramMode, 0);
input_desc->setDescItem(entry, SQLDESC_PARAMETER_INDEX,
paramIdx, 0);
input_desc->setDescItem(entry, SQLDESC_ORDINAL_POSITION,
ordPos, 0);
}
else{
input_desc->setDescItem(entry, SQLDESC_PARAMETER_MODE,
PARAMETER_MODE_IN, 0);
input_desc->setDescItem(entry, SQLDESC_PARAMETER_INDEX,
entry, 0);
input_desc->setDescItem(entry, SQLDESC_ORDINAL_POSITION,
0, 0);
}
input_desc->setDescItem(entry, SQLDESC_LENGTH, length, 0);
// Set up length of null indicator and varcharlen indicator values
// in the descriptor.
//
input_desc->setDescItem(entry, SQLDESC_IND_LENGTH,
operand->getNullIndicatorLength(), 0);
input_desc->setDescItem(entry, SQLDESC_VC_IND_LENGTH,
operand->getVCIndicatorLength(), 0);
// Get offsets to data, null indicator and varchar len indicator
// in the actual row and set them in the descriptor.
//
// This is being done for sqlark_exploded_ and packed formats only.
// The offsets that are set in descriptor assume that the returned
// row to user is a single contiguous aligned row even though internally
// that row could be represented by multiple fragments, each with
// different atp index.
//
// These values are used by caller(mxcs) to set up input and output
// data pointers so that they are aligned and set up the same way
// as the actual row in cli.
//
// These values cannot be set by external callers.
//
// If RWRS buffer is being input, then do this for data params only.
//
if ((rwrsInfo ) &&
((entry == rwrsInfo->rwrsInputSizeIndex()) ||
(entry == rwrsInfo->rwrsMaxInputRowlenIndex()) ||
(entry == rwrsInfo->rwrsBufferAddrIndex()) ||
(entry == rwrsInfo->rwrsPartnNumIndex())))
{
// control params, skip them.
goto next_clause;
}
Lng32 dataOffset = -1;
Lng32 nullIndOffset = -1;
Lng32 currEntryStartOffset = -1;
Lng32 currEntryStartAlignment = -1;
Lng32 inputDatalen = -1;
if (input_desc->getDescItem(entry, SQLDESC_OCTET_LENGTH, &inputDatalen, NULL, 0, NULL, 0, NULL, 0) == ERROR)
return ex_expr::EXPR_ERROR;
Lng32 alignment = 1;
if (operand->getNullIndOffset() >= 0)
{
if (operand->getTupleFormat() == ExpTupleDesc::SQLARK_EXPLODED_FORMAT)
alignment = operand->getNullIndicatorLength();
currAlignedOffset = ADJUST(currAlignedOffset, alignment);
nullIndOffset = currAlignedOffset;
currEntryStartOffset = currAlignedOffset;
currEntryStartAlignment = alignment;
currAlignedOffset += operand->getNullIndicatorLength();
}
if (operand->getVCLenIndOffset() >= 0)
{
if (operand->getTupleFormat() == ExpTupleDesc::SQLARK_EXPLODED_FORMAT)
alignment = operand->getVCIndicatorLength();
currAlignedOffset = ADJUST(currAlignedOffset, alignment);
dataOffset = currAlignedOffset;
if (currEntryStartOffset == -1)
{
currEntryStartOffset = currAlignedOffset;
currEntryStartAlignment = alignment;
}
currAlignedOffset += operand->getVCIndicatorLength() + inputDatalen;
}
else
{
if (operand->getTupleFormat() == ExpTupleDesc::SQLARK_EXPLODED_FORMAT)
alignment = operand->getDataAlignmentSize();
currAlignedOffset = ADJUST(currAlignedOffset, alignment);
dataOffset = currAlignedOffset;
if (currEntryStartOffset == -1)
{
currEntryStartOffset = currAlignedOffset;
currEntryStartAlignment = alignment;
}
currAlignedOffset += inputDatalen;
}
if (NOT firstEntryProcessed)
{
firstEntryProcessed = TRUE;
firstEntryStartAlignment = currEntryStartAlignment;
}
input_desc->setDescItemInternal(entry, SQLDESC_DATA_OFFSET,
dataOffset, 0);
input_desc->setDescItemInternal(entry, SQLDESC_NULL_IND_OFFSET,
nullIndOffset, 0);
input_desc->setDescItemInternal(entry-1, SQLDESC_ALIGNED_LENGTH,
currEntryStartOffset - prevEntryStartOffset, 0);
prevEntryStartOffset = currEntryStartOffset;
} // INOUT clause
next_clause:
clause = clause->getNextClause();
}
if (firstEntryProcessed)
{
currAlignedOffset = ADJUST(currAlignedOffset, firstEntryStartAlignment);
input_desc->setDescItemInternal(entry, SQLDESC_ALIGNED_LENGTH,
currAlignedOffset - prevEntryStartOffset, 0);
}
if(!(isCall() && input_desc->isDescTypeWide())
|| (entry > input_desc->getUsedEntryCount()))
input_desc->setUsedEntryCount(entry);
return ex_expr::EXPR_OK;
}
/* If you are adding code to InputValues that will may cause an error to generated
(i.e. an SQL error with some SQLCODE value) then please read on.
You must make two decision about the error
1. Can this error be raised when rowset input data is being copied in?
If YES, is the error data dependent? A data dependent error is one which can occur
for row i of the rowset but not on row (i+1). In other words it is not an error that
applies to the whole statement but only to a specific row in the rowsets. If the
answer to both these questions is YES please place the following line of code at some
point after the error is in the diagsarea
setRowNumberInCli(diagsArea, RowNum, targetRowsetSize) ;
2. If the answer the previous question(s) were YES, then read on. Now please
judge if this error can be considered Nonfatal for rowsets, i.e. does it make
sense to continue execution with next rowset row or should we stop right away.
If you decide that it is a Nonfatal error (look in the method below for examples)
then place the following four lines of code after the call to setRowNumberInCli.
if (tolerateNonFatalError && diagsArea->canAcceptMoreErrors())
continue;
else
return ex_expr::EXPR_ERROR;
*/
// error path not taken . This is only if something bad happens in NVT
ex_expr::exp_return_type
InputOutputExpr::inputSingleRowValue(atp_struct *atp,
void * inputDesc_,
char * tgtRowPtr,
CollHeap * heap,
UInt32 flags)
{
NABoolean isOdbc = isODBC(flags);
NABoolean isDbtr = isDBTR(flags);
NABoolean isRwrs = isRWRS(flags);
NABoolean isIFIO = (isInternalFormatIO(flags) ||
(isDbtr && isRwrs));
ex_clause * clause = getClauses();
Descriptor * inputDesc = (Descriptor *)inputDesc_;
Attributes * operand = 0;
ComDiagsArea *diagsArea = atp->getDiagsArea();
char * target = 0;
char * targetVCLenInd = 0;
char * targetNullInd = 0;
char * source = 0;
Lng32 sourceLen;
char * sourceIndPtr = 0;
short sourceType;
Lng32 sourcePrecision;
Lng32 sourceScale;
char * tempSource = 0;
Lng32 tempSourceType = 0;
Lng32 indData;
short entry = 0;
char * intermediate = NULL;
short savedSourceType = 0;
Lng32 savedSourcePrecision = 0;
Lng32 savedSourceScale = 0;
Lng32 savedSourceLen = 0;
ExeErrorCode errorCode = EXE_OK;
// if bulk move has not been disabled before, then check to see if bulk
// move could be done. This is done by comparing attributes of the
// executor items and the descriptor entries. See method
// Descriptor::isBulkMovePossible() for details on when bulk move will
// be disabled.
// If bulk move is possible then set up bulk move info, if not already
// done.
if (NOT inputDesc->bulkMoveDisabled())
{
if (NOT inputDesc->bulkMoveSetup())
{
setupBulkMoveInfo(inputDesc, heap, TRUE /* input desc */, flags);
}
if (NOT inputDesc->bulkMoveDisabled())
{
// bulk move is enabled and setup has been done.
// Do bulk move now.
doBulkMove(atp, inputDesc, tgtRowPtr, TRUE /* input desc */);
if (NOT inputDesc->doSlowMove())
return ex_expr::EXPR_OK;
}
}
CharInfo::CharSet sourceCharSet = CharInfo::UnknownCharSet;
CharInfo::CharSet targetCharSet = CharInfo::UnknownCharSet;
while (clause)
{
if (clause->getType() == ex_clause::INOUT_TYPE)
{
entry++;
if ((NOT inputDesc->bulkMoveDisabled()) &&
(NOT inputDesc->doSlowMove(entry)))
goto next_clause;
operand = clause->getOperand(0);
if (operand->isBlankHV())
{
clause = clause->getNextClause();
continue;
}
char * dataPtr =
(tgtRowPtr ? tgtRowPtr :
(atp->getTupp(operand->getAtpIndex())).getDataPointer());
target = (char *)(dataPtr + operand->getOffset());
if (operand->getVCIndicatorLength() > 0)
targetVCLenInd = (char *)(dataPtr + operand->getVCLenIndOffset());
if (operand->getNullFlag())
targetNullInd = (char*)(dataPtr + operand->getNullIndOffset());
inputDesc->getDescItem(entry, SQLDESC_TYPE_FS, &tempSourceType, 0, 0, 0, 0);
sourceType = (short) tempSourceType;
if ((sourceType >= REC_MIN_INTERVAL) &&
(sourceType <= REC_MAX_INTERVAL)) {
inputDesc->getDescItem(entry, SQLDESC_INT_LEAD_PREC,
&sourcePrecision, 0, 0, 0, 0);
// SQLDESC_PRECISION gets the fractional precision
inputDesc->getDescItem(entry, SQLDESC_PRECISION,
&sourceScale, 0, 0, 0, 0);
}
else if (sourceType == REC_DATETIME) {
inputDesc->getDescItem(entry, SQLDESC_DATETIME_CODE,
&sourcePrecision, 0, 0, 0, 0);
inputDesc->getDescItem(entry, SQLDESC_PRECISION,
&sourceScale, 0, 0, 0, 0);
} else {
inputDesc->getDescItem(entry, SQLDESC_PRECISION,
&sourcePrecision, 0, 0, 0, 0);
inputDesc->getDescItem(entry, SQLDESC_SCALE,
&sourceScale, 0, 0, 0, 0);
};
if ((sourceType >= REC_MIN_CHARACTER) &&
(sourceType <= REC_MAX_CHARACTER)) {
Lng32 temp_char_set;
inputDesc->getDescItem(
entry, SQLDESC_CHAR_SET, &temp_char_set,
NULL, 0, NULL, 0);
sourceCharSet = (CharInfo::CharSet)temp_char_set;
sourceScale = sourceCharSet;
}
// 5/18/98: added to handle SJIS encoding charset case.
// This is used in ODBC where the target character set is still
// ASCII but the actual character set used is SJIS.
//
short targetType = operand->getDatatype();
targetCharSet = operand->getCharSet();
if ( (targetType == REC_NCHAR_F_UNICODE ||
targetType == REC_NCHAR_V_UNICODE )
)
{
// charset can be retrieved as a long INTERNALLY
// using programatic interface by calling getDescItem(),
// and can only be retrieved as a character string EXTERNALLY
// using "get descriptor" syntax.
if ( sourceCharSet == CharInfo::SJIS )
{
switch ( sourceType ) {
case REC_BYTE_F_ASCII:
sourceType = REC_MBYTE_F_SJIS;
break;
case REC_BYTE_V_ANSI:
case REC_BYTE_V_ASCII_LONG:
case REC_BYTE_V_ASCII:
sourceType = REC_MBYTE_V_SJIS;
break;
default:
break;
}
}
}
// The following is redefined at a later point for variable length
// strings
inputDesc->getDescItem(entry, SQLDESC_OCTET_LENGTH, &sourceLen, 0, 0, 0,
0);
inputDesc->getDescItem(entry, SQLDESC_IND_DATA, &indData,
0, 0, 0, 0);
if (indData == 0)
{
inputDesc->getDescItem(entry, SQLDESC_IND_PTR, &tempSource,
0, 0, 0, 0);
}
else
tempSource = (char * )&indData;
if ((isOdbc) && (isRwrs))
{
if (operand->getNullIndOffset() >= 0)
sourceIndPtr = (char*)
(inputDesc->getCurrRowPtrInRowwiseRowset()
+ operand->getNullIndOffset());
else
sourceIndPtr = NULL;
}
else
sourceIndPtr = tempSource;
NABoolean nullMoved = FALSE;
if (sourceIndPtr)
{
short temp = 0;
str_cpy_all((char *)&temp, sourceIndPtr, operand->getNullIndicatorLength());
if (temp > 0)
{
// invalid null indicator value.
// Valid indicator values are 0(not null value) and
// -ve (null value).
errorCode = EXE_CONVERT_STRING_ERROR;
goto error_return;
}
if (operand->getNullFlag())
{
nullMoved = (temp < 0); //if null indicator is a negative
//number, the value is null
str_cpy_all (targetNullInd, sourceIndPtr,
operand->getNullIndicatorLength());
}
else
{
if (temp < 0)
{
// input is a null value
// error. Trying to move a null input value to a
// non-nullable buffer.
errorCode = EXE_ASSIGNING_NULL_TO_NOT_NULL;
goto error_return;
}
}
}
else
{
// indicator not specified.
if (operand->getNullFlag())
{
// move 0 (non-null value) to first two bytes of buffer.
str_pad(targetNullInd, operand->getNullIndicatorLength(), '\0');
}
}
if ((isOdbc) && (isRwrs))
{
if (operand->getOffset() >= 0)
source = (char*)(inputDesc->getCurrRowPtrInRowwiseRowset()
+ operand->getOffset());
else
source = NULL;
}
else
source = inputDesc -> getVarData(entry);
if (!source)
{
continue;
}
if (DFS2REC::isSQLVarChar(sourceType))
{
// the first 2 bytes of data are actually the variable
// length indicator
short VCLen;
str_cpy_all((char *) &VCLen, source, sizeof(short));
sourceLen = (Lng32) VCLen;
source = &source[sizeof(short)];
#ifdef _DEBUG
assert(SQL_VARCHAR_HDR_SIZE == sizeof(short));
#endif
}
// 12/22/97: added for Unicode Vchar.
if (! nullMoved) {
ex_expr::exp_return_type retcode = ex_expr::EXPR_OK;
Lng32 warningMark;
if (diagsArea)
warningMark= diagsArea->getNumber(DgSqlCode::WARNING_);
else
warningMark = 0;
NABoolean implicitConversion = FALSE;
// if the source and target are not compatible, then do implicit
// conversion if skipTypeCheck is set.
// Furthermore, if restrictedSkipTypeCheck is set, then only
// convert for the conversions that are externally supported.
// See usage of default IMPLICIT_HOSTVAR_CONVERSION in generator
// (GenExpGenerator.cpp) for restricted conversions.
if (NOT areCompatible(sourceType, targetType))
{
if ((NOT skipTypeCheck()) ||
((restrictedSkipTypeCheck()) &&
(NOT implicitConversionSupported(sourceType,
operand->getDatatype()))))
{
errorCode = EXE_CONVERT_NOT_SUPPORTED;
goto error_return;
}
implicitConversion = TRUE;
}
else
{
// check charset for source and target if they are CHARACTER types.
if ( DFS2REC::isAnyCharacter(sourceType) &&
NOT CharInfo::isAssignmentCompatible(sourceCharSet, targetCharSet)
)
{
ExRaiseSqlError(heap, &diagsArea, CLI_ASSIGN_INCOMPATIBLE_CHARSET);
if (diagsArea != atp->getDiagsArea())
atp->setDiagsArea(diagsArea);
*diagsArea
<< DgString0(CharInfo::getCharSetName((CharInfo::CharSet)sourceCharSet))
<< DgString1(CharInfo::getCharSetName((CharInfo::CharSet)targetCharSet));
return ex_expr::EXPR_ERROR;
}
}
// Check length limit for KANJI/KSC5601/UCS2 ANSI VARCHAR (i.e., there
// should be a wchar_t typed NULL somewhere in the range [1, sourceLen/2]
// in the source. We have to do this for KANJI/KSC because their FS type
// codes are shared with the ISO88591 CHARACTER types and the conversion
// routines for ISO88591 ANSI VARCHAR in convDoIt() can not verify the
// wchar_t nulls.
//
// We also check the ANSI Unicode source here because we then can safely
// use the NAWstrlen() function to obtain the length in the next IF block to
// check code points.
if ((sourceType == REC_BYTE_V_ANSI &&
CharInfo::is_NCHAR_MP(sourceCharSet)) ||
sourceType == REC_NCHAR_V_ANSI_UNICODE
)
{
Lng32 i = 0;
Int32 sourceLenInWchar = sourceLen/SQL_DBCHAR_SIZE;
wchar_t* sourceInWchar = (wchar_t*)source;
while ((i < sourceLenInWchar) && (sourceInWchar[i] != 0))
i++;
if ( i == sourceLenInWchar )
{
errorCode = EXE_MISSING_NULL_TERMINATOR;
goto error_return;
};
}
if ( DFS2REC::isAnyCharacter(sourceType) &&
sourceCharSet == CharInfo::UNICODE )
{
Lng32 realSourceLen = sourceLen;
if (sourceType == REC_NCHAR_V_ANSI_UNICODE)
{
realSourceLen = NAWstrlen((wchar_t*)source) * SQL_DBCHAR_SIZE;
if ( realSourceLen > sourceLen )
realSourceLen = sourceLen ;
}
realSourceLen /= SQL_DBCHAR_SIZE;
if ( CharInfo::checkCodePoint((wchar_t*)source, realSourceLen,
CharInfo::UNICODE ) == FALSE )
{
// Error code 3400 falls in CLI error code area, but it is
// a perfect fit to use here (as an exeutor error).
ExRaiseSqlError(heap, &diagsArea, (ExeErrorCode)3400);
if (diagsArea != atp->getDiagsArea())
atp->setDiagsArea(diagsArea);
*diagsArea << DgString0(SQLCHARSETSTRING_UNICODE);
return ex_expr::EXPR_ERROR;
}
}
// if incompatible conversions are to be done,
// and the source is REC_BYTE_V_ANSI,
// and the target is not a string type, convert the source to
// REC_BYTE_F_ASCII first. Conversion from V_ANSI is currently
// only supported if target is a string type.
if ((implicitConversion) &&
((sourceType == REC_BYTE_V_ANSI) &&
((targetType < REC_MIN_CHARACTER) ||
(targetType > REC_MAX_CHARACTER))))
{
intermediate = new (heap) char[sourceLen];
if (::convDoIt(source,
sourceLen,
sourceType,
sourcePrecision,
sourceScale,
intermediate,
sourceLen,
REC_BYTE_F_ASCII,
sourcePrecision,
sourceScale,
NULL,
0,
heap,
&diagsArea) != ex_expr::EXPR_OK)
{
NADELETEBASIC(intermediate, heap);
errorCode = EXE_OK;
goto error_return;
};
sourceType = REC_BYTE_F_ASCII;
source = intermediate;
} // sourceType == REC_BYTE_V_ANSI
if ((sourceType >= REC_MIN_BINARY) &&
(sourceType <= REC_MAX_BINARY) &&
(sourceType == operand->getDatatype()) &&
(sourcePrecision > 0) &&
(sourcePrecision == operand->getPrecision()))
{
// validate source precision. Make the source precision
// zero, that will trigger the validation. See method
// checkPrecision in exp_conv.cpp.
sourcePrecision = 0;
}
ULng32 convFlags = 0;
if ((NOT implicitConversion) &&
(((sourceType == REC_DATETIME) ||
((sourceType >= REC_MIN_INTERVAL) &&
(sourceType <= REC_MAX_INTERVAL))) &&
(NOT isIFIO)))
{
// this is a datetime or interval conversion.
// Convert the external string datetime/interval to its
// internal format. The conversion follows the
// same rules as "CAST(char TO datetime/interval)".
// If the two interval types are not the same, then first
// convert source from its string to its internal format,
// and then convert to the target interval type.
if (sourceType == REC_DATETIME)
{
if ((sourcePrecision != operand->getPrecision()) ||
(sourceScale != operand->getScale()))
{
// source and target must have the same datetime_code,
// they must both be date, time or timestamp.
errorCode = EXE_CONVERT_NOT_SUPPORTED;
goto error_return;
}
else
sourceType = REC_BYTE_F_ASCII;
}
else if ((sourceType == targetType) &&
(sourcePrecision == operand->getPrecision()) &&
(sourceScale == operand->getScale()))
{
// 'interval' source and target match all attributes.
// Convert source(string external) to target(internal) format
sourceType = REC_BYTE_F_ASCII;
convFlags |= CONV_ALLOW_SIGN_IN_INTERVAL;
}
else
{
// interval type and source/target do not match.
// Convert source string interval to corresponding
// internal format.
short intermediateLen = SQL_LARGE_SIZE;
intermediate = new (heap) char[intermediateLen];
convFlags |= CONV_ALLOW_SIGN_IN_INTERVAL;
if (::convDoIt(source,
sourceLen,
REC_BYTE_F_ASCII,
0, //sourcePrecision,
0, //sourceScale,
intermediate,
intermediateLen,
sourceType,
sourcePrecision,
sourceScale,
NULL,
0,
heap,
&diagsArea,
CONV_UNKNOWN,
0,
convFlags) != ex_expr::EXPR_OK)
{
NADELETEBASIC(intermediate, heap);
errorCode = EXE_OK;
goto error_return;
};
source = intermediate;
sourceLen = intermediateLen;
}
}
if (noDatetimeValidation())
convFlags |= CONV_NO_DATETIME_VALIDATION;
retcode = ::convDoIt(source,
sourceLen,
sourceType,
sourcePrecision,
sourceScale,
target,
operand->getLength(),
operand->getDatatype(),
operand->getPrecision(),
operand->getScale(),
targetVCLenInd,
operand->getVCIndicatorLength(),
heap,
&diagsArea,
CONV_UNKNOWN,
0,
convFlags);
// NADELETEBASIC(intermediate, heap);
if (retcode != ex_expr::EXPR_OK)
{
errorCode = EXE_OK;
goto error_return;
}
else if (diagsArea && diagsArea->mainSQLCODE() == EXE_STRING_OVERFLOW)
{
Int32 warningMark2 = diagsArea->getNumber(DgSqlCode::WARNING_);
Int32 counter = warningMark2 - warningMark;
diagsArea->deleteWarning(warningMark2-counter);
errorCode = EXE_STRING_OVERFLOW;
goto error_return;
}
// up- or down-scale target.
if ((operand->getDatatype() >= REC_MIN_NUMERIC) &&
(operand->getDatatype() <= REC_MAX_NUMERIC) &&
(sourceScale != operand->getScale()) &&
::scaleDoIt(target,
operand->getLength(),
operand->getDatatype(),
sourceScale,
operand->getScale(),
sourceType,
heap) != ex_expr::EXPR_OK)
{
errorCode = EXE_NUMERIC_OVERFLOW;
goto error_return;
}
} // if (! nullMoved)
} // if clause == IN_OUT type
next_clause:
clause = clause->getNextClause();
} // while(clause)
return ex_expr::EXPR_OK;
error_return:
if (errorCode != EXE_OK)
ExRaiseSqlError(heap, &diagsArea, errorCode);
if (diagsArea != atp->getDiagsArea())
atp->setDiagsArea(diagsArea);
return ex_expr::EXPR_ERROR;
}
ex_expr::exp_return_type
InputOutputExpr::inputRowwiseRowsetValues(atp_struct *atp,
void * inputDesc_,
void * rwrs_info,
NABoolean tolerateNonFatalError,
CollHeap * heap,
UInt32 flags)
{
NABoolean isOdbc = isODBC(flags);
NABoolean isDbtr = isDBTR(flags);
ex_clause * clause = getClauses();
Descriptor * inputDesc = (Descriptor *)inputDesc_;
RWRSInfo * rwrsInfo = (RWRSInfo *)rwrs_info;
Attributes * operand = 0;
ComDiagsArea *diagsArea = atp->getDiagsArea();
Lng32 targetRowsetSize = -1;
Lng32 dynamicRowsetSize = -1;
Lng32 rwrsInputBuffer = -1;
char * source = NULL;
char * target = NULL;
short entry = 0;
ExeErrorCode errorCode = EXE_OK;
Lng32 intParam1 = 0;
Lng32 intParam2 = 0;
Lng32 intParam3 = 0;
Lng32 numRowsProcessed = 0;
char * tgtRowAddr = rwrsInfo->getRWRSInternalBufferAddr();
char ** rwrsInternalBufferAddrLoc = NULL;
char * rwrsMaxInputRowlenLoc = NULL;
Lng32 inputRowsetSize = -1;
Lng32 maxInputRowlen = -1;
char * bufferAddr = NULL;
Lng32 partnNum = -1;
// input RWRS are always V2.
inputDesc->setRowwiseRowsetV2(TRUE);
while (clause)
{
if (clause->getType() == ex_clause::INOUT_TYPE)
{
entry++;
if ((entry == rwrsInfo->rwrsInputSizeIndex()) ||
(entry == rwrsInfo->rwrsMaxInputRowlenIndex()) ||
(entry == rwrsInfo->rwrsBufferAddrIndex()) ||
(entry == rwrsInfo->rwrsPartnNumIndex()))
{
operand = clause->getOperand(0);
target =
(char *)((atp->getTupp(operand->getAtpIndex())).
getDataPointer() + operand->getOffset());
// find the number of rows in the input rowset
source = inputDesc->getVarData(entry);
if (entry == rwrsInfo->rwrsInputSizeIndex())
{
if (source)
{
// do the conversion
str_cpy_all(target, source, sizeof(Lng32));
}
else
{
*(Lng32*)target = inputDesc->getRowwiseRowsetSize();
}
inputRowsetSize = *((Lng32 *) target);
}
else if (entry == rwrsInfo->rwrsMaxInputRowlenIndex())
{
if (source)
{
// do the conversion
str_cpy_all(target, source, sizeof(Lng32));
}
else
{
*(Lng32*)target = inputDesc->getRowwiseRowsetRowLen();
}
maxInputRowlen = *((Lng32 *) target);
rwrsMaxInputRowlenLoc = target;
}
else if (entry == rwrsInfo->rwrsBufferAddrIndex())
{
if (source)
{
// do the conversion
str_cpy_all(target, source, sizeof(Long));
}
else
{
*(Long*)target = inputDesc->getRowwiseRowsetPtr();
}
bufferAddr = *((char **) target);
// We need to send the address of the rowwise rowset
// buffer to other nodes below the root.
// It will be determined later in this procedure whether
// we can send the buffer address of the application's
// rowwise rowset buffer or if we first need to move data
// to our internal buffer and send the addr of the internal
// buffer.
rwrsInternalBufferAddrLoc = (char **)target;
}
else if (entry == rwrsInfo->rwrsPartnNumIndex())
{
partnNum = *((Lng32 *) target);
}
// skip this INOUT clause during rowset bulk move processing.
((ex_inout_clause*)clause)->setExcludeFromBulkMove(TRUE);
} // rwrs info
} // if inout clause
clause = clause->getNextClause();
} // while clause
if ((inputRowsetSize < 0) ||
(inputRowsetSize > rwrsInfo->rwrsMaxSize()))
{
//raise error
errorCode = (ExeErrorCode)30038; //EXE_RW_ROWSET_SIZE_OUTOF_RANGE;
goto error_return;
}
if (maxInputRowlen < 0)
{
//raise error
errorCode = (ExeErrorCode)30039; //EXE_RW_ROWSET_ROWLEN_OUTOF_RANGE;
goto error_return;
}
if (inputRowsetSize == 0)
return ex_expr::EXPR_OK;
if (rwrsInfo->rwrsIsCompressed() && rwrsInfo->dcompressInMaster())
{
// if not already allocated, allocate a buffer to decompress user buf.
Lng32 dcomBufLen = maxInputRowlen * rwrsInfo->rwrsMaxSize();
if ((rwrsInfo->getRWRSDcompressedBufferAddr() == NULL) ||
(rwrsInfo->getRWRSDcompressedBufferLen() < dcomBufLen))
{
if (rwrsInfo->getRWRSDcompressedBufferAddr())
{
NADELETEBASIC((char *)rwrsInfo->getRWRSDcompressedBufferAddr(), heap);
}
char * dcomBuf = new(heap) char[dcomBufLen];
rwrsInfo->setRWRSDcompressedBufferAddr(dcomBuf);
rwrsInfo->setRWRSDcompressedBufferLen(dcomBufLen);
}
// dcompress/decode the user buffer into the dcompressedBuffer.
// Length of buffer is in the first 4 bytes of bufferAddr.
// Pass the pointer to the length bytes to ExDecode.
// Pass the buffer length plus size of length bytes.
Lng32 compressedBuflen = *(Lng32 *)bufferAddr + sizeof(Lng32);
unsigned char * compressedBuf = (unsigned char *)(bufferAddr);
Int32 dcompressedBuflen = -1;
Lng32 param1 = 0;
Lng32 param2 = 0;
Lng32 rc = ExDecode(compressedBuf, compressedBuflen,
(unsigned char *)rwrsInfo->getRWRSDcompressedBufferAddr(),
&dcompressedBuflen, param1, param2);
if (rc)
{
// error.
errorCode = (ExeErrorCode)30045;
intParam1 = rc;
intParam2 = param1;
intParam3 = param2;
goto error_return;
}
if (dcompressedBuflen > rwrsInfo->getRWRSDcompressedBufferLen())
{
// error.
errorCode = (ExeErrorCode)30046;
intParam1 = dcompressedBuflen;
intParam2 = rwrsInfo->getRWRSDcompressedBufferLen();
goto error_return;
}
// change user buffer addr to point to the decompressed buffer
bufferAddr = rwrsInfo->getRWRSDcompressedBufferAddr();
}
// find out if user rwrs buffer could be passed in to the unpack method.
// This could be done :
// -- bulk move is not disabled and there are no slow bulk moves
// -- user row layout is the same as the internal row
// -- there is no mismatch in user and internal column definitions
// -- columns in user row could be moved with one bulk move to the
// internal buffer
// if these conditions are met, then user buffer address, user row length
// and number of rows are passed on to the unpack buffer.
if (NOT inputDesc->bulkMoveDisabled())
{
if (NOT inputDesc->bulkMoveSetup())
{
rwrsInfo->setUseUserRWRSBuffer(FALSE);
setupBulkMoveInfo(inputDesc, heap, TRUE /* input desc */, flags);
if ((NOT inputDesc->bulkMoveDisabled()) &&
(NOT inputDesc->doSlowMove()) &&
(inputDesc->bulkMoveInfo()->usedEntries() == 1))
{
rwrsInfo->setUseUserRWRSBuffer(TRUE);
}
}
}
if (rwrsInfo->useUserRWRSBuffer())
{
inputDesc->setDescItem(0, SQLDESC_ROWWISE_ROWSET_SIZE,
inputRowsetSize, 0);
inputDesc->setDescItem(0, SQLDESC_ROWWISE_ROWSET_ROW_LEN,
maxInputRowlen, 0);
inputDesc->setDescItem(0, SQLDESC_ROWWISE_ROWSET_PTR,
(Long)bufferAddr, 0);
inputDesc->setDescItem(0, SQLDESC_ROWWISE_ROWSET_PARTN_NUM,
partnNum, 0);
inputDesc->setDescItem(0, SQLDESC_ROWSET_NUM_PROCESSED,
inputRowsetSize, NULL);
// move the address of the user rowwise rowset buffer so it could
// be passed on to other operators.
*rwrsInternalBufferAddrLoc = bufferAddr;
// move the max length of each user row so it could be passed on to
// other operators.
*rwrsMaxInputRowlenLoc = maxInputRowlen;
}
else
{
// error path not taken . This is only if something bad happens in NVT
if (isDbtr)
{
// rowwise rowsets from dbtr *must* use the optimized input
// path. Something is not setup correctly, if it doesn't.
// Return an error.
errorCode = (ExeErrorCode)30041;
goto error_return;
}
inputDesc->setDescItem(0, SQLDESC_ROWWISE_ROWSET_SIZE,
inputRowsetSize, 0);
inputDesc->setDescItem(0, SQLDESC_ROWWISE_ROWSET_ROW_LEN,
maxInputRowlen, 0);
inputDesc->setDescItem(0, SQLDESC_ROWWISE_ROWSET_PTR,
(Long)bufferAddr, 0);
while (numRowsProcessed < inputRowsetSize)
{
inputDesc->setDescItem(0, SQLDESC_ROWSET_NUM_PROCESSED,
numRowsProcessed, NULL);
setIsRWRS(flags, TRUE);
if (inputSingleRowValue(atp, inputDesc, (char*)tgtRowAddr, heap,
flags)
== ex_expr::EXPR_ERROR)
{
errorCode = EXE_OK;
goto error_return;
}
numRowsProcessed++;
tgtRowAddr += rwrsInfo->rwrsMaxInternalRowlen();
}
// reset rows processed
inputDesc->setDescItem(0, SQLDESC_ROWSET_NUM_PROCESSED,
0, NULL);
// move the address of the internal rowwise rowset buffer so it could
// be passed on to other operators.
*rwrsInternalBufferAddrLoc =
rwrsInfo->getRWRSInternalBufferAddr();
// move the max length of each row so it could be passed on to other
// operators.
*rwrsMaxInputRowlenLoc =
rwrsInfo->rwrsMaxInternalRowlen();
} // use internal buffer
return ex_expr::EXPR_OK;
error_return:
if (errorCode != EXE_OK)
ExRaiseSqlError(heap, &diagsArea, errorCode,
NULL,
(intParam1 != 0 ? &intParam1 : NULL),
(intParam2 != 0 ? &intParam2 : NULL),
(intParam3 != 0 ? &intParam3 : NULL));
if (diagsArea != atp->getDiagsArea())
atp->setDiagsArea(diagsArea);
return ex_expr::EXPR_ERROR;
}
ex_expr::exp_return_type
InputOutputExpr::inputValues(atp_struct *atp,
void * inputDesc_,
NABoolean tolerateNonFatalError,
CollHeap * heap,
UInt32 flags) {
NABoolean isOdbc = isODBC(flags);
NABoolean isIFIO = isInternalFormatIO(flags);
ex_clause * clause = getClauses();
Descriptor * inputDesc = (Descriptor *)inputDesc_;
Attributes * operand = 0;
ComDiagsArea *diagsArea = atp->getDiagsArea();
char * target = 0;
char * targetVCLenInd = 0;
char * targetNullInd = 0;
Lng32 targetRowsetSize;
Lng32 dynamicRowsetSize = 0; // Specified with ROWSET FOR INPUT SIZE <var>
// or some other rowset syntax; <var> can
// change at run time
char * source = 0;
Lng32 sourceLen;
char * sourceIndPtr = 0;
short sourceType;
Lng32 sourcePrecision;
Lng32 sourceScale;
char * tempSource = 0;
Lng32 tempSourceType = 0;
Lng32 indData;
short entry = 0;
char * intermediate = NULL;
Lng32 rowsetVarLayoutSize;
Lng32 rowsetIndLayoutSize;
short savedSourceType = 0;
Lng32 savedSourcePrecision = 0;
Lng32 savedSourceScale = 0;
Lng32 savedSourceLen = 0;
// We changed the code here, because the previous version uses
// isCall() && isWideDescType() as a condition. isCall() returns FALSE when
// there is no paramter index in the expression. So the error was
// raised when there is no inputs and there is outputs.
// We relax the bypass condition. until we have a fix in the generator that
// can put a flag in the root to describe whether it is CALL statement.
NABoolean useParamIdx = FALSE;
if(!inputDesc) {
if(getNumEntries() > 0)
{
ExRaiseSqlError(heap, &diagsArea, CLI_STMT_DESC_COUNT_MISMATCH);
if (diagsArea != atp->getDiagsArea())
atp->setDiagsArea(diagsArea);
return ex_expr::EXPR_ERROR;
}
else
return ex_expr::EXPR_OK;
}
else{
useParamIdx = isCall() && inputDesc->isDescTypeWide();
NABoolean byPassCheck = inputDesc->isDescTypeWide();
if (!byPassCheck && !inputDesc->thereIsACompoundStatement() &&
getNumEntries() != inputDesc->getUsedEntryCount())
{
ExRaiseSqlError(heap, &diagsArea, CLI_STMT_DESC_COUNT_MISMATCH);
if (diagsArea != atp->getDiagsArea())
atp->setDiagsArea(diagsArea);
return ex_expr::EXPR_ERROR;
}
}
if (inputDesc && inputDesc->rowwiseRowset())
{
ExRaiseSqlError(heap, &diagsArea, CLI_ROWWISE_ROWSETS_NOT_SUPPORTED);
if (diagsArea != atp->getDiagsArea())
atp->setDiagsArea(diagsArea);
return ex_expr::EXPR_ERROR;
}
// If bulk move has not been disabled before, then check to see if bulk
// move could be done. This is done by comparing attributes of the
// executor items and the descriptor entries. See method
// Descriptor::checkBulkMoveStatus() for details on when bulk move will
// be disabled.
// If bulk move is possible then set up bulk move info, if not already
// done.
if (NOT inputDesc->bulkMoveDisabled())
{
if (NOT inputDesc->bulkMoveSetup())
{
setupBulkMoveInfo(inputDesc, heap, TRUE /* input desc */, flags);
}
if (NOT inputDesc->bulkMoveDisabled())
{
// bulk move is enabled and setup has been done.
// Do bulk move now.
doBulkMove(atp, inputDesc, NULL, TRUE /* input desc */);
if (NOT inputDesc->doSlowMove())
return ex_expr::EXPR_OK;
}
}
CharInfo::CharSet sourceCharSet = CharInfo::UnknownCharSet;
CharInfo::CharSet targetCharSet = CharInfo::UnknownCharSet;
// Note that some of the input operands may participate in rowsets, while
// other may not. That is, simple host variables and rowset host variables
// can be mixed for input purposes.
while (clause) {
if (clause->getType() == ex_clause::INOUT_TYPE) {
entry++;
if ((NOT inputDesc->bulkMoveDisabled()) &&
(NOT inputDesc->doSlowMove(entry)))
goto next_clause;
if(useParamIdx){
entry = ((ex_inout_clause *)clause)->getParamIdx();
}
operand = clause->getOperand(0);
if (operand->isBlankHV()) {
clause = clause->getNextClause();
continue;
}
target =
(char *)((atp->getTupp(operand->getAtpIndex())).getDataPointer() +
operand->getOffset());
if (operand->getVCIndicatorLength() > 0)
targetVCLenInd =
(char *)((atp->getTupp(operand->getAtpIndex())).getDataPointer() +
operand->getVCLenIndOffset());
if (operand->getNullFlag())
targetNullInd =
(atp->getTupp(operand->getAtpIndex())).getDataPointer()
+ operand->getNullIndOffset();
inputDesc->getDescItem(entry, SQLDESC_TYPE_FS, &tempSourceType, 0, 0, 0, 0);
sourceType = (short) tempSourceType;
if ((sourceType >= REC_MIN_INTERVAL) &&
(sourceType <= REC_MAX_INTERVAL)) {
inputDesc->getDescItem(entry, SQLDESC_INT_LEAD_PREC,
&sourcePrecision, 0, 0, 0, 0);
// SQLDESC_PRECISION gets the fractional precision
inputDesc->getDescItem(entry, SQLDESC_PRECISION,
&sourceScale, 0, 0, 0, 0);
}
else if (sourceType == REC_DATETIME) {
inputDesc->getDescItem(entry, SQLDESC_DATETIME_CODE,
&sourcePrecision, 0, 0, 0, 0);
inputDesc->getDescItem(entry, SQLDESC_PRECISION,
&sourceScale, 0, 0, 0, 0);
} else {
inputDesc->getDescItem(entry, SQLDESC_PRECISION,
&sourcePrecision, 0, 0, 0, 0);
inputDesc->getDescItem(entry, SQLDESC_SCALE,
&sourceScale, 0, 0, 0, 0);
};
if ((sourceType >= REC_MIN_CHARACTER) &&
(sourceType <= REC_MAX_CHARACTER)) {
Lng32 temp_char_set;
inputDesc->getDescItem(
entry, SQLDESC_CHAR_SET, &temp_char_set,
NULL, 0, NULL, 0);
sourceCharSet = (CharInfo::CharSet)temp_char_set;
sourceScale = temp_char_set;
}
// 5/18/98: added to handle SJIS encoding charset case.
// This is used in ODBC where the target character set is still
// ASCII but the actual character set used is SJIS.
//
short targetType = operand->getDatatype();
targetCharSet = operand->getCharSet();
if ( (targetType == REC_NCHAR_F_UNICODE ||
targetType == REC_NCHAR_V_UNICODE )
)
{
// charset can be retrieved as a long INTERNALLY
// using programatic interface by calling getDescItem(),
// and can only be retrieved as a character string EXTERNALLY
// using "get descriptor" syntax.
if ( sourceCharSet == CharInfo::SJIS )
{
switch ( sourceType ) {
case REC_BYTE_F_ASCII:
sourceType = REC_MBYTE_F_SJIS;
break;
case REC_BYTE_V_ANSI:
case REC_BYTE_V_ASCII_LONG:
case REC_BYTE_V_ASCII:
sourceType = REC_MBYTE_V_SJIS;
break;
default:
break;
}
}
}
// ex_expr::exp_return_type retcode = ex_expr::EXPR_OK;
// long warningMark;
// if (diagsArea)
// warningMark= diagsArea->getNumber(DgSqlCode::WARNING_);
// else
// warningMark = 0;
// The following is redefined at a later point for variable length
// strings
inputDesc->getDescItem(entry, SQLDESC_OCTET_LENGTH, &sourceLen, 0, 0, 0,
0);
// Verify that the operand deals with the same rowset as specified
// in the descriptor. This check was added since the pre-processor
// have had generated different information for the module defintion
// file and the cpp file.
// Find rowset size, if any
targetRowsetSize = operand->getRowsetSize();
if (targetRowsetSize > 0) {
// dynamicRowsetSize is specified with ROWSET FOR INPUT SIZE <var>
// or some other rowset syntax; <var> can change at run time
if (dynamicRowsetSize > targetRowsetSize) {
ExRaiseSqlError(heap, &diagsArea, EXE_ROWSET_INDEX_OUTOF_RANGE);
if (diagsArea != atp->getDiagsArea()) {
atp->setDiagsArea(diagsArea);
return ex_expr::EXPR_ERROR;
}
}
if (dynamicRowsetSize > 0) {
targetRowsetSize = dynamicRowsetSize;
}
// do the conversion
source = (char *)&targetRowsetSize;
if (::convDoIt(source,
sizeof(Lng32),
REC_BIN32_SIGNED,
0,
0,
target,
sizeof(Lng32),
REC_BIN32_SIGNED,
0,
0,
0,
0
) != ex_expr::EXPR_OK)
{
if (diagsArea != atp->getDiagsArea())
atp->setDiagsArea(diagsArea);
return ex_expr::EXPR_ERROR;
}
// Increment the location for the next element.
target += sizeof(Lng32);
if (operand->getNullFlag()) {
targetNullInd = target;
target += operand->getNullIndicatorLength();
}
if (operand->getVCIndicatorLength() > 0) {
target += operand->getVCIndicatorLength();
}
// save current source and target datatype attributes since
// they may get changed later in this method.
// Restore them for the next iteration of this for loop.
// Do this only for rowsets, for non-rowsets this loop is executed
// only once.
savedSourceType = sourceType;
savedSourcePrecision = sourcePrecision;
savedSourceScale = sourceScale;
savedSourceLen = sourceLen;
} // if (targetRowsetSize > 0)
else {
inputDesc->getDescItem(entry, SQLDESC_ROWSET_VAR_LAYOUT_SIZE, &rowsetVarLayoutSize,
0, 0, 0, 0);
if (rowsetVarLayoutSize > 0) {
ExRaiseSqlError(heap, &diagsArea, EXE_ROWSET_SCALAR_ARRAY_MISMATCH);
if (diagsArea != atp->getDiagsArea()) {
atp->setDiagsArea(diagsArea);
return ex_expr::EXPR_ERROR;
// Raises an error when query was compiled with scalar param
// and array value is passed in at runtime. When query is compiled
// with array params and some (not all) values are scalars at runtime
// we do not raise an error and we duplicate the scalar value.
}
}
} // else (targetRowsetSize > 0)
Lng32 numToProcess = ((targetRowsetSize > 0) ? targetRowsetSize : 1);
// Define and clear case indexes potentially used for rowsets
ConvInstruction index1 = CONV_UNKNOWN;
ConvInstruction index2 = CONV_UNKNOWN;
ConvInstruction index3 = CONV_UNKNOWN;
for (Lng32 RowNum = 0; RowNum < numToProcess; RowNum++)
{
// Increment the location for the next element.
if ((targetRowsetSize > 0) && (RowNum > 0)) {
target += operand->getStorageLength();
if (operand->getVCIndicatorLength() > 0)
targetVCLenInd += operand->getStorageLength();
if (operand->getNullFlag())
targetNullInd += operand->getStorageLength();
// restore the original datatype attributes.
sourceType = savedSourceType;
sourcePrecision = savedSourcePrecision;
sourceScale = savedSourceScale;
sourceLen = savedSourceLen;
}
if (targetRowsetSize > 0)
inputDesc->setDescItem(0, SQLDESC_ROWSET_HANDLE, RowNum, 0);
inputDesc->getDescItem(entry, SQLDESC_IND_DATA, &indData,
0, 0, 0, 0);
if (indData == 0) {
inputDesc->getDescItem(entry, SQLDESC_IND_PTR, &tempSource,
0, 0, 0, 0);
inputDesc->getDescItem(entry, SQLDESC_ROWSET_IND_LAYOUT_SIZE, &rowsetIndLayoutSize,
0, 0, 0, 0);
if ((!tempSource) && (rowsetIndLayoutSize > 0)) {
ExRaiseSqlError(heap, &diagsArea, EXE_ROWSET_VARDATA_OR_INDDATA_ERROR);
if (diagsArea != atp->getDiagsArea())
atp->setDiagsArea(diagsArea);
return ex_expr::EXPR_ERROR;
}
}
else
tempSource = (char *)&indData;
sourceIndPtr = tempSource;
NABoolean nullMoved = FALSE;
if (sourceIndPtr) {
short temp = 0;
str_cpy_all((char *)&temp, sourceIndPtr, operand->getNullIndicatorLength());
if (inputDesc && inputDesc->thereIsACompoundStatement()
&& (temp > 0)) {
// We may be processing input data with invalid indicators if we
// are in an IF statement
temp = 0;
}
if (temp > 0)
{
// invalid null indicator value.
// Valid indicator values are 0(not null value) and
// -ve (null value).
ExRaiseSqlError(heap, &diagsArea, EXE_CONVERT_STRING_ERROR);
if (diagsArea != atp->getDiagsArea())
atp->setDiagsArea(diagsArea);
setRowNumberInCli(diagsArea, RowNum, targetRowsetSize) ;
if (tolerateNonFatalError && diagsArea->canAcceptMoreErrors())
continue;
else
return ex_expr::EXPR_ERROR;
}
if (operand->getNullFlag()) {
nullMoved = (temp < 0); //if null indicator is a negative
//number, the value is null
str_cpy_all (targetNullInd, sourceIndPtr,
operand->getNullIndicatorLength());
}
else {
if (temp < 0) {
// input is a null value
// error. Trying to move a null input value to a
// non-nullable buffer.
ExRaiseSqlError(heap, &diagsArea, EXE_ASSIGNING_NULL_TO_NOT_NULL);
if (diagsArea != atp->getDiagsArea())
atp->setDiagsArea(diagsArea);
setRowNumberInCli(diagsArea, RowNum, targetRowsetSize) ;
if (tolerateNonFatalError && diagsArea->canAcceptMoreErrors())
continue;
else
return ex_expr::EXPR_ERROR;
}
}
}
else {
// indicator not specified.
if (operand->getNullFlag()) {
// move 0 (non-null value) to first two bytes of buffer.
str_pad(targetNullInd, operand->getNullIndicatorLength(), '\0');
}
}
source = inputDesc -> getVarData(entry);
if (!source) {
continue;
}
if (DFS2REC::isSQLVarChar(sourceType)) {
Lng32 vcIndLen = inputDesc->getVarIndicatorLength(entry);
sourceLen = ExpTupleDesc::getVarLength(source, vcIndLen);
source = &source[vcIndLen];
}
// 12/22/97: added for Unicode Vchar.
if (! nullMoved) {
ex_expr::exp_return_type retcode = ex_expr::EXPR_OK;
Lng32 warningMark;
if (diagsArea)
warningMark= diagsArea->getNumber(DgSqlCode::WARNING_);
else
warningMark = 0;
NABoolean implicitConversion = FALSE;
// if the source and target are not compatible, then do implicit
// conversion if skipTypeCheck is set.
// Furthermore, if restrictedSkipTypeCheck is set, then only
// convert for the conversions that are externally supported.
// See usage of default IMPLICIT_HOSTVAR_CONVERSION in generator
// (GenExpGenerator.cpp) for restricted conversions.
if (NOT areCompatible(sourceType, targetType))
{
if ((NOT skipTypeCheck()) ||
((restrictedSkipTypeCheck()) &&
(NOT implicitConversionSupported(sourceType,
operand->getDatatype()))))
{
ExRaiseSqlError(heap, &diagsArea,EXE_CONVERT_NOT_SUPPORTED);
if (diagsArea != atp->getDiagsArea())
atp->setDiagsArea(diagsArea);
setRowNumberInCli(diagsArea, RowNum, targetRowsetSize) ;
if (tolerateNonFatalError && diagsArea->canAcceptMoreErrors())
continue;
else
return ex_expr::EXPR_ERROR;
}
implicitConversion = TRUE;
} else {
// check charset for source and target if they are CHARACTER types.
if ( DFS2REC::isAnyCharacter(sourceType) &&
NOT CharInfo::isAssignmentCompatible(sourceCharSet, targetCharSet)
)
{
ExRaiseSqlError(heap, &diagsArea, CLI_ASSIGN_INCOMPATIBLE_CHARSET);
if (diagsArea != atp->getDiagsArea())
atp->setDiagsArea(diagsArea);
*diagsArea
<< DgString0(CharInfo::getCharSetName((CharInfo::CharSet)sourceCharSet))
<< DgString1(CharInfo::getCharSetName((CharInfo::CharSet)targetCharSet));
setRowNumberInCli(diagsArea, RowNum, targetRowsetSize) ;
if (tolerateNonFatalError && diagsArea->canAcceptMoreErrors())
continue;
else
return ex_expr::EXPR_ERROR;
}
}
// In the case of VARCHARS, we zero out remaining elements
if (targetRowsetSize > 0) {
Int32 i;
if ((sourceType >= REC_MIN_V_N_CHAR_H)
&& (sourceType <= REC_MAX_V_N_CHAR_H)) {
for (i = strlen(source); i < operand->getLength(); i++) {
target[i] = 0;
}
}
if (DFS2REC::isSQLVarChar(sourceType)) {
for (i = sourceLen; i < operand->getLength(); i++) {
target[i] = 0;
}
}
}
// Check length limit for KANJI/KSC5601/UCS2 ANSI VARCHAR (i.e., there
// should be a NAWchar typed NULL somewhere in the range [1, sourceLen/2]
// in the source. We have to do this for KANJI/KSC because their FS type
// codes are shared with the ISO88591 CHARACTER types and the conversion
// routines for ISO88591 ANSI VARCHAR in convDoIt() can not verify the
// NAWchar nulls.
//
// We also check the ANSI Unicode source here because we then can safely
// use the NAWstrlen() function to obtain the length in the next IF block to
// check code points.
if ((sourceType == REC_BYTE_V_ANSI && CharInfo::is_NCHAR_MP(sourceCharSet)) ||
sourceType == REC_NCHAR_V_ANSI_UNICODE
)
{
Lng32 i = 0;
Int32 sourceLenInWchar = sourceLen/SQL_DBCHAR_SIZE;
NAWchar* sourceInWchar = (NAWchar*)source;
while ((i < sourceLenInWchar) && (sourceInWchar[i] != 0))
i++;
if ( i == sourceLenInWchar ) {
ExRaiseSqlError(heap, &diagsArea, EXE_MISSING_NULL_TERMINATOR);
if (diagsArea != atp->getDiagsArea())
atp->setDiagsArea(diagsArea);
setRowNumberInCli(diagsArea, RowNum, targetRowsetSize) ;
if (tolerateNonFatalError && diagsArea->canAcceptMoreErrors())
continue;
else
return ex_expr::EXPR_ERROR;
};
}
if ( DFS2REC::isAnyCharacter(sourceType) &&
sourceCharSet == CharInfo::UNICODE )
{
Lng32 realSourceLen = sourceLen;
if (sourceType == REC_NCHAR_V_ANSI_UNICODE) {
realSourceLen = NAWstrlen((NAWchar*)source) * SQL_DBCHAR_SIZE;
if ( realSourceLen > sourceLen )
realSourceLen = sourceLen ;
}
realSourceLen /= SQL_DBCHAR_SIZE;
if ( CharInfo::checkCodePoint((NAWchar*)source, realSourceLen,
CharInfo::UNICODE ) == FALSE )
{
// Error code 3400 falls in CLI error code area, but it is
// a perfect fit to use here (as an exeutor error).
ExRaiseSqlError(heap, &diagsArea, (ExeErrorCode)3400);
if (diagsArea != atp->getDiagsArea())
atp->setDiagsArea(diagsArea);
*diagsArea << DgString0(SQLCHARSETSTRING_UNICODE);
setRowNumberInCli(diagsArea, RowNum, targetRowsetSize) ;
if (tolerateNonFatalError && diagsArea->canAcceptMoreErrors())
continue;
else
return ex_expr::EXPR_ERROR;
}
}
// if incompatible conversions are to be done,
// and the source is REC_BYTE_V_ANSI,
// and the target is not a string type, convert the source to
// REC_BYTE_F_ASCII first. Conversion from V_ANSI is currently
// only supported if target is a string type.
if ((implicitConversion) &&
((sourceType == REC_BYTE_V_ANSI) &&
((targetType < REC_MIN_CHARACTER) ||
(targetType > REC_MAX_CHARACTER))))
{
intermediate = new (heap) char[sourceLen];
// Initialize index for this convDoIt call, if needed
if (index1 == CONV_UNKNOWN) {
ex_conv_clause tempClause;
index1 = tempClause.findInstruction(sourceType,
sourceLen,
REC_BYTE_F_ASCII,
sourceLen,
0);
}
if (::convDoIt(source,
sourceLen,
sourceType,
sourcePrecision,
sourceScale,
intermediate,
sourceLen,
REC_BYTE_F_ASCII,
sourcePrecision,
sourceScale,
NULL,
0,
heap,
&diagsArea,
index1) != ex_expr::EXPR_OK) {
if (diagsArea != atp->getDiagsArea())
atp->setDiagsArea(diagsArea);
NADELETEBASIC(intermediate, heap);
setRowNumberInCli(diagsArea, RowNum, targetRowsetSize) ;
if (tolerateNonFatalError && diagsArea->canAcceptMoreErrors())
continue;
else
return ex_expr::EXPR_ERROR;
};
sourceType = REC_BYTE_F_ASCII;
source = intermediate;
} // sourceType == REC_BYTE_V_ANSI
if ((sourcePrecision > 0) &&
((sourceType >= REC_MIN_BINARY) &&
(sourceType <= REC_MAX_BINARY) &&
(sourceType == operand->getDatatype()) &&
(sourcePrecision == operand->getPrecision()) ||
(DFS2REC::isAnyCharacter(sourceType) && !suppressCharLimitCheck())))
{
// Validate source precision. Make the source precision
// zero, that will trigger the validation. See method
// checkPrecision in exp_conv.cpp. This also applies to
// source chars or varchars, where we don't trust the
// precision (max number of chars) in the value
sourcePrecision = 0;
}
ULng32 convFlags = 0;
if ((NOT implicitConversion) &&
(((sourceType == REC_DATETIME) ||
((sourceType >= REC_MIN_INTERVAL) &&
(sourceType <= REC_MAX_INTERVAL))) &&
(NOT isIFIO)))
{
// this is a datetime or interval conversion.
// Convert the external string datetime/interval to its
// internal format. The conversion follows the
// same rules as "CAST(char TO datetime/interval)".
// If the two interval types are not the same, then first
// convert source from its string to its internal format,
// and then convert to the target interval type.
if (sourceType == REC_DATETIME)
{
if ((sourcePrecision != operand->getPrecision()) ||
(sourceScale != operand->getScale()))
{
// source and target must have the same datetime_code,
// they must both be date, time or timestamp.
ExRaiseSqlError(heap, &diagsArea,
EXE_CONVERT_NOT_SUPPORTED);
if (diagsArea != atp->getDiagsArea())
atp->setDiagsArea(diagsArea);
setRowNumberInCli(diagsArea, RowNum, targetRowsetSize) ;
if (tolerateNonFatalError && diagsArea->canAcceptMoreErrors())
continue;
else
return ex_expr::EXPR_ERROR;
}
else
{
sourceType = REC_BYTE_F_ASCII;
sourcePrecision = 0;
sourceScale = SQLCHARSETCODE_ISO88591; // assume target charset is ASCII-compatible
convFlags |= CONV_NO_HADOOP_DATE_FIX;
}
}
else if ((sourceType == targetType) &&
(sourcePrecision == operand->getPrecision()) &&
(sourceScale == operand->getScale()))
{
// 'interval' source and target match all attributes.
// Convert source(string external) to target(internal) format
sourceType = REC_BYTE_F_ASCII;
sourcePrecision = 0; // TBD $$$$ add source max chars later
sourceScale = SQLCHARSETCODE_ISO88591; // assume target charset is ASCII-compatible
convFlags |= CONV_ALLOW_SIGN_IN_INTERVAL;
}
else
{
// interval type and source/target do not match.
// Convert source string interval to corresponding
// internal format.
short intermediateLen = SQL_LARGE_SIZE;
intermediate = new (heap) char[intermediateLen];
convFlags |= CONV_ALLOW_SIGN_IN_INTERVAL;
// Initialize index for this convDoIt call, if needed
if (index2 == CONV_UNKNOWN) {
ex_conv_clause tempClause;
index2 = tempClause.findInstruction(REC_BYTE_F_ASCII,
sourceLen,
sourceType,
intermediateLen,
0 - sourceScale);
}
if (noDatetimeValidation())
convFlags |= CONV_NO_DATETIME_VALIDATION;
if (::convDoIt(source,
sourceLen,
REC_BYTE_F_ASCII,
0, //sourcePrecision,
0, //sourceScale,
intermediate,
intermediateLen,
sourceType,
sourcePrecision,
sourceScale,
NULL,
0,
heap,
&diagsArea,
index2,
0,
convFlags) != ex_expr::EXPR_OK)
{
if (diagsArea != atp->getDiagsArea())
atp->setDiagsArea(diagsArea);
NADELETEBASIC(intermediate, heap);
setRowNumberInCli(diagsArea, RowNum, targetRowsetSize) ;
if (tolerateNonFatalError && diagsArea->canAcceptMoreErrors())
continue;
else
return ex_expr::EXPR_ERROR;
};
source = intermediate;
sourceLen = intermediateLen;
}
}
// Initialize index for this convDoIt call, if needed
if (index3 == CONV_UNKNOWN) {
ex_conv_clause tempClause;
index3 = tempClause.findInstruction(sourceType,
sourceLen,
operand->getDatatype(),
operand->getLength(),
sourceScale -
operand->getScale());
}
if (noDatetimeValidation())
convFlags |= CONV_NO_DATETIME_VALIDATION;
if ((sourceType==REC_BLOB) || (sourceType ==REC_CLOB))
{
// the first 4 bytes of data are actually the variable
// length indicator
Lng32 VCLen;
str_cpy_all((char *) &VCLen, source, sizeof(Int32));
sourceLen = (Lng32) VCLen;
source = &source[sizeof(Int32)];
}
retcode = ::convDoIt(source,
sourceLen,
sourceType,
sourcePrecision,
sourceScale,
target,
operand->getLength(),
operand->getDatatype(),
operand->getPrecision(),
operand->getScale(),
targetVCLenInd,
operand->getVCIndicatorLength(),
heap,
&diagsArea,
index3,
0,
convFlags);
// NADELETEBASIC(intermediate, heap);
if (retcode != ex_expr::EXPR_OK)
{
if (diagsArea != atp->getDiagsArea())
atp->setDiagsArea(diagsArea);
setRowNumberInCli(diagsArea, RowNum, targetRowsetSize) ;
if (tolerateNonFatalError && diagsArea->canAcceptMoreErrors())
continue;
else
return ex_expr::EXPR_ERROR;
}
else if (diagsArea && diagsArea->mainSQLCODE() == EXE_STRING_OVERFLOW)
{
Int32 warningMark2 = diagsArea->getNumber(DgSqlCode::WARNING_);
Int32 counter = warningMark2 - warningMark;
diagsArea->deleteWarning(warningMark2-counter);
ExRaiseSqlError(heap, &diagsArea, EXE_STRING_OVERFLOW);
if (diagsArea != atp->getDiagsArea())
atp->setDiagsArea(diagsArea);
setRowNumberInCli(diagsArea, RowNum, targetRowsetSize) ;
if (tolerateNonFatalError && diagsArea->canAcceptMoreErrors())
continue;
else
return ex_expr::EXPR_ERROR;
}
// If the query contains ROWSET FOR INPUT SIZE <var> or
// ROWSET <var> ( <list> ) then we get the number of entries
// from <var>
if (operand->getHVRowsetForInputSize() ||
operand->getHVRowsetLocalSize()) {
short targetType = operand->getDatatype();
switch (targetType) {
case REC_BIN8_SIGNED :
if (*((Int8 *) target) <= 0) {
//raise error
ExRaiseSqlError(heap, &diagsArea, EXE_ROWSET_NEGATIVE_SIZE);
if (diagsArea != atp->getDiagsArea())
atp->setDiagsArea(diagsArea);
return ex_expr::EXPR_ERROR;
}
else {
dynamicRowsetSize = *((Int8 *) target);
break;
}
case REC_BIN8_UNSIGNED :
if (*((UInt8 *) target) == 0) {
//raise error
ExRaiseSqlError(heap, &diagsArea, EXE_ROWSET_NEGATIVE_SIZE);
if (diagsArea != atp->getDiagsArea())
atp->setDiagsArea(diagsArea);
return ex_expr::EXPR_ERROR;
}
else {
dynamicRowsetSize = *((UInt8 *) target);
break;
}
case REC_BIN16_SIGNED :
if (*((short *) target) <= 0) {
//raise error
ExRaiseSqlError(heap, &diagsArea, EXE_ROWSET_NEGATIVE_SIZE);
if (diagsArea != atp->getDiagsArea())
atp->setDiagsArea(diagsArea);
return ex_expr::EXPR_ERROR;
}
else {
dynamicRowsetSize = *((short *) target);
break;
}
case REC_BIN16_UNSIGNED :
if (*((unsigned short *) target) == 0) {
//raise error
ExRaiseSqlError(heap, &diagsArea, EXE_ROWSET_NEGATIVE_SIZE);
if (diagsArea != atp->getDiagsArea())
atp->setDiagsArea(diagsArea);
return ex_expr::EXPR_ERROR;
}
else {
dynamicRowsetSize = *((unsigned short *) target);
break;
}
case REC_BIN32_SIGNED :
if (*((Lng32 *) target) <= 0) {
//raise error
ExRaiseSqlError(heap, &diagsArea, EXE_ROWSET_NEGATIVE_SIZE);
if (diagsArea != atp->getDiagsArea())
atp->setDiagsArea(diagsArea);
return ex_expr::EXPR_ERROR;
}
else {
dynamicRowsetSize = *((Lng32 *) target); //long is the same as int
break;
}
case REC_BIN32_UNSIGNED :
if (*((ULng32 *) target) == 0) { // unsigned long is the same as unsigned int
//raise error
ExRaiseSqlError(heap, &diagsArea, EXE_ROWSET_NEGATIVE_SIZE);
if (diagsArea != atp->getDiagsArea())
atp->setDiagsArea(diagsArea);
return ex_expr::EXPR_ERROR;
}
else {
dynamicRowsetSize = *((ULng32 *) target);
break;
}
default:
//raise error
ExRaiseSqlError(heap, &diagsArea,EXE_ROWSET_WRONG_SIZETYPE);
if (diagsArea != atp->getDiagsArea())
atp->setDiagsArea(diagsArea);
return ex_expr::EXPR_ERROR;
}
}
// up- or down-scale target, if there is a difference
// in scale, but be careful to ignore scale used as charset
if ((operand->getDatatype() >= REC_MIN_NUMERIC) &&
(operand->getDatatype() <= REC_MAX_NUMERIC) &&
(sourceScale != operand->getScale()) &&
::scaleDoIt(target,
operand->getLength(),
operand->getDatatype(),
sourceScale,
operand->getScale(),
sourceType,
heap) != ex_expr::EXPR_OK)
{
ExRaiseSqlError(heap, &diagsArea, EXE_NUMERIC_OVERFLOW);
if (diagsArea != atp->getDiagsArea())
atp->setDiagsArea(diagsArea);
setRowNumberInCli(diagsArea, RowNum, targetRowsetSize) ;
if (tolerateNonFatalError && diagsArea->canAcceptMoreErrors())
continue;
else
return ex_expr::EXPR_ERROR;
}
} // if (! nullMoved)
} // loop over rowset elements (RowNum)
} // if clause == IN_OUT type
next_clause:
clause = clause->getNextClause();
} // while(clause)
return ex_expr::EXPR_OK;
}
Lng32 InputOutputExpr::getCompiledOutputRowsetSize(atp_struct *atp)
{
ex_clause * clause = getClauses();
Attributes * operand;
Lng32 sourceRowsetSize = 0;
// Note that every output operands must participate in rowsets, or none of
// them. That is, you cannot mix rowset host variables and simple host
// variables for output purposes (INTO clause).
if (clause) {
operand = clause->getOperand(0);
sourceRowsetSize = operand->getRowsetSize();
}
return sourceRowsetSize;
}
ex_expr::exp_return_type InputOutputExpr::addDescInfoIntoStaticDesc
(Descriptor * desc, NABoolean isInput)
{
ex_clause *clause = getClauses();
short entry = 0;
if(isCall() && desc->isDescTypeWide()){
while(clause){
if(clause->getType() == ex_clause::INOUT_TYPE){
entry = ((ex_inout_clause *)clause)->getParamIdx();
if(entry > desc->getUsedEntryCount())
continue;
desc->setDescItem(entry, SQLDESC_NAME, 0,
((ex_inout_clause *)clause)->getName());
short paramMode = ((ex_inout_clause *)clause)->getParamMode();
short ordPos = ((ex_inout_clause *)clause)->getOrdPos();
desc->setDescItem(entry, SQLDESC_PARAMETER_MODE, paramMode, 0);
desc->setDescItem(entry, SQLDESC_PARAMETER_INDEX, entry, 0);
desc->setDescItem(entry, SQLDESC_ORDINAL_POSITION, ordPos, 0);
}
clause = clause->getNextClause();
}
}
else if(isCall() && !desc->isDescTypeWide()){
while (clause && entry < desc->getUsedEntryCount()){
if (clause->getType() == ex_clause::INOUT_TYPE){
entry++;
desc->setDescItem(entry, SQLDESC_NAME, 0,
((ex_inout_clause *)clause)->getName());
short paramMode = ((ex_inout_clause *)clause)->getParamMode();
short paramIdx = ((ex_inout_clause *)clause)->getParamIdx();
short ordPos = ((ex_inout_clause *)clause)->getOrdPos();
desc->setDescItem(entry, SQLDESC_PARAMETER_MODE, paramMode, 0);
desc->setDescItem(entry, SQLDESC_PARAMETER_INDEX, paramIdx, 0);
desc->setDescItem(entry, SQLDESC_ORDINAL_POSITION, ordPos, 0);
}
clause = clause->getNextClause();
}
}
else{
while (clause && entry < desc->getUsedEntryCount()){
if (clause->getType() == ex_clause::INOUT_TYPE){
entry++;
desc->setDescItem(entry, SQLDESC_NAME, 0,
((ex_inout_clause *)clause)->getName());
if(isInput)
desc->setDescItem(entry, SQLDESC_PARAMETER_MODE,
PARAMETER_MODE_IN, 0);
else
desc->setDescItem(entry, SQLDESC_PARAMETER_MODE,
PARAMETER_MODE_OUT, 0);
desc->setDescItem(entry, SQLDESC_PARAMETER_INDEX, entry, 0);
desc->setDescItem(entry, SQLDESC_ORDINAL_POSITION, 0, 0);
}
clause = clause->getNextClause();
}
}
return ex_expr::EXPR_OK;
}
Lng32 InputOutputExpr::getMaxParamIdx()
{
Lng32 maxParamIdx = 0;
ex_clause *clause = getClauses();
while(clause){
if(clause->getType() == ex_clause::INOUT_TYPE){
Lng32 paramIdx = ((ex_inout_clause *)clause)->getParamIdx();
if(paramIdx > maxParamIdx)
maxParamIdx = paramIdx;
}
clause = clause->getNextClause();
}
return maxParamIdx;
}