blob: 5a3dc49574468f095049c70f932429cf0d2b7227 [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: CmpStoredProc.C
* Description: The implementation of the internal stored procedure related
* classes.
*
*
* Created: 03/14/97
* Language: C++
*
*
*
*
*****************************************************************************
*/
#define SQLPARSERGLOBALS_FLAGS
#define SQLPARSERGLOBALS_NADEFAULTS
#include "CmpStoredProc.h"
#include "TrafDDLdesc.h"
#include "parser.h"
#include "str.h"
#include "ElemDDLColDef.h"
#include "CmpErrors.h"
#include "CmpContext.h"
#include "CmpMessage.h" // for CmpMessageISPRequest
#include "ComDiags.h"
#include "CmpDescribe.h" // for sendAllControls
#include "CharType.h"
#include "NumericType.h"
#include "DatetimeType.h"
#include "DTICommonType.h" // for DatetimeIntervalCommonType
#include "ItemColRef.h" // for ConstValue
#include "ItemNAType.h" // for NATypeToItem
#include "ItemOther.h" // for ItemList
#include "NAExit.h"
#include "NAMemory.h"
#include "NAString.h"
#include "ParserMsg.h"
#include "StoredProcInterface.h"
#include <time.h> // timestamp to generate a unique table name
#include <memory.h> // for memset
#include "SqlParserGlobalsCmn.h"
// helper routines used in this cpp file
static const char emptyString[] = "";
inline static char *copyString(const NAString &s) // cf. readRealArk.cpp
{
char *c = new(CmpCommon::statementHeap()) char[s.length()+1];
str_cpy_all(c, s.data(), s.length()+1);
return c;
}
static void CmpSPERROR2Diags(const SP_ERROR_STRUCT* spError,
ComDiagsArea* diags)
{
if ( !diags )
return;
if (spError[0].error == arkcmpErrorISPMergeCatDiags)
{
// Special error message to indicate that errors should really be
// merged in from the catalog manager diags area.
// not supported, raise unsupported error instead
*diags << DgSqlCode(-4222)
<< DgString0("CmpSPERROR2Diags");
return;
}
for ( Int32 i=0; i< SP_MAX_ERROR_STRUCTS; i++)
{
const SP_ERROR_STRUCT* pSET = &(spError[i]);
if (pSET->error)
{
*diags << DgSqlCode(pSET->error);
if(pSET->error == -20067) { //IDS_UTILITIES_BADSYNTAX = 20067
// If utilities parser returned syntax error for syntax based
// utilities, error IDS_UTILITIES_BADSYNTAX is returned by
// utilities code to mxmcp.
// pSET->optionalString[1] has the utilities command from command line.
// pSET->optionalInteger[0] has the approximate position of error in
// the syntax of the utilities command given by user in command line.
// The approximate error position is returned by utilities parser.
// Function StoreSyntaxError(.....) takes the information to put a
// caret (^) in the approximate position of the command and the
// information is saved in diags.
StoreSyntaxError(pSET->optionalString[1], // const char * input_str
pSET->optionalInteger[0], // Int32 input_pos
*diags, // ComDiagsArea & diagsArea
0, // Int32 dgStrNum
CharInfo::UTF8, // CharInfo::CharSet input_str_cs
CharInfo::UTF8); // CharInfo::CharSet terminal_cs
}
else{
if ( pSET->optionalString[0] &&
pSET->optionalString[0] != (char *) ComDiags_UnInitialized_Int )
*diags << DgString0(pSET->optionalString[0]);
if ( pSET->optionalString[1] &&
pSET->optionalString[1] != (char *)ComDiags_UnInitialized_Int )
*diags << DgString1(pSET->optionalString[1]);
if ( pSET->optionalString[2] &&
pSET->optionalString[2] != (char *)ComDiags_UnInitialized_Int )
*diags << DgString2(pSET->optionalString[2]);
if ( pSET->optionalString[3] &&
pSET->optionalString[3] != (char *)ComDiags_UnInitialized_Int )
*diags << DgString3(pSET->optionalString[3]);
if ( pSET->optionalString[4] &&
pSET->optionalString[4] != (char *)ComDiags_UnInitialized_Int )
*diags << DgString4(pSET->optionalString[4]);
if ( pSET->optionalInteger[0] != ComDiags_UnInitialized_Int )
*diags << DgInt0(pSET->optionalInteger[0]);
if ( pSET->optionalInteger[1] != ComDiags_UnInitialized_Int )
*diags << DgInt1(pSET->optionalInteger[1]);
if ( pSET->optionalInteger[2] != ComDiags_UnInitialized_Int )
*diags << DgInt2(pSET->optionalInteger[2]);
if ( pSET->optionalInteger[3] != ComDiags_UnInitialized_Int )
*diags << DgInt3(pSET->optionalInteger[3]);
if ( pSET->optionalInteger[4] != ComDiags_UnInitialized_Int )
*diags << DgInt4(pSET->optionalInteger[4]);
}
}
else if ( i==0 )
{
// this is the case that ISP implementation does not return any
// SP_ERROR_STRUCT info back, but does return with error status.
*diags << DgSqlCode(arkcmpErrorISPNoSPError);
break;
}
else
// no more SP_ERROR_STRUCT.
break;
}
}
// -----------------------------------------------------------------------
// helper routines used by both CmpSPInputFormat and CmpSPOutputFormat
// -----------------------------------------------------------------------
// This routine converts a field description in SP_FIELDDESC_STRUCT
// into an ElemDDLColDef. Error will be put into context
static ElemDDLColDef* SPFieldDesc2ElemDDLColDef(SP_FIELDDESC_STRUCT* fd,
CmpContext* context)
{
ElemDDLColDef* elemDDL=0;
if (!fd)
return 0;
Parser parser(context);
if ( !(elemDDL = parser.parseColumnDefinition(fd->COLUMN_DEF, strlen(fd->COLUMN_DEF),
CharInfo::UTF8
)))
{
return 0; // the error is put into context_ diags by parser already
}
return elemDDL;
}
// -----------------------------------------------------------------------
// Methods of CmpSPInputFormat
// -----------------------------------------------------------------------
CmpSPInputFormat::CmpSPInputFormat(CmpContext* context)
{
context_ = context;
inputType_ = 0;
}
CmpSPInputFormat::~CmpSPInputFormat()
{
}
NABoolean CmpSPInputFormat::SetFormat(Lng32 ncols,
SP_FIELDDESC_STRUCT* fields)
{
SP_FIELDDESC_STRUCT* fd = fields;
ElemDDLColDef* elem=0;
for (Int32 i=0; i < ncols; i++, fd++)
{
if ( !( elem = SPFieldDesc2ElemDDLColDef(fd, context_) ) )
return FALSE;
ItemExpr* item = new((CollHeap*)context_->statementHeap())
NATypeToItem((NAType*)elem->getColumnDataType());
if (inputType_)
inputType_ = new((CollHeap*)context_->statementHeap())
ItemList(inputType_, item);
else
inputType_ = item;
}
return TRUE;
}
// -----------------------------------------------------------------------
// Methods of CmpSPOutputFormat
// -----------------------------------------------------------------------
CmpSPOutputFormat::CmpSPOutputFormat(CmpContext* context)
{
context_ = context;
}
NABoolean CmpSPOutputFormat::SetFormat(Lng32 nCols,
const char* tableName,
SP_FIELDDESC_STRUCT* fields,
Lng32 nKeys,
SP_KEYDESC_STRUCT *keys)
{
// To convert the SP_FIELDDESC_STRUCT into columns_desc,
// 1. call the parser to parse the column definition.
// 2. The ElemDDLColDef is used to generate columns_desc.
// 3. Refer to catman/CatExecCreateTable.C/CatBuildColumnList
// to convert ElemDDLColDefArray to CatColumnList.
// 4. Refer to sqlcat/readRealArk.C/convertColumns to convert
// CatColumnList to columns_desc.
// Note : 03/18/97.
// It is not a good practice to clone the conversion code. The
// correct way to handle this is to call CatBuildColumnList and
// convertColumns since it will conforms to however catman processes
// the DDL queries from StmtDDLCreateTable RelExpr. But in
// CatBuildColumnList routine, the CatRWBaseTable is created which
// involves the SOL cache routines and it will check the security for
// creating tables and a whole lot more logics deep in catman.
// copying the code is much easier. ( Ya, I'm copying
// the code and blame it to someone else :-) ).
// Since this is for the internally developed stored
// procedures, once there are changes in the data types supported and
// requires changes in CatBuildColumnList or convertColumns routines.
// The following conversion code needs to be changed accordingly.
// Currently (03/29/97), catman has routines ( internal usage only )
// to generate the in memory virtual table structure without going
// through SOL. It is used for index table ( i.e. with type E_INDEX )
// Once this interface is externalized, it should be used here to
// go through catman code to get desc structures.
nCols_ = nCols;
// set up tableDesc_, allocate the storage from allocate routine,
// so the delete of the tableDesc_ could be done correctly.
// As in sqlcat/desc.h TrafAllocateDDLdesc(), allocate routine gets
// the storage from HEAP ( i.e. CmpCommon::statementHeap() ).
// Likewise, copyString should be the same implementation as in
// readRealArk.cpp. It was externalized originally, but being put as
// static function by someone. Currently the code copyString is cloned
// in the beginning of the file. ( which will allocate the memory from
// statementHeap ).
TrafDesc* table_desc = TrafAllocateDDLdesc(DESC_TABLE_TYPE, NULL);
tableDesc_ = table_desc;
char* objName = copyString(NAString(tableName));
table_desc->tableDesc()->tablename = objName;
table_desc->tableDesc()->record_length = 0; // to be set later in generator.
table_desc->tableDesc()->colcount = (Int32)nCols;
TrafDesc * files_desc = TrafAllocateDDLdesc(DESC_FILES_TYPE, NULL);
table_desc->tableDesc()->files_desc = files_desc;
// populate the TrafColumnsDesc
TrafDesc* prev_col_desc = 0;
TrafDesc* first_col_desc = 0;
for ( Int32 i=0; i < nCols; i++ )
{
TrafDesc* column_desc = TrafAllocateDDLdesc(DESC_COLUMNS_TYPE, NULL);
if (prev_col_desc)
prev_col_desc->next = column_desc;
else
first_col_desc = column_desc;
prev_col_desc = column_desc;
if ( !getColumnDesc( &(fields[i]), (column_desc->columnsDesc())))
{
*(context_->diags()) << DgSqlCode(arkcmpErrorISPFieldDef);
tableDesc_ = 0; // since HEAP is from statement heap, it will
// be removed automatically at the end of statement.
return FALSE;
}
column_desc->columnsDesc()->colnumber = i;
}
table_desc->tableDesc()->columns_desc = first_col_desc;
// populate index_desc and key_desc
TrafDesc* keys_desc = 0;
if ( !getKeysDesc( nKeys, keys, keys_desc) )
{
*(context_->diags()) << DgSqlCode(arkcmpErrorISPFieldDef);
tableDesc_ = 0;
return FALSE;
}
TrafDesc * index_desc = TrafAllocateDDLdesc(DESC_INDEXES_TYPE, NULL);
index_desc->indexesDesc()->tablename = objName;
index_desc->indexesDesc()->indexname = objName;
index_desc->indexesDesc()->keytag = 0; // primary index
index_desc->indexesDesc()->record_length =
table_desc->tableDesc()->record_length;
index_desc->indexesDesc()->colcount =
table_desc->tableDesc()->colcount;
index_desc->indexesDesc()->blocksize = 4096; // anything > 0
TrafDesc * i_files_desc = TrafAllocateDDLdesc(DESC_FILES_TYPE, NULL);
index_desc->indexesDesc()->files_desc = i_files_desc;
index_desc->indexesDesc()->keys_desc = keys_desc;
table_desc->tableDesc()->indexes_desc = index_desc;
return TRUE;
}
CmpSPOutputFormat::~CmpSPOutputFormat()
{
}
NABoolean CmpSPOutputFormat::getColumnDesc(SP_FIELDDESC_STRUCT* fDesc,
TrafColumnsDesc* colsDesc )
{
ElemDDLColDef* elemDDL;
if (! (elemDDL=SPFieldDesc2ElemDDLColDef(fDesc, context_)) )
return FALSE;
else
{
// convert ElemDDLColViewDef into CatColumn, then column_desc
if ( !ElemDDLColDef2ColumnDescStruct
(elemDDL, tableDesc_->tableDesc()->tablename, colsDesc) )
return FALSE;
}
return TRUE;
}
NABoolean CmpSPOutputFormat::getKeysDesc(Lng32 nKeys,
SP_KEYDESC_STRUCT* keys,
TrafDesc* &keysDesc)
{
// key is not supported yet in FCS ( Sep. 97 release ) needs to rework on this.
TrafDesc* prev_key_desc = 0;
TrafDesc* first_key_desc = 0;
for ( Int32 i=0; i < nKeys; i++ )
{
TrafDesc* key_desc = TrafAllocateDDLdesc(DESC_KEYS_TYPE, NULL);
if (prev_key_desc)
prev_key_desc->next = key_desc;
else
first_key_desc = key_desc;
prev_key_desc = key_desc;
key_desc->keysDesc()->keyseqnumber = i;
// TODO, find out the tablecolnumber from column name ????
key_desc->keysDesc()->tablecolnumber = 0;
key_desc->keysDesc()->setDescending(FALSE);
}
keysDesc = first_key_desc;
return TRUE;
}
NABoolean CmpSPOutputFormat::ElemDDLColDef2ColumnDescStruct
(ElemDDLColDef* elem,
const char* tableName,
TrafColumnsDesc* colDesc)
{
// Just copy the pointer for this name --
// no need to alloc + strcpy a la copyString
// colDesc->tablename = (char *)tableName;
colDesc->colname = copyString(elem->getColumnName());
// colDesc->colnumber, filled outside
NAType* genericType = elem->getColumnDataType();
colDesc->datatype = genericType->getFSDatatype();
colDesc->length = genericType->getNominalSize();
colDesc->pictureText = (char *)emptyString;
// The logic for converting from an NAType to column_desc is also
// in the catman code -- readRealArk.cpp::convertColumn(). Any changes
// there must be reflected here as well and vice versa.
if ( genericType->getTypeQualifier() == NA_NUMERIC_TYPE )
{
NumericType & nt = *((NumericType*)genericType);
colDesc->scale = nt.getScale();
// if this is a float (real or double) datatype,
// then get the precision. Otherwise, for other
// numeric type, get the precision only if it
// is not binary precision.
if(nt.isExact() && nt.binaryPrecision() &&
(nt.getFSDatatype() != REC_BPINT_UNSIGNED))
{
colDesc->precision = 0;
}
else
{
colDesc->precision = nt.getPrecision();
}
}
else
{
colDesc->scale = 0;
colDesc->precision = 0;
}
if ( genericType->getTypeQualifier() == NA_CHARACTER_TYPE )
{
CharType & charType = (CharType &) *genericType;
colDesc->character_set = charType.getCharSet();
colDesc->encoding_charset = charType.getEncodingCharSet();
colDesc->collation_sequence = charType.getCollation();
colDesc->setUpshifted(charType.isUpshifted());
}
if ( genericType->getTypeQualifier() == NA_DATETIME_TYPE ||
genericType->getTypeQualifier() == NA_INTERVAL_TYPE )
{
DatetimeIntervalCommonType& dti =
(DatetimeIntervalCommonType& )*genericType;
colDesc->datetimestart = dti.getStartField();
colDesc->datetimeend = dti.getEndField();
colDesc->datetimefractprec = dti.getFractionPrecision();
colDesc->intervalleadingprec = dti.getLeadingPrecision();
}
// offset, to be done (do we need it?)
colDesc->setNullable(NOT elem->isNotNullConstraintSpecified());
colDesc->colclass = 'U';
colDesc->setAdded(FALSE);
// defaultclass, to be done? (not referenced, not needed)
ConstValue *pDefVal = (ConstValue*)elem->getDefaultValueExpr();
if (!pDefVal || pDefVal->isNull())
colDesc->defaultvalue = NULL;
else
colDesc->defaultvalue = copyString(pDefVal->getConstStr());
colDesc->colFlags = 0;
return TRUE;
}
// -----------------------------------------------------------------------
// Methods of CmpSPExecDataItem
// -----------------------------------------------------------------------
CmpSPExecDataItem::CmpSPExecDataItem(ULng32 exprSize,
void* expr,
ULng32 dataSize,
void* data,
CmpContext* context)
{
exprSize_ = exprSize;
expr_ = expr;
dataSize_ = dataSize;
data_ = data;
context_ = context;
SPFuncsDiags_ = ComDiagsArea::allocate(context->heap());
}
CmpSPExecDataItem::~CmpSPExecDataItem()
{
if (SPFuncsDiags_)
SPFuncsDiags_->decrRefCount();
}
CollHeap* CmpSPExecDataItem::wHeap()
{
return context_->heap();
}
// -----------------------------------------------------------------------
// Methods of CmpISPDataItemInput
// -----------------------------------------------------------------------
CmpSPExecDataItemInput::CmpSPExecDataItemInput(ULng32 exprSize,
void* expr,
ULng32 dataSize,
void* data,
CmpContext* context)
: CmpSPExecDataItem(exprSize, expr,
dataSize, data, context)
{
CMPASSERT(ExSPPrepareInputBuffer(data_) == 0);
CMPASSERT(ExSPPosition(data_) == 0);
currentRow_ = 0;
rowLength_ = 0;
control_ = 0;
}
CmpSPExecDataItemInput::~CmpSPExecDataItemInput()
{
}
short CmpSPExecDataItemInput::next()
{
short status = 0;
status = ExSPGetInputRow(data_, control_, currentRow_, rowLength_);
CMPASSERT(status != -1);
return status;
}
// -----------------------------------------------------------------------
// Methods of CmpISPDataItemReply
// -----------------------------------------------------------------------
CmpSPExecDataItemReply::CmpSPExecDataItemReply(ULng32 exprSize,
void* expr,
ULng32 dataRowSize,
ULng32 dataTotalSize,
CmpSPExecDataItemInput* inputData,
CollHeap* outHeap,
CmpContext* context)
: CmpSPExecDataItem(exprSize, expr, dataTotalSize, 0, context)
{
// allocate the reply buffer
outHeap_ = outHeap;
// data_ has to come from outHeap, because it will be taken away to be sent back to
// executor.
data_ = new(outHeap) char[dataTotalSize];
rowLength_ = dataRowSize; // passed in from executor
rowBuffer_ = new(context->statementHeap()) char[rowLength_];
memset(rowBuffer_, 0, rowLength_);
CMPASSERT(ExSPInitReplyBuffer(data_, dataTotalSize) == 0);
inputData_ = inputData;
rowExist_ = FALSE;
diagsExist_ = FALSE;
EORExist_ = FALSE;
}
CmpSPExecDataItemReply::~CmpSPExecDataItemReply()
{
NADELETEBASIC((char *)data_, outHeap_);
NADELETEBASIC((char *)rowBuffer_, context_->statementHeap());
diags_.clear(); // this is to avoid memory leak.
}
NABoolean CmpSPExecDataItemReply::prepare()
{
return ( ExSPPrepareReplyBuffer(data_) == 0 );
}
NABoolean CmpSPExecDataItemReply::MoveDiags(const SP_ERROR_STRUCT* error, const SP_STATUS spStatus)
{
// Copy the error information from SP_ERROR_STRUCT into diags_
diagsExist_ = TRUE;
EORExist_ = TRUE;
diags_.clear();
CmpSPERROR2Diags(error, &diags_);
return TRUE;
}
NABoolean CmpSPExecDataItemReply::MergeDiags(ComDiagsArea* pDiags)
{
if (pDiags == NULL ||
pDiags->getNumber() <= 0)
return FALSE;
// merge the warning and error information from *pDiags into diags_
diagsExist_ = TRUE;
EORExist_ = TRUE;
diags_.mergeAfter(*pDiags);
return TRUE;
}
short CmpSPExecDataItemReply::AddARow()
{
// set the rowExist_ flag to be TRUE
// call the executor provided routines to move the data into buffer to be sent
// back to executor.
// if moved in successfully, reset the rowExist_ flag.
// otherwise, return with buffer full and wait for the next GetNext request from
// executor with enough buffer size to fill the row.
rowExist_ = TRUE;
short status = 0;
status = ExSPPutReplyRow(data(),
inputData_->control(),
(char*)rowBuffer_,
rowLength_, 0);
if ( status == 1 ) // buffer is full
return 1;
memset(rowBuffer_, 0, rowLength_);
rowExist_ = FALSE;
return status;
}
short CmpSPExecDataItemReply::AddEOR()
{
// if diagsExist_, sent back the diags info along with end of row indicator
// if moved in successfully, reset the diagsExist_ and EORExist_ flag.
// if buffer is full, just return 1, wait for executor to send enough buffer.
short status= 0;
EORExist_ = TRUE;
if (diagsExist_)
{
status = ExSPPutReplyRow(data(), inputData_->control(), 0, 0,
&diags_);
if ( status == 1) // buffer is full
return 1;
diagsExist_ = FALSE;
diags_.clear();
}
if (status != 2) // In case of warning we have already returned an EOD
status = ExSPPutReplyRow(data(), inputData_->control(), 0, 0, 0);
if ( status == 1 ) // buffer is full
return 1;
EORExist_ = FALSE;
return status;
}
void CmpSPExecDataItemReply::allocateData(ULng32 size)
{
NADELETEBASIC((char *)data_, outHeap_);
data_ = new(outHeap_) char[size];
CMPASSERT(ExSPInitReplyBuffer(data_, size) == 0);
}
// -----------------------------------------------------------------------
// Methods of CmpISPDataObject
// -----------------------------------------------------------------------
CmpISPDataObject::CmpISPDataObject(CmpMessageISPRequest* req,
CmpInternalSP* isp,
CollHeap* outheap,
CmpContext* context)
: input_(req->inputExprSize(), (void*)req->inputExpr(),
req->inputDataSize(), (void*)req->inputData(), context),
key_(req->keyExprSize(), (void*)req->keyExpr(),
req->keyDataSize(), (void*)req->keyData(), context),
output_(req->outputExprSize(), (void*)req->outputExpr(),
req->outputRowSize(), req->outputTotalSize(),&input_,outheap,context),
outHeap_(outheap),
context_(context)
{
isp->SetCmpISPDataObject(this);
ispRequest_ = req;
CMPASSERT( ExSPUnpackIOExpr(input_.expr(),
output_.expr(),
context->statementHeap()) == 0 );
}
CmpISPDataObject::~CmpISPDataObject()
{
if (ispRequest_)
ispRequest_->decrRefCount();
}
// -----------------------------------------------------------------------
// Methods of CmpStoredProc
// -----------------------------------------------------------------------
CmpStoredProc::CmpStoredProc(const NAString& procName, CmpContext* cmpContext)
: procName_(procName)
{
cmpContext_ = cmpContext;
}
CmpStoredProc::~CmpStoredProc()
{
}
NABoolean CmpStoredProc::InputFormat(Lng32, CmpSPInputFormat&)
{
return FALSE;
}
NABoolean CmpStoredProc::OutputFormat(CmpSPOutputFormat&)
{
return FALSE;
}
CmpStoredProc::ExecStatus CmpStoredProc::open(CmpSPDataObject&)
{
return FAIL;
}
CmpStoredProc::ExecStatus CmpStoredProc::fetch(CmpSPDataObject&)
{
return FAIL;
}
CmpStoredProc::ExecStatus CmpStoredProc::close(CmpSPDataObject&)
{
return FAIL;
}
// -----------------------------------------------------------------------
// Methods of CmpISPFuncs
// -----------------------------------------------------------------------
NAList<CmpISPFuncs::ProcFuncsStruct> CmpISPFuncs::procFuncsArray_(NULL,256);
CmpISPFuncs::CmpISPFuncs()
{
}
CmpISPFuncs::~CmpISPFuncs()
{
}
NABoolean CmpISPFuncs::ValidPFuncs
(const ProcFuncsStruct& pFuncs) const
{
return ( pFuncs.procName_ &&
pFuncs.compileFunc_ &&
pFuncs.inFormatFunc_ &&
pFuncs.outNumFormatFunc_ &&
pFuncs.outFormatFunc_ &&
pFuncs.procFunc_ );
}
Int32 CmpISPFuncs::RegFuncs(
const char* procName, //null terminated
SP_COMPILE_FUNCPTR compileFunc,
SP_INPUTFORMAT_FUNCPTR inFormatFunc,
SP_PARSE_FUNCPTR parseFunc,
SP_NUM_OUTPUTFIELDS_FUNCPTR outNumFormatFunc,
SP_OUTPUTFORMAT_FUNCPTR outFormatFunc,
SP_PROCESS_FUNCPTR procFunc,
SP_HANDLE spHandle,
const char* version)
{
if ( strcmp(version, CMPISPVERSION) != 0 )
{
ABORT ( "arkcmp: The ISP interface version is not compatible" );
NAExit(1);
}
procFuncsArray_.insert(ProcFuncsStruct(procName,
compileFunc,
inFormatFunc,
parseFunc,
outNumFormatFunc,
outFormatFunc,
procFunc,
spHandle));
return 1;
}
// -----------------------------------------------------------------------
// Methods of CmpInternalSP
// -----------------------------------------------------------------------
CmpInternalSP::CmpInternalSP(const NAString& name, CmpContext* context)
: CmpStoredProc(name, context),
compHandle_(0),
procHandle_(0),
state_(NONE),
ispData_(0)
{
for ( Int32 j=0; j < SP_MAX_ERROR_STRUCTS; j++ )
{
for ( Int32 i=0; i < SP_ERROR_MAX_OPTIONAL_STRINGS; i++ )
spError_[j].optionalString[i] = new((CollHeap*)cmpContext()->statementHeap())
char[SP_STRING_MAX_LENGTH];
}
initSP_ERROR_STRUCT();
}
CmpInternalSP::~CmpInternalSP()
{
if ( state_ == COMPILE )
{
// needs to call the compileFunc_
initSP_ERROR_STRUCT();
if ( (*(procFuncs_.compileFunc_)) (SP_COMP_EXIT, &compHandle_,
procFuncs_.spHandle_, &spError_[0]) !=SP_SUCCESS)
appendDiags();
}
// if not being properly closed, could be an exception happened,
// close the process properly
if ( state_ == PROCESS )
{
close(*ispData_);
// When it gets here, there must be an assertion happened before.
// To pass back the error infomation from SP close interface routine,
// the CoMDiags is moved into the context_ diags.
cmpContext()->diags()->mergeAfter(ispData_->output()->diags());
}
delete ispData_;
for ( Int32 j=0; j < SP_MAX_ERROR_STRUCTS; j++ )
{
for (Int32 i=0; i < SP_ERROR_MAX_OPTIONAL_STRINGS; i++)
NADELETEBASIC(spError_[j].optionalString[i], cmpContext()->statementHeap());
}
}
// helper routines used internally
static NAString timeStamp()
{
time_t tp;
tp = time(NULL);
char str[256];
sprintf(str, "%u", (UInt32)tp); // take lower 4 bytes as unique value
return NAString(str);
}
NAString CmpInternalSP::OutTableName()
{
// To make the OutTableName unique, put in the timestamp
// Note the timeStamp() above returns just lower 4 bytes of seconds since
// 01/01/1970 00:00:00, so it may not be unique if we get here to soon.
NAString tableName = "SPTableOut" + procName() + timeStamp();
return tableName;
}
NABoolean CmpInternalSP::startCompileCycle()
{
procFuncs_ = procFuncsLookupTable_[procName()];
if (!procFuncsLookupTable_.ValidPFuncs(procFuncs_))
{
*(cmpContext()->diags()) << DgSqlCode(arkcmpErrorISPNotFound)
<< DgString0(procName().data());
return FALSE;
}
if ( state_ != COMPILE )
{
// needs to call the compileFunc_
initSP_ERROR_STRUCT();
if ( (*(procFuncs_.compileFunc_)) (SP_COMP_INIT, &compHandle_,
procFuncs_.spHandle_, &spError_[0])!= SP_SUCCESS )
{
appendDiags();
return FALSE;
}
}
state_ = COMPILE;
return TRUE;
}
SP_FIELDDESC_STRUCT* CmpInternalSP::allocSP_FIELDDESC_STRUCT(Lng32 num)
{
// The SP_FIELDDESC_STRUCT is allocated using new, not the overloaded one
// because it is an array of struct to the SP interface function. So it can
// not be derived from NABasicObject. Since the caller is not supposed to
// jump out of the routine before it returns, it will not cause memory leak.
SP_FIELDDESC_STRUCT* fd = 0;
if (num)
{
fd = new SP_FIELDDESC_STRUCT[num];
memset(fd, 0, sizeof(SP_FIELDDESC_STRUCT) * (Int32)num);
}
return fd;
}
void CmpInternalSP::deleteSP_FIELDDESC_STRUCT(SP_FIELDDESC_STRUCT* fd)
{
delete[] fd;
}
SP_KEYDESC_STRUCT* CmpInternalSP::allocSP_KEYDESC_STRUCT(Lng32 num)
{
// The SP_KEYDESC_STRUCT is allocated using new, not the overloaded one
// because it is an array of struct to the SP interface function. So it can
// not be derived from NABasicObject. Since the caller is not supposed to
// jump out of the routine before it returns, it will not cause memory leak.
SP_KEYDESC_STRUCT* kd = 0;
if (num)
{
kd = new SP_KEYDESC_STRUCT[num];
memset(kd, 0, sizeof(SP_KEYDESC_STRUCT) * (Int32)num);
}
return kd;
}
void CmpInternalSP::deleteSP_KEYDESC_STRUCT(SP_KEYDESC_STRUCT* kd)
{
delete[] kd;
}
void CmpInternalSP::initSP_ERROR_STRUCT()
{
for ( Int32 j=0; j<SP_MAX_ERROR_STRUCTS; j++ )
{
spError_[j].error = 0;
for ( Int32 i=0; i<SP_ERROR_MAX_OPTIONAL_STRINGS; i++)
*(spError_[j].optionalString[i]) = '\0';
for ( Int32 k=0; k<SP_ERROR_MAX_OPTIONAL_INTS; k++ )
spError_[j].optionalInteger[k] = ComDiags_UnInitialized_Int;
}
}
void CmpInternalSP::appendDiags()
{
CmpSPERROR2Diags(spError_, cmpContext()->diags());
}
// virtual methods for interface between arkcmp ( compiler ) and
// stored procedure implementation.
NABoolean CmpInternalSP::InputFormat(Lng32 num, CmpSPInputFormat& t)
{
NABoolean retStatus = TRUE;
if ( !startCompileCycle() )
return FALSE;
SP_FIELDDESC_STRUCT* fields = allocSP_FIELDDESC_STRUCT(num);
initSP_ERROR_STRUCT();
if ( (*(procFuncs_.inFormatFunc_)) (fields, num,
compHandle_, procFuncs_.spHandle_, &spError_[0])
== SP_SUCCESS )
{
if (!(t.SetFormat(num, fields) ) )
retStatus = FALSE;
}
else
{
retStatus = FALSE;
appendDiags();
}
if ( !retStatus )
*(cmpContext()->diags()) << DgSqlCode(arkcmpErrorISPFieldDef);
// It is not supposed to jump out of this function before this delete.
// This has been agreed on the interface between arkcmp/server and ISP.
deleteSP_FIELDDESC_STRUCT(fields);
return retStatus;
}
NABoolean CmpInternalSP::OutputFormat(CmpSPOutputFormat& t)
{
NABoolean retStatus = TRUE;
CMPASSERT(state_ == COMPILE);
initSP_ERROR_STRUCT();
Lng32 num;
NAString outTableName = OutTableName();
if ( (*(procFuncs_.outNumFormatFunc_))( &num, compHandle_,
procFuncs_.spHandle_, &spError_[0])
== SP_SUCCESS )
{
if ( num == 0 )
{
// Create a dummy column if there is no output, since otherwise
// NATable generated later on will break later in binder.
SP_FIELDDESC_STRUCT fields;
strcpy(fields.COLUMN_DEF, "Dummy1 int");
if ( !(t.SetFormat(1, outTableName.data(), &fields, 0, 0) ) )
retStatus = FALSE;
}
else
{
SP_FIELDDESC_STRUCT* fields = allocSP_FIELDDESC_STRUCT(num);
SP_KEYDESC_STRUCT* keys = allocSP_KEYDESC_STRUCT(num);
Lng32 nKeys = 0;
initSP_ERROR_STRUCT();
if ( (*(procFuncs_.outFormatFunc_))
(fields, keys, &nKeys, compHandle_, procFuncs_.spHandle_, &spError_[0])
== SP_SUCCESS )
{
if (!(t.SetFormat(num, outTableName.data(), fields, nKeys, keys ) ) )
{
retStatus = FALSE;
}
}
else
{
appendDiags();
retStatus = FALSE;
}
// It is not supposed to jump out of this function before this
// delete[]
deleteSP_FIELDDESC_STRUCT(fields);
deleteSP_KEYDESC_STRUCT(keys);
}
}
else
{
appendDiags();
retStatus = FALSE;
}
if ( !retStatus )
*(cmpContext()->diags()) << DgSqlCode(arkcmpErrorISPFieldDef);
return retStatus;
}
NABoolean CmpInternalSP::ParseInput(const NAString& param)
{
CMPASSERT(state_ == COMPILE);
if ( !(procFuncs_.parseFunc_) )
{
// an optional function to call
return TRUE;
}
// test code, for CMPASSERT, check SPUtil.cpp SP_ERROR_*
// for more details.
// CMPASSERT( strcmp((char*)param.data(), "TestCMPASSERT") != 0 );
initSP_ERROR_STRUCT();
if ( (*(procFuncs_.parseFunc_))((char*)param.data(), compHandle_,
procFuncs_.spHandle_, &spError_[0]) != SP_SUCCESS )
{
appendDiags();
return FALSE;
}
return TRUE;
}
//CmpStoredProc::Status CmpInternalSP::open(CmpISPDataObject& data)
CmpStoredProc::ExecStatus CmpInternalSP::open(CmpISPDataObject& data)
{
CMPASSERT(state_ == NONE);
procFuncs_ = procFuncsLookupTable_[procName()];
if (!procFuncsLookupTable_.ValidPFuncs(procFuncs_))
{
*(cmpContext()->diags()) << DgSqlCode(arkcmpErrorISPNotFound)
<< DgString0(procName().data());
return FAIL;
}
initSP_ERROR_STRUCT();
SP_STATUS spStatus =
(*(procFuncs_.procFunc_))( SP_PROC_OPEN,
(SP_ROW_DATA)data.input(),
CmpSPExtractFunc,
(SP_ROW_DATA)data.output(),
CmpSPFormatFunc,
(SP_KEY_VALUE)data.key(),
CmpSPKeyValueFunc,
&procHandle_,
procFuncs_.spHandle_,
&spError_[0]);
if (spStatus == SP_FAIL || spStatus == SP_SUCCESS_WARNING)
{
// Errors or warnings, go get them
data.output()->MoveDiags(spError_, spStatus);
if (spStatus == SP_FAIL)
return FAIL;
}
state_ = PROCESS;
return SUCCESS;
}
CmpStoredProc::ExecStatus CmpInternalSP::fetch(CmpISPDataObject& data)
{
CMPASSERT(state_ == PROCESS);
// Need to send controls explicitly from the compiler space to the
// cli context so ISP requests can perform CLI operations to extract
// their values.
// For now, only send controls for MODIFY, POPULATE INDEX, TRANSFORM,
// PURGEDATA, MV_refresh and VALIDATE sp requests.
// This also means RECOVER since these operations
// can be perfomed through a RECOVER operation.
//
// VO, Versioning Light: Also send controls for sp_SchLevel
// (UPGRADE and DOWNGRADE)
if (procName() == "sp_partn" ||
procName() == "sp_populate" ||
procName() == "sp_purgedata" ||
procName() == "sp_recover" ||
procName() == "sp_transform" ||
procName() == "sp_validate" ||
procName() == "sp_purgedata" ||
procName() == "sp_refresh" ||
procName() == "sp_SchLevel")
{
sendAllControls(FALSE, FALSE, TRUE);
// set the parent qid for this session
const char *parentQid = cmpContext()->sqlSession()->getParentQid();
if (parentQid != NULL)
{
setParentQidAtSession(cmpContext()->statementHeap(), parentQid);
}
}
// Send sqlparser_flags
if (Get_SqlParser_Flags(ALLOW_FUNNY_IDENTIFIER))
sendParserFlag(ALLOW_FUNNY_IDENTIFIER);
initSP_ERROR_STRUCT();
SP_STATUS spStatus =
(*(procFuncs_.procFunc_))( SP_PROC_FETCH,
(SP_ROW_DATA)data.input(),
CmpSPExtractFunc,
(SP_ROW_DATA)data.output(),
CmpSPFormatFunc,
(SP_KEY_VALUE)data.key(),
CmpSPKeyValueFunc,
&procHandle_,
procFuncs_.spHandle_,
&spError_[0]);
if (spStatus == SP_FAIL)
{
// Errors, go get them
data.output()->MoveDiags(spError_, spStatus);
if (CmpCommon::diags() != NULL)
{
data.output()->MergeDiags(CmpCommon::diags());
CmpCommon::diags()->clear();
}
return FAIL;
}
if (CmpCommon::diags() != NULL)
{
data.output()->MergeDiags(CmpCommon::diags());
CmpCommon::diags()->clear();
}
if ( spStatus == SP_MOREDATA)
{
return MOREDATA;
}
return SUCCESS;
}
CmpStoredProc::ExecStatus CmpInternalSP::close(CmpISPDataObject& data)
{
CMPASSERT(state_ == PROCESS);
initSP_ERROR_STRUCT();
state_ = NONE;
SP_STATUS spStatus =
(*(procFuncs_.procFunc_))( SP_PROC_CLOSE,
(SP_ROW_DATA)data.input(),
CmpSPExtractFunc,
(SP_ROW_DATA)data.output(),
CmpSPFormatFunc,
(SP_KEY_VALUE)data.key(),
CmpSPKeyValueFunc,
&procHandle_,
procFuncs_.spHandle_,
&spError_[0]);
if (spStatus != SP_SUCCESS )
{
data.output()->MoveDiags(spError_, spStatus);
return FAIL;
}
return SUCCESS;
}