blob: 874407e29f873e95ccb63ff46b0c5bc9ed304866 [file] [log] [blame]
/* -*-C++-*-
**********************************************************************
*
* File: LmRoutineC.cpp
* Description: Base class for C routines
* Created: 05/14/2008
* Language: C++
*
// @@@ 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 @@@
**********************************************************************/
#include "LmRoutineC.h"
#include "ComVersionDefs.h"
SQLUDR_CHAR *LmRoutineC::host_data_ = NULL;
LmRoutineC::LmRoutineC(const char *sqlName,
const char *externalName,
const char *librarySqlName,
ComUInt32 numSqlParam,
char *routineSig,
ComUInt32 maxResultSets,
ComRoutineLanguage language,
ComRoutineParamStyle paramStyle,
ComRoutineTransactionAttributes transactionAttrs,
ComRoutineSQLAccess sqlAccessMode,
ComRoutineExternalSecurity externalSecurity,
Int32 routineOwnerId,
const char *parentQid,
ComUInt32 inputParamRowLen,
ComUInt32 outputRowLen,
const char *currentUserName,
const char *sessionUserName,
LmParameter *parameters,
LmLanguageManagerC *lm,
LmHandle routine,
LmContainer *container,
ComDiagsArea *diagsArea)
: LmRoutine(container, routine, sqlName, externalName, librarySqlName,
numSqlParam, maxResultSets,
language, paramStyle, transactionAttrs, sqlAccessMode,
externalSecurity,
routineOwnerId,
parentQid, inputParamRowLen, outputRowLen,
currentUserName, sessionUserName,
parameters, lm),
callType_(SQLUDR_CALLTYPE_INITIAL),
stateArea_(NULL),
udrInfo_(NULL),
finalCallRequired_(FALSE)
{
// If we ever support C stored procedures, we need to handle the
// case of 0 inputs and 0 outputs. For functions there is always at
// least one output.
LM_ASSERT(numSqlParam_ > 0);
// Allocate (static) host_data_ if it's not already allocated.
// This will never be deleted and stays until the process dies.
if (LmRoutineC::host_data_ == NULL)
{
LmRoutineC::host_data_ = (SQLUDR_CHAR *)
malloc(SQLUDR_STATEAREA_BUFFER_SIZE + 128);
LM_ASSERT(LmRoutineC::host_data_);
memset((char *)LmRoutineC::host_data_, 0,
SQLUDR_STATEAREA_BUFFER_SIZE + 128);
}
// Allocate the SQLUDR_STATEAREA structure
stateArea_ = (SQLUDR_STATEAREA *) malloc(sizeof(SQLUDR_STATEAREA) + 32);
LM_ASSERT(stateArea_);
memset(stateArea_, 0, sizeof(SQLUDR_STATEAREA) + 32);
stateArea_->version = SQLUDR_STATEAREA_CURRENT_VERSION;
stateArea_->host_data.data = LmRoutineC::host_data_;
stateArea_->host_data.length = SQLUDR_STATEAREA_BUFFER_SIZE;;
stateArea_->stmt_data.data = (SQLUDR_CHAR *)
malloc(SQLUDR_STATEAREA_BUFFER_SIZE + 128);
LM_ASSERT(stateArea_->stmt_data.data);
memset((char *)stateArea_->stmt_data.data, 0,
SQLUDR_STATEAREA_BUFFER_SIZE + 128);
stateArea_->stmt_data.length = SQLUDR_STATEAREA_BUFFER_SIZE;
// Allocate SQLUDR_UDRINFO structure
udrInfo_ = (SQLUDR_UDRINFO *) malloc(sizeof(SQLUDR_UDRINFO) + 32);
LM_ASSERT(udrInfo_);
memset(udrInfo_, 0, sizeof(SQLUDR_UDRINFO) + 32);
udrInfo_->version = SQLUDR_UDRINFO_CURRENT_VERSION;
udrInfo_->sql_version = 2400;
udrInfo_->routine_name = sqlName_;
// udrInfo_->library_name = librarySqlName_;
const char *qid = getParentQid();
if (qid)
udrInfo_->query_id = copy_and_pad(qid, strlen(qid), 8);
// Allocate memory for SQLUDR_PARAM structure.
// We assume that the inputs come first, followed by outputs
ComUInt32 num_inputs = 0, num_outputs = 0;
SQLUDR_PARAM *sqludr_params = (SQLUDR_PARAM *)
malloc(numSqlParam_ * sizeof(SQLUDR_PARAM) + 32);
LM_ASSERT(sqludr_params);
memset(sqludr_params, 0, numSqlParam_ * sizeof(SQLUDR_PARAM) + 32);
// Initialize the SQLUDR_PARAM structures
ComUInt32 dataBytes = 0;
ComUInt32 i;
for (i = 0; i < numSqlParam_; i++)
{
LmParameter &p = lmParams_[i];
switch (p.direction())
{
case COM_INPUT_COLUMN:
num_inputs++;
dataBytes = p.inSize();
break;
case COM_OUTPUT_COLUMN:
num_outputs++;
dataBytes = p.outSize();
break;
default:
LM_ASSERT1(0, "Invalid parameter mode");
break;
}
SQLUDR_PARAM &udfParamInfo = sqludr_params[i];
udfParamInfo.version = SQLUDR_PARAM_CURRENT_VERSION;
udfParamInfo.datatype =
(SQLUDR_INT16) getAnsiTypeFromFSType(p.fsType());
udfParamInfo.data_len = (SQLUDR_UINT32) dataBytes;
// The parameter name seen by the routine body will be a pointer
// to the LmParameter's copy of the name.
udfParamInfo.name = (SQLUDR_CHAR *) p.getParamName();
// Union u1 contains:
// * character_set
// * datetime_code
// * interval_code
// * scale
if (p.isCharacter())
{
udfParamInfo.u1.character_set =
(SQLUDR_INT16) p.encodingCharSet();
}
else if (p.isDateTime())
{
// For datetime types: ANSI type will be SQLTYPECODE_DATETIME and
// SQLUDR_PARAM stores a code to indicate date, time, or
// timestamp.
udfParamInfo.u1.datetime_code =
(SQLUDR_INT16) p.getDatetimeCode();
}
else if (p.isInterval())
{
// For interval types: ANSI type will be SQLTYPECODE_INTERVAL and
// SQLUDR_PARAM stores a code to indicate start and end fields.
udfParamInfo.u1.interval_code =
(SQLUDR_INT16) getIntervalCode(p.fsType());
}
else
{
udfParamInfo.u1.scale = (SQLUDR_INT16) p.scale();
}
// Union u2 contains:
// * precision
// * collation
if (p.isTimeOrTimestamp())
{
udfParamInfo.u2.precision = (SQLUDR_INT16) p.getTimePrecision();
}
else if (p.isCharacter())
{
SQLUDR_COLLATION collation = SQLUDR_COLLATION_UNKNOWN;
switch (p.collation())
{
case CharInfo::DefaultCollation:
collation = SQLUDR_COLLATION_DEFAULT;
break;
case CharInfo::CZECH_COLLATION:
collation = SQLUDR_COLLATION_CZECH;
break;
case CharInfo::CZECH_COLLATION_CI:
collation = SQLUDR_COLLATION_CZECH_CI;
break;
case CharInfo::SJIS_COLLATION:
collation = SQLUDR_COLLATION_SJIS;
break;
}
udfParamInfo.u2.collation = (SQLUDR_INT16) collation;
}
else
{
udfParamInfo.u2.precision = (SQLUDR_INT16) p.prec();
}
// Set data offsets and lengths
if((getParamStyle() == COM_STYLE_SQLROW) ||
(getParamStyle() == COM_STYLE_SQLROW_TM))
{
switch (p.direction())
{
case COM_INPUT_COLUMN:
udfParamInfo.data_offset = (SQLUDR_UINT32) p.inDataOffset();
udfParamInfo.ind_offset = (SQLUDR_UINT32) p.inNullIndOffset();
udfParamInfo.vc_ind_offset = (SQLUDR_UINT32) p.inVCLenIndOffset();
udfParamInfo.vc_ind_len = (SQLUDR_UINT8) p.inVCLenIndSize();
break;
case COM_OUTPUT_COLUMN:
udfParamInfo.data_offset = (SQLUDR_UINT32) p.outDataOffset();
udfParamInfo.ind_offset = (SQLUDR_UINT32) p.outNullIndOffset();
udfParamInfo.vc_ind_offset = (SQLUDR_UINT32) p.outVCLenIndOffset();
udfParamInfo.vc_ind_len = (SQLUDR_UINT8) p.outVCLenIndSize();
break;
default:
LM_ASSERT1(0, "Invalid parameter mode");
break;
}
} // if (getParamStyle() == COM_STYLE_SQLROW)
} // for each param
// Fill in the rest of udrInfo_ structure fields
udrInfo_->sql_version = (SQLUDR_UINT16) ComVersion_GetMXV();
udrInfo_->row_format = SQLUDR_ROWFORMAT_EXPLODED;
udrInfo_->num_inputs = num_inputs;
udrInfo_->num_return_values = num_outputs;
udrInfo_->inputs = (num_inputs > 0 ? &sqludr_params[0] : NULL);
udrInfo_->return_values =
(num_outputs > 0 ? &sqludr_params[num_inputs] : NULL);
udrInfo_->in_row_length = inputParamRowLen_;
udrInfo_->out_row_length = outputRowLen_;
switch (getParamStyle())
{
case COM_STYLE_SQL:
udrInfo_->param_style = SQLUDR_PARAMSTYLE_SQL;
udrInfo_->param_style_version = 1;
break;
case COM_STYLE_SQLROW:
case COM_STYLE_SQLROW_TM:
udrInfo_->param_style = SQLUDR_PARAMSTYLE_SQLROW;
udrInfo_->param_style_version = 1;
break;
case COM_STYLE_CPP_OBJ:
// udrInfo_ is not used
break;
default:
LM_ASSERT(0);
break;
}
// Fill current user name and session user name
if (currentUserName)
udrInfo_->current_user_name = copy_and_pad(currentUserName, strlen(currentUserName), 8);
if (sessionUserName)
udrInfo_->session_user_name = copy_and_pad(sessionUserName, strlen(sessionUserName), 8);
} // LmRoutineC::LmRoutineC
LmRoutineC::~LmRoutineC()
{
// udrInfo_->return_values is not needed to be deleted. It's
// all consecutively allocated memory starting from
// udrInfo_->inputs.
SQLUDR_PARAM *sqludr_params = udrInfo_->inputs;
free(sqludr_params);
if (stateArea_ && stateArea_->stmt_data.data)
free(stateArea_->stmt_data.data);
if (stateArea_)
free(stateArea_);
if (udrInfo_)
{
if (udrInfo_->query_id)
free(udrInfo_->query_id);
if (udrInfo_->current_user_name)
free(udrInfo_->current_user_name);
if (udrInfo_->session_user_name)
free(udrInfo_->session_user_name);
deletePassThroughInputs();
free(udrInfo_);
}
}
void
LmRoutineC::setNumPassThroughInputs(ComUInt32 numPassThroughInputs)
{
deletePassThroughInputs();
udrInfo_->num_pass_through = numPassThroughInputs;
udrInfo_->pass_through = NULL;
if (numPassThroughInputs > 0)
{
ComUInt32 numBytes = (numPassThroughInputs * sizeof(SQLUDR_BUFFER)) + 32;
udrInfo_->pass_through = (SQLUDR_BUFFER *) malloc(numBytes);
LM_ASSERT(udrInfo_->pass_through);
memset(udrInfo_->pass_through, 0, numBytes);
}
}
void
LmRoutineC::setPassThroughInput(ComUInt32 index,
void *data,
ComUInt32 dataLen)
{
SQLUDR_BUFFER &pass_through_buffer = udrInfo_->pass_through[index];
pass_through_buffer.length = dataLen;
if (dataLen > 0)
{
LM_ASSERT(data);
pass_through_buffer.data = copy_and_pad((char *) data, dataLen, 32);
}
else
{
pass_through_buffer.data = copy_and_pad("", 0, 32);
}
}
void LmRoutineC::deletePassThroughInputs()
{
if (udrInfo_)
{
if (udrInfo_->num_pass_through > 0)
{
LM_ASSERT(udrInfo_->pass_through);
for (ComUInt32 i = 0; i < udrInfo_->num_pass_through; i++)
{
SQLUDR_BUFFER &b = udrInfo_->pass_through[i];
free(b.data);
}
free(udrInfo_->pass_through);
}
udrInfo_->num_pass_through = 0;
udrInfo_->pass_through = NULL;
}
}
LmResult LmRoutineC::handleFinalCall(ComDiagsArea *diagsArea)
{
setFinalCall();
return invokeRoutine(NULL, NULL, diagsArea);
}
LmResult
LmRoutineC::processReturnStatus(ComSInt32 retcode, ComDiagsArea *diags)
{
char *returnMsgText = NULL, *noMsgText = NULL;
if (retcode == SQLUDR_SUCCESS)
return LM_OK;
if (msgText_[0] != '\0')
{
returnMsgText = msgText_;
}
else
{
const char *text =
"No SQL message text was provided by user-defined function ";
ComUInt32 msgLen = str_len(text) + str_len(getNameForDiags());
noMsgText = new (collHeap()) char[msgLen + 1];
sprintf(noMsgText, "%s%s", text, getNameForDiags());
returnMsgText = noMsgText;
}
// Check the returned SQLSTATE value and raise appropriate
// SQL code. Valid SQLSTATE values begin with "38" except "38000"
if ((strncmp(sqlState_, "38", 2) == 0) &&
(strncmp(sqlState_, "38000", 5) != 0))
{
Int32 sqlCode = (retcode == SQLUDR_ERROR) ?
-LME_CUSTOM_ERROR : LME_CUSTOM_WARNING;
*diags << DgSqlCode(sqlCode)
<< DgString0(returnMsgText)
<< DgString1(sqlState_);
*diags << DgCustomSQLState(sqlState_);
}
else
{
Int32 sqlCode = (retcode == SQLUDR_ERROR) ? -LME_UDF_ERROR : LME_UDF_WARNING;
*diags << DgSqlCode(sqlCode)
<< DgString0(getNameForDiags())
<< DgString1(sqlState_)
<< DgString2(returnMsgText);
}
if (noMsgText)
NADELETEBASIC(noMsgText, collHeap());
return (retcode == SQLUDR_ERROR) ? LM_ERR: LM_OK;
}