blob: fc9dc490e713421535c0adf047621729c64cd1ac [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: udrload.cpp
* description: this is the module that processes udr load messages.
* the tasks for this process are to :
* . extract sp descriptive attributes from message
* . extract sp parameter attributes from message
* . build SPInfo data structure and populate with attributes
* . return sp id (creation timestamp) to client
* . handle errors returned from lm.
* . deal with resource allocation problems of SPInfo data structures
*
* created: 01/01/2001
* language: c++
*
*
*****************************************************************************
*/
#include "udrextrn.h"
#include "spinfo.h"
#include "udrdefs.h"
#include "udrutil.h"
#include "ComDiags.h"
#include "ComSmallDefs.h"
#include "UdrStreams.h"
#include "UdrExeIpc.h"
#include "LmRoutineC.h"
#include "LmRoutineCppObj.h"
#include "LmRoutineJavaObj.h"
#include "LmJavaOptions.h"
#include "udrdecs.h"
#include "spinfoCallback.h"
// include the file generated by this command and checked into git:
// javah -d $TRAF_HOME/../sql/langman org.trafodion.sql.udr.UDR
#include "org_trafodion_sql_udr_UDR.h"
class UdrServerReplyStream;
SPInfo * processLoadParameters(UdrGlobals *UdrGlob,
UdrLoadMsg &request,
ComDiagsArea &d);
void displayLoadParameters(UdrLoadMsg &request);
void reportLoadResults(UdrGlobals *UdrGlob, SPInfo *sp, LmRoutine *lmr_);
void copyRoutineOptionalData(UdrLoadMsg &request, SPInfo *sp);
void processALoadMessage(UdrGlobals *UdrGlob,
UdrServerReplyStream &msgStream,
UdrLoadMsg &request,
IpcEnvironment &env)
{
const char *moduleName = "processALoadMessage";
char errorText[MAXERRTEXT];
ComDiagsArea *diags = ComDiagsArea::allocate(UdrGlob->getIpcHeap());
doMessageBox(UdrGlob, TRACE_SHOW_DIALOGS,
UdrGlob->showLoad_, moduleName);
NABoolean showLoadLogic = (UdrGlob->verbose_ &&
UdrGlob->traceLevel_ >= TRACE_IPMS &&
UdrGlob->showLoad_);
if (showLoadLogic)
{
ServerDebug("[UdrServ (%s)] Processing load request", moduleName);
}
// UDR_LOAD message always comes with transaction and they are out
// side Enter Tx and Exit Tx pair. Make sure we are under correct
// transaction.
msgStream.activateCurrentMsgTransaction();
//
// Check to see if the incoming UDR handle has already been seen
//
NABoolean handleAlreadyExists = FALSE;
SPInfo *sp = UdrGlob->getSPList()->spFind(request.getHandle());
if (sp)
{
handleAlreadyExists = TRUE;
if (showLoadLogic)
{
ServerDebug(" Duplicate handle arrived");
ServerDebug(" SPInfoState is %s", sp->getSPInfoStateString());
}
if (sp->getSPInfoState() != SPInfo::UNLOADING)
{
//
// An SPInfo exists but it is not one of the token instances to
// represent an out-of-sequence LOAD/UNLOAD. This is an internal
// error. Something has been botched in the message protocol.
//
char buf[100];
convertInt64ToAscii(request.getHandle(), buf);
*diags << DgSqlCode(-UDR_ERR_DUPLICATE_LOADS);
*diags << DgString0(buf);
}
else
{
// The LOAD/UNLOAD requests for this handle arrived
// out-of-sequence. Nothing to do at this point. An empty reply
// will be generated later in this function.
}
}
if (!handleAlreadyExists)
{
if (!UdrHandleIsValid(request.getHandle()))
{
*diags << DgSqlCode(-UDR_ERR_MISSING_UDRHANDLE);
*diags << DgString0("Load Message");
}
else
{
//
// First process the metadata in the LOAD requests and then
// contact Language Manager to load the SP.
//
sp = processLoadParameters(UdrGlob, request, *diags);
if (showLoadLogic)
{
ServerDebug("[UdrServ (%s)] About to call LM::getRoutine",
moduleName);
}
if (sp == NULL)
{
*diags << DgSqlCode(-UDR_ERR_UNABLE_TO_ALLOCATE_MEMORY);
*diags << DgString0("SPInfo");
}
else
{
UdrGlob->setCurrSP(sp);
LmRoutine *lmRoutine;
LmResult lmResult;
LmLanguageManager *lm =
UdrGlob->getOrCreateLM(lmResult, sp->getLanguage(), diags);
LmHandle emitRowFuncPtr;
if (sp->getParamStyle() == COM_STYLE_CPP_OBJ)
emitRowFuncPtr = (LmHandle)&SpInfoEmitRowCpp;
else
emitRowFuncPtr = (LmHandle)&SpInfoEmitRow;
if (lm)
{
if (sp->getParamStyle() == COM_STYLE_JAVA_OBJ ||
sp->getParamStyle() == COM_STYLE_CPP_OBJ)
{
lmResult = lm->getObjRoutine(
request.getUDRSerInvocationInfo(),
request.getUDRSerInvocationInfoLen(),
request.getUDRSerPlanInfo(),
request.getUDRSerPlanInfoLen(),
sp->getLanguage(),
sp->getParamStyle(),
sp->getExternalName(),
sp->getContainerName(),
sp->getExternalPathName(),
sp->getLibrarySqlName(),
&lmRoutine,
diags);
if (lmRoutine && lmResult == LM_OK)
{
if (sp->getParamStyle() == COM_STYLE_CPP_OBJ)
{
LmRoutineCppObj *cppRoutine =
static_cast<LmRoutineCppObj *>(lmRoutine);
#ifndef NDEBUG
int debugLoop = 2;
if (cppRoutine->getInvocationInfo()->getDebugFlags() &
tmudr::UDRInvocationInfo::DEBUG_LOAD_MSG_LOOP)
debugLoop = 1;
// go into a loop to allow the user to attach a debugger,
// if requested, set debugLoop = 2 in the debugger to get out
while (debugLoop < 2)
debugLoop = 1-debugLoop;
#endif
}
else if (sp->getParamStyle() == COM_STYLE_JAVA_OBJ)
{
LmRoutineJavaObj *javaObjRoutine =
static_cast<LmRoutineJavaObj *>(lmRoutine);
// avoid unpacking the UDRInvocationInfo to look
// up row lengths and take those from the request
// message instead
javaObjRoutine->setRowLengths(
request.getInputRowSize(),
request.getOutputRowSize());
// provide pointers of the JNI functions with special
// parameter lists
javaObjRoutine->setJNIFunctionPtrs(
reinterpret_cast<void *>(&Java_org_trafodion_sql_udr_UDR_SpInfoGetNextRowJava),
reinterpret_cast<void *>(&Java_org_trafodion_sql_udr_UDR_SpInfoEmitRowJava));
}
// set function pointers for functions provided
// by tdm_udrserv
lmResult = lmRoutine->setFunctionPtrs(SpInfoGetNextRow,
SpInfoEmitRowCpp,
diags);
if (lmResult == LM_OK)
// add items to the UDRInvocationInfo that are not
// known at compile time (total # of instances is
// kind of known, but we want to give the executor a
// chance to change it)
lmResult = lmRoutine->setRuntimeInfo(
request.getParentQid(),
request.getNumInstances(),
request.getInstanceNum(),
diags);
}
}
else
lmResult = lm->getRoutine(sp->getNumParameters(),
sp->getLmParameters(),
sp->getNumTables(),
sp->getLmTables(),
sp->getReturnValue(),
sp->getParamStyle(),
sp->getTransactionAttrs(),
sp->getSQLAccessMode(),
sp->getParentQid(),
sp->getRequestRowSize(),
sp->getReplyRowSize(),
sp->getSqlName(),
sp->getExternalName(),
sp->getRoutineSig(),
sp->getContainerName(),
sp->getExternalPathName(),
sp->getLibrarySqlName(),
UdrGlob->getCurrentUserName(),
UdrGlob->getSessionUserName(),
sp->getExternalSecurity(),
sp->getRoutineOwnerId(),
&lmRoutine,
(LmHandle)&SpInfoGetNextRow,
emitRowFuncPtr,
sp->getMaxNumResultSets(),
diags);
}
if (lmResult == LM_OK)
{
if (lmRoutine == NULL)
{
*diags << DgSqlCode(-UDR_ERR_MISSING_LMROUTINE);
*diags << DgString0("error: returned a null LM handle");
*diags << DgInt1((Int32)0);
}
else
{
sp->setLMHandle(lmRoutine);
// Retrieve any optional data from UdrLoadMsg.
copyRoutineOptionalData(request, sp);
reportLoadResults(UdrGlob, sp, lmRoutine);
sp->setSPInfoState(SPInfo::LOADED);
}
} // lmResult == LM_OK
if (showLoadLogic)
{
if (lmResult == LM_OK)
{
sprintf(errorText,
"[UdrServ (%.30s)] LM::getRoutine was successful.",
moduleName);
}
else
{
sprintf(errorText,
"[UdrServ (%.30s)] LM::getRoutine resulted in error.",
moduleName);
}
ServerDebug(errorText);
doMessageBox(UdrGlob, TRACE_SHOW_DIALOGS,
UdrGlob->showMain_, errorText);
}
if (sp && !(sp->isLoaded()))
{
sp->setSPInfoState(SPInfo::LOAD_FAILED);
}
} // if (sp == NULL) else ...
} // if (handle is not valid) else ...
} // !handleAlreadyExists
// build a reply and send it
msgStream.clearAllObjects();
UdrLoadReply *reply = new (UdrGlob->getIpcHeap())
UdrLoadReply(UdrGlob->getIpcHeap());
if (reply == NULL)
{ // no reply buffer build...
controlErrorReply(UdrGlob, msgStream, UDR_ERR_MESSAGE_PROCESSING,
INVOKE_ERR_NO_REPLY_BUFFER, NULL);
return;
}
// Only return a valid UDR Handle if diagnostics are not present and
// no LM errors occurred. We also return a valid handle if this LOAD
// arrived out-of-sequence after the UNLOAD and no diagnostics have
// been generated yet.
if (diags && diags->getNumber() > 0)
{
reply->setHandle(INVALID_UDR_HANDLE);
}
else if (sp)
{
if (sp->isLoaded() || handleAlreadyExists)
{
reply->setHandle(sp->getUdrHandle());
}
else
{
reply->setHandle(INVALID_UDR_HANDLE);
}
}
msgStream << *reply;
if (diags && diags->getNumber() > 0)
{
msgStream << *diags;
UdrGlob->numErrUDR_++;
UdrGlob->numErrSP_++;
UdrGlob->numErrLoadSP_++;
if (showLoadLogic)
dumpDiagnostics(diags, 2);
}
if (showLoadLogic)
{
ServerDebug("[UdrServ (%s)] About to send LOAD reply", moduleName);
}
#ifdef _DEBUG
if (UdrGlob && UdrGlob->getJavaLM())
{
sleepIfPropertySet(*(UdrGlob->getJavaLM()),
"MXUDR_LOAD_DELAY", diags);
}
#endif // _DEBUG
sendControlReply(UdrGlob, msgStream, sp);
if (diags)
{
diags->decrRefCount();
}
reply->decrRefCount();
} // processALoadMessage
SPInfo *processLoadParameters(UdrGlobals *UdrGlob,
UdrLoadMsg &request,
ComDiagsArea &d)
{
const char *moduleName = "processLoadParameters";
Lng32 oldDiags = 0;
Lng32 newDiags = 0;
doMessageBox(UdrGlob, TRACE_SHOW_DIALOGS,
UdrGlob->showLoad_, moduleName);
// check for test mode processing...
SPInfo *sp = NULL;
int numRetries = 0;
// process message parameters...
if (UdrGlob->traceLevel_ >= TRACE_DETAILS &&
UdrGlob->showLoad_)
{
displayLoadParameters(request);
}
doMessageBox(UdrGlob, TRACE_SHOW_DIALOGS,
UdrGlob->showLoad_, "create SPInfo");
oldDiags = d.getNumber();
while (sp == NULL && numRetries < 1)
{
sp = new (UdrGlob->getUdrHeap()) SPInfo(UdrGlob, UdrGlob->getUdrHeap(),
request.getHandle(),
(char *)request.getSqlName(),
(char *)request.getRoutineName(),
(char *)request.getSignature(),
(char *)request.getContainerName(),
(char *)request.getExternalPath(),
(char *)request.getLibrarySqlName(),
request.getNumParameters(),
request.getNumInValues(),
request.getNumOutValues(),
request.getMaxResultSets(),
request.getTransactionAttrs(),
request.getSqlAccessMode(),
request.getLanguage(),
request.getParamStyle(),
request.isIsolate(),
request.isCallOnNull(),
request.isExtraCall(),
request.isDeterministic(),
request.getExternalSecurity(),
request.getRoutineOwnerId(),
request.getInBufferSize(),
request.getOutBufferSize(),
request.getInputRowSize(),
request.getOutputRowSize(),
/* ComDiagsArea */ d,
(char *)request.getParentQid());
if (sp == NULL)
{ // memory problem
UdrGlob->getSPList()->releaseOldestSPJ(/* ComDiagsArea */ d);
numRetries++;
}
}
if (sp == NULL)
{
return NULL;
}
newDiags = d.getNumber();
if (oldDiags != newDiags)
{
// diagnostics generated in ctor for SPInfo
// something bad has happened. Bail out.
delete sp;
return NULL;
}
// Process IN/INOUT parameters
ComUInt32 i;
for (i = 0; i < sp->getNumInParameters(); i++)
{
UdrParameterInfo &pi = (UdrParameterInfo &)request.getInParam(i);
// Copy information from the UdrParameterInfo into the spinfo. The
// spinfo will initialize an LmParameter instance for this
// parameter.
sp->setInParam(i, pi);
}
// Process OUT/INOUT parameters
for (ComUInt32 j = 0; j < sp->getNumOutParameters(); j++)
{
UdrParameterInfo &po = (UdrParameterInfo &)request.getOutParam(j);
// Copy information from the UdrParameterInfo into the spinfo. The
// spinfo will initialize an LmParameter instance for this
// parameter. Note for INOUT parameters, the LmParameter will not
// be completely re-initialized. It was partially initialized in
// the loop above for input parameters. This setOutParam call
// completes the LmParameter initialization.
sp->setOutParam(j, po);
}
// Process table input info if any
if(sp->getParamStyle() == COM_STYLE_SQLROW_TM ||
sp->getParamStyle() == COM_STYLE_CPP_OBJ ||
sp->getParamStyle() == COM_STYLE_JAVA_OBJ)
{
sp->setNumTableInfo(request.getNumInputTables());
sp->setTableInputInfo(request.getInputTables());
}
if (request.getUdrJavaDebugPort() > 0 &&
sp->getLanguage() == COM_LANGUAGE_JAVA &&
UdrGlob->getJavaLM() == NULL) // Langman not started already
{
// Add debug options to the UDR globals. Note that this will
// only work for the first UDR in this tmd_udrserv process,
// since the options are evaluated when the JVM gets started.
// Also note that the compiler will only generate these flags
// for debug builds or for the DB_ROOT user (e.g. sqlci).
char buf[200];
int debugPort = request.getUdrJavaDebugPort();
int timeout = request.getUdrJavaDebugTimeout();
// if the debug port is a multiple of 1000 then add pid mod 1000
// to the debug port, to be able to debug parallel queries with
// multiple UDR servers
if (debugPort % 1000 == 0)
debugPort += (int) getpid() % 1000;
if (timeout > 0)
{
// suspend the JVM and wait for <timeout> milliseconds for the
// debugger to attach
snprintf(
buf,
sizeof(buf),
"-agentlib:jdwp=transport=dt_socket,address=%d,server=y,timeout=%d",
debugPort,
timeout);
printf("Debugging tdm_udrserv JVM instance %d of %d, attach debugger to port %d within %d seconds\n",
request.getInstanceNum(),
request.getNumInstances(),
debugPort,
timeout/1000);
}
else
{
// don't suspend the JVM, attaching the debugger is optional
snprintf(
buf,
sizeof(buf),
"-agentlib:jdwp=transport=dt_socket,address=%d,server=y,suspend=n",
debugPort);
printf("Debugging tdm_udrserv JVM instance %d of %d, attach debugger to port %d\n",
request.getInstanceNum(),
request.getNumInstances(),
debugPort);
}
UdrGlob->getJavaOptions()->addOption(buf, false);
}
if (UdrGlob->verbose_ &&
UdrGlob->traceLevel_ >= TRACE_DETAILS &&
UdrGlob->showLoad_)
{
sp->displaySPInfo(2);
}
if (UdrGlob->verbose_ &&
UdrGlob->traceLevel_ >= TRACE_DATA_AREAS &&
UdrGlob->showLoad_)
{
ServerDebug(" LmParameter array:");
for (ComUInt32 i = 0; i < sp->getNumParameters(); i++)
{
dumpLmParameter(sp->getLmParameter(i), i, " ");
}
}
if (UdrGlob->verbose_ &&
UdrGlob->traceLevel_ >= TRACE_DATA_AREAS &&
UdrGlob->showLoad_)
ServerDebug("");
return sp;
} // processLoadParameters
void displayLoadParameters(UdrLoadMsg &request)
{
ServerDebug("");
ServerDebug("UDR load parameters:");
ServerDebug(" UDR handle : " INT64_SPEC , request.getHandle());
ServerDebug(" sql name : %s", request.getSqlName());
ServerDebug(" routine name : %s", request.getRoutineName());
ServerDebug(" routine signature : %s", request.getSignature());
ServerDebug(" container name : %s", request.getContainerName());
ServerDebug(" external path name : %s", request.getExternalPath());
ServerDebug(" library SQL name : %s", request.getLibrarySqlName());
ServerDebug(" parent QID : %s", request.getParentQid());
const char *transString;
switch (request.getTransactionAttrs() )
{
case COM_NO_TRANSACTION_REQUIRED:
transString = "NO TRANSACTION REQUIRED";
break;
case COM_TRANSACTION_REQUIRED:
transString = "TRANSACTION REQUIRED";
break;
default:
transString = "*** UNKNOWN ***";
break;
}
ServerDebug(" transaction required : %s", transString);
const char *modeString;
switch (request.getSqlAccessMode() )
{
case COM_NO_SQL:
modeString = "NO SQL";
break;
case COM_CONTAINS_SQL:
modeString = "CONTAINS SQL";
break;
case COM_READS_SQL:
modeString = "READS SQL DATA";
break;
case COM_MODIFIES_SQL:
modeString = "MODIFIES SQL DATA";
break;
default:
modeString = "*** UNKNOWN ***";
break;
}
ServerDebug(" sql access mode : %s", modeString);
const char *language;
switch (request.getLanguage())
{
case COM_LANGUAGE_JAVA:
language = "Java";
break;
case COM_LANGUAGE_C:
language = "C";
break;
case COM_LANGUAGE_CPP:
language = "C++";
break;
case COM_LANGUAGE_SQL:
language = "SQL";
break;
default:
language = "*** UNKNOWN ***";
break;
}
ServerDebug(" language : %s", language);
const char *externalSecurity;
switch (request.getExternalSecurity())
{
case COM_ROUTINE_EXTERNAL_SECURITY_INVOKER:
externalSecurity = "EXTERNAL SECURITY INVOKER";
break;
case COM_ROUTINE_EXTERNAL_SECURITY_DEFINER:
externalSecurity = "EXTERNAL SECURITY DEFINER";
break;
default:
externalSecurity = "*** UNKNOWN ***";
break;
}
ServerDebug(" externalSecurity : %s", externalSecurity);
ServerDebug(" routine Owner Id : %d",
(Int32) request.getRoutineOwnerId());
ServerDebug(" max result sets : %d",
(Int32) request.getMaxResultSets());
ServerDebug(" num params : %d",
(Int32) request.getNumParameters());
ServerDebug(" num in values : %d",
(Int32) request.getNumInValues());
ServerDebug(" num out values : %d",
(Int32) request.getNumOutValues());
ServerDebug(" udr flags : %d",
(Int32) request.getUdrFlags());
if (request.isIsolate())
ServerDebug(" isolate : TRUE");
else
ServerDebug(" isolate : FALSE");
if (request.isCallOnNull())
ServerDebug(" call on NULL : TRUE");
else
ServerDebug(" call on NULL : FALSE");
if (request.isExtraCall())
ServerDebug(" extra call : TRUE");
else
ServerDebug(" extra call : FALSE");
if (request.isDeterministic())
ServerDebug(" deterministic : TRUE");
else
ServerDebug(" deterministic : FALSE");
ServerDebug("");
} // displayLoadParameters
void reportLoadResults(UdrGlobals *UdrGlob, SPInfo *sp, LmRoutine *lmr_)
{
const char *moduleName = "reportLoadResults";
doMessageBox(UdrGlob, TRACE_SHOW_DIALOGS,
UdrGlob->showLoad_, moduleName);
if (UdrGlob->verbose_ &&
UdrGlob->traceLevel_ >= TRACE_DETAILS &&
UdrGlob->showLoad_ )
{
ServerDebug("");
ServerDebug("[UdrServ (%s)] LOAD message results:", moduleName);
ServerDebug(" LOAD Udr Handle : " INT64_SPEC ,
(Int64) sp->getUdrHandle());
ServerDebug(" LM result parameter : %d", (Int32) sp->getNumParameters());
if (sp->getReturnValue() != NULL)
{
dumpLmParameter(*sp->getReturnValue(), 0, " ");
}
if (lmr_)
{
ServerDebug(" LM routine : ");
dumpBuffer((unsigned char *)lmr_, sizeof(LmRoutine));
}
ServerDebug("");
}
} // reportLoadResults
// Utility function to push optional data buffers from UdrLoadMsg
// into LM.
void copyRoutineOptionalData(UdrLoadMsg &request, SPInfo *sp)
{
if (sp->getLanguage() != COM_LANGUAGE_C)
return;
ComUInt32 numBufs = request.getNumOptionalDataBufs();
LmRoutineC *routine = (LmRoutineC *) sp->getLMHandle();
routine->setNumPassThroughInputs(numBufs);
for (ComUInt32 i = 0; i < numBufs; i++)
{
// Process each optional data buffer. Each buffer is a 4-byte
// length field followed by data.
ComUInt32 buflen = 0;
char *dataBuf = request.getOptionalDataBuf(i);
memcpy(&buflen, dataBuf, 4);
if (buflen > 0)
routine->setPassThroughInput(i, dataBuf + 4, buflen);
else
routine->setPassThroughInput(i, (void *)"", 0);
}
}