| /********************************************************************** |
| // @@@ 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: Statement.C |
| * Description: Methods for class Statement. |
| * |
| * Created: 7/10/95 |
| * Language: C++ |
| * |
| * |
| * |
| * |
| ***************************************************************************** |
| */ |
| |
| #define _XOPEN_SOURCE_EXTENDED 1 |
| #include "cli_stdh.h" |
| #undef _XOPEN_SOURCE_EXTENDED |
| |
| #include "ComCextdecs.h" |
| #include "ex_stdh.h" |
| #include "ex_exe_stmt_globals.h" |
| #include "ComTdb.h" |
| #include "ex_tcb.h" |
| #include "ex_root.h" |
| #include "ExStats.h" |
| #include "ExSqlComp.h" |
| #include "ex_transaction.h" |
| #include "ex_frag_rt.h" |
| #include "ComDiags.h" |
| #include "NAMemory.h" |
| #include "LateBindInfo.h" |
| #include "sql_buffer.h" |
| #include "ex_control.h" |
| #include "Descriptor.h" // For call to Descriptor::getCharDataFromCharHostVar(). |
| #include "exp_clause_derived.h" |
| #include "sql_id.h" |
| #include "ex_error.h" |
| #include "ComRtUtils.h" |
| #include "ComDistribution.h" |
| #include "Cli.h" |
| #include "Int64.h" |
| #include "ComSqlId.h" |
| #include "CmpErrors.h" |
| |
| #include "TriggerEnable.h" // triggers |
| #include "ComSmallDefs.h" // MV |
| #include "ComMvAttributeBitmap.h" // MV |
| |
| #include "logmxevent.h" |
| |
| #include "ExpLOBinterface.h" |
| |
| #include "ExUdrServer.h" |
| #include "wstr.h" |
| #include "QueryText.h" |
| #include <wchar.h> |
| |
| #include "ComAnsiNamePart.h" |
| #include "ExRsInfo.h" |
| |
| #include "arkcmp_proc.h" |
| #include "CmpContext.h" |
| |
| #include "HiveClient_JNI.h" |
| |
| // Printf-style tracing macros for the debug build. The macros are |
| // no-ops in the release build. |
| #ifdef _DEBUG |
| #include <stdarg.h> |
| #define StmtDebug0(s) StmtPrintf((s)) |
| #define StmtDebug1(s,a1) StmtPrintf((s),(a1)) |
| #define StmtDebug2(s,a1,a2) StmtPrintf((s),(a1),(a2)) |
| #define StmtDebug3(s,a1,a2,a3) StmtPrintf((s),(a1),(a2),(a3)) |
| #define StmtDebug4(s,a1,a2,a3,a4) StmtPrintf((s),(a1),(a2),(a3),(a4)) |
| #define StmtDebug5(s,a1,a2,a3,a4,a5) StmtPrintf((s),(a1),(a2),(a3),(a4),(a5)) |
| const char *TransIdToText(Int64 transId) |
| { |
| static char text[256]; |
| short actualLen; |
| short error = TRANSIDTOTEXT(transId, text, 255, &actualLen); |
| if (error) |
| str_sprintf(text, "(error %d)", (Int32) error); |
| else |
| text[actualLen] = 0; |
| return &text[0]; |
| } |
| #else |
| #define StmtDebug0(s) |
| #define StmtDebug1(s,a1) |
| #define StmtDebug2(s,a1,a2) |
| #define StmtDebug3(s,a1,a2,a3) |
| #define StmtDebug4(s,a1,a2,a3,a4) |
| #define StmtDebug5(s,a1,a2,a3,a4,a5) |
| #endif |
| |
| const char *RetcodeToString(RETCODE r) |
| { |
| switch (r) |
| { |
| case SUCCESS: return "SUCCESS"; |
| case SQL_EOF: return "SQL_EOF"; |
| case ERROR: return "ERROR"; |
| case WARNING: return "WARNING"; |
| case NOT_FINISHED: return "NOT_FINISHED"; |
| default: return ComRtGetUnknownString((Int32) r); |
| } |
| } |
| |
| class Dealloc |
| { |
| NAHeap *heap_; |
| void * addr_; |
| public: |
| Dealloc() : heap_(NULL), addr_(NULL) {} |
| ~Dealloc() { if (addr_ != NULL) heap_->deallocateMemory(addr_); } |
| NAHeap * setHeap(NAHeap *heap) { return heap_ = heap; } |
| void * getAddr(void *addr) { return addr_ = addr; } |
| }; |
| |
| //////////////////////////////////////////////////////////////////// |
| // class StatementInfo |
| //////////////////////////////////////////////////////////////////// |
| StatementInfo::StatementInfo() |
| { |
| statement_ = NULL; |
| inputDesc_ = NULL; |
| outputDesc_ = NULL; |
| hashValue_ = 0; |
| flags_ = 0; |
| }; |
| |
| StatementInfo::~StatementInfo() |
| { |
| statement_ = NULL; |
| inputDesc_ = NULL; |
| outputDesc_ = NULL; |
| hashValue_ = 0; |
| flags_ = 0; |
| }; |
| |
| // This function is an internal test mechanism for SPJ result sets |
| static const char *getProxySyntaxFromEnvironment(CliGlobals *cliGlobals, |
| Lng32 rsIndex) |
| { |
| char *stmtText = NULL; |
| |
| |
| return stmtText; |
| } |
| |
| //////////////////////////////////////////////////////////////////// |
| // class Statement |
| //////////////////////////////////////////////////////////////////// |
| Statement::Statement(SQLSTMT_ID * statement_id_, |
| CliGlobals * cliGlobals, |
| StatementType stmt_type_, |
| char * cn, Module *module) |
| : stmt_state(INITIAL_), |
| cliGlobals_(cliGlobals), |
| context_(cliGlobals->currContext()), |
| clonedFrom_(NULL), |
| parentCall_(NULL), |
| heap_("Statement Heap", |
| context_->exHeap(), |
| 8096 /* 20480 */ /* block size */, |
| 0 /* upperLimit */), |
| prevCloseStatement_(NULL), |
| nextCloseStatement_(NULL), |
| closeSequence_((stmt_type_ == STATIC_STMT) ? 0 : -1) |
| , holdable_(SQLCLIDEV_NONHOLDABLE) |
| , statementIndex_(0) |
| , module_(module) |
| , inputArrayMaxsize_(0) // compile time info for dynamic rowsets |
| , rowsetAtomicity_(UNSPECIFIED_) |
| , notAtomicFailureLimit_(0) |
| , anyTransWasStartedByMe_(FALSE) |
| , autoCommitCleared_(FALSE) |
| , savedRoVal_(TransMode::READ_WRITE_ ) |
| , savedRbVal_ (TransMode::ROLLBACK_MODE_NOT_SPECIFIED_ ) |
| , savedAiVal_( -1 ) |
| , udrSecurity_ (NULL) |
| , versionOnEntry_ (COM_VERS_UNKNOWN) |
| , versionToUse_ (COM_VERS_UNKNOWN) |
| , fetchErrorCode_ (VERSION_NO_ERROR) |
| , mxcmpErrorCode_ (VERSION_NO_ERROR) |
| , stmtStats_(NULL) |
| , extractConsumerQueryTemplate_(NULL) |
| , stmtInfo_(NULL) |
| , aqrStmtInfo_(NULL) |
| , state_(INITIAL_STATE_) |
| , compileStatsArea_(NULL) |
| , spaceObject_(new(context_->exHeap()) |
| Space(Space::EXECUTOR_SPACE, TRUE, (char *)"Stmt Space")) |
| , space_(*spaceObject_) |
| , aqrInitialExeStartTime_(-1) |
| , compileEndTime_(-1) |
| { |
| cliLevel_ = context_->getNumOfCliCalls(); |
| |
| #ifdef _DEBUG |
| stmtDebug_ = FALSE; |
| stmtListDebug_ = FALSE; |
| Lng32 numCliCalls = context_->getNumOfCliCalls(); |
| |
| // We have two printf-style trace mechanisms in the debug build |
| // |
| // 1. A detailed account of all significant events in the life |
| // of this statement. Enabled by the STMT_DEBUG environment |
| // variable. |
| // |
| // 2. An account of when this is statement is added to or removed |
| // from any of the context's statement lists. Enabled by the |
| // STMTLIST_DEBUG or STMT_DEBUG environment variable. The code |
| // for this trace is in the ContextCli class. All we do here in |
| // the Statement class is carry a flag indicating whether events |
| // for this instance should be reported. |
| |
| // Each trace is enabled on a per-instance basis. We check the |
| // environment variables every time through the Statement |
| // constructor. Also note that we do NOT enable either trace for |
| // statements created internally by SQL/MX code. For those |
| // statements, the numCliCalls variable will be greater than one. |
| |
| // The commands to create trace output are the StmtDebugX macros |
| // above in this source file. In the release build the macros |
| // evaluate to a noop. |
| |
| if (numCliCalls == 1) |
| { |
| char *envVar; |
| envVar = getenv("STMT_DEBUG"); |
| if (envVar && envVar[0]) |
| { |
| stmtDebug_ = TRUE; |
| stmtListDebug_ = TRUE; |
| } |
| else |
| { |
| envVar = getenv("STMTLIST_DEBUG"); |
| if (envVar && envVar[0]) |
| { |
| stmtListDebug_ = TRUE; |
| } |
| } |
| } |
| #endif |
| |
| StmtDebug1("[BEGIN constructor] %p", this); |
| StmtDebug2(" Context address %p, context handle %d", |
| context_, (Lng32) context_->getContextHandle()); |
| |
| clonedStatements = new(&heap_) Queue(&heap_); |
| // for now a statement space is allocated from the statement heap |
| space_.setParent(&heap_); |
| |
| // Set up a space object which might be used during unpacking to allocate |
| // additional space for potential upgrading of objects in the plan. This |
| // space is derived from heap_, and therefore goes away with it when the |
| // statement is destroyed. |
| // |
| unpackSpace_ = new(&heap_) Space(); |
| unpackSpace_->setType(Space::EXECUTOR_SPACE); |
| unpackSpace_->setParent(&heap_); |
| |
| stmt_type = stmt_type_; |
| |
| // Allocate statement-id |
| statement_id = (SQLSTMT_ID *)(heap_.allocateMemory(sizeof(SQLSTMT_ID))); |
| init_SQLCLI_OBJ_ID(statement_id); |
| |
| SQLMODULE_ID* new_module = |
| (SQLMODULE_ID *)(heap_.allocateMemory(sizeof(SQLMODULE_ID))); |
| init_SQLMODULE_ID(new_module); |
| |
| statement_id->module = new_module; |
| statement_id->identifier = 0; |
| statement_id->handle = 0; |
| statement_id->name_mode = statement_id_->name_mode; |
| |
| const SQLMODULE_ID* old_module = statement_id_-> module; |
| |
| if(statement_id_->module->module_name) |
| { |
| Lng32 old_module_nm_len = (Lng32) getModNameLen(old_module); |
| |
| char * mn = (char *)(heap_.allocateMemory(old_module_nm_len + 1)); |
| str_cpy_all(mn, |
| statement_id_->module->module_name, old_module_nm_len); |
| |
| new_module->module_name_len = old_module_nm_len; |
| mn[old_module_nm_len] = 0; |
| new_module->module_name = mn; |
| StmtDebug1(" Module name: \"%s\"", mn); |
| } |
| |
| if(statement_id_->identifier) |
| { |
| Lng32 stmt_name_len = (Lng32) getIdLen(statement_id_); |
| char * id = |
| (char *)(heap_.allocateMemory(stmt_name_len + 1)); |
| |
| str_cpy_all(id, statement_id_->identifier, |
| stmt_name_len); |
| statement_id->identifier_len = stmt_name_len; |
| id[stmt_name_len] = 0; |
| statement_id->identifier = id; |
| } |
| |
| if (context_->aqrInfo() && |
| context_->aqrInfo()->aqrStmtInfo() && |
| (context_->aqrInfo()->aqrStmtInfo()->getSavedStmtHandle() != 0)) |
| statement_id->handle=context_->aqrInfo()->aqrStmtInfo()->getSavedStmtHandle(); |
| else |
| statement_id->handle=(void*)getContext()->getNextStatementHandle(); |
| StmtDebug1(" New statement handle: %p", statement_id->handle); |
| |
| default_input_desc = 0; |
| default_output_desc = 0; |
| |
| cursor_name_ = NULL; |
| |
| switch (statement_id->name_mode) |
| { |
| case stmt_handle: |
| // nothing to handle for now... |
| break; |
| |
| case stmt_name: |
| // nothing to handle for now... |
| break; |
| |
| case cursor_name: |
| if (cn) |
| setCursorName(cn); |
| else |
| setCursorName(statement_id->identifier); |
| break; |
| |
| case stmt_via_desc: |
| case curs_via_desc: |
| { |
| SQLDESC_ID tmpDescId; |
| if (statement_id->name_mode == stmt_via_desc) |
| { |
| init_SQLDESC_ID(&tmpDescId, |
| SQLCLI_CURRENT_VERSION, |
| stmt_via_desc, |
| statement_id->module, |
| statement_id->identifier, |
| 0, |
| SQLCHARSETSTRING_ISO88591, |
| (Lng32) getIdLen(statement_id) |
| ); |
| } |
| else |
| { |
| init_SQLDESC_ID(&tmpDescId, |
| SQLCLI_CURRENT_VERSION, |
| curs_via_desc, |
| statement_id->module, |
| statement_id->identifier, |
| 0, |
| SQLCHARSETSTRING_ISO88591, |
| (Lng32) getIdLen(statement_id) |
| ); |
| } |
| |
| // get the value of the name from the descriptor |
| SQLCLI_OBJ_ID* tmpObjId = Descriptor::GetNameViaDesc(&tmpDescId, |
| context_,heap_); |
| |
| // now that we have used the statement_id->identifier to get the |
| // descriptor, we can get rid of it as it won't be used again... |
| // NOTE: init_SQLDESC_ID() doesn't make a copy of the identifier so |
| // it isn't safe to free statement_id->identifier until after |
| // the call to GetNameViaDesc(). |
| if (statement_id->identifier) |
| { |
| heap_.deallocateMemory((char*)statement_id->identifier); |
| statement_id->identifier = 0; |
| } |
| |
| if (tmpObjId) |
| { |
| statement_id->identifier_len = (Lng32) getIdLen(tmpObjId); |
| char * id = |
| (char *)(heap_.allocateMemory(statement_id->identifier_len + 1)); |
| if (tmpObjId->identifier) { |
| str_cpy_all(id, tmpObjId->identifier, |
| statement_id->identifier_len); |
| id[statement_id->identifier_len] = 0; |
| // fix memory leak (genesis case 10-981230-3244) |
| // caused by not freeing a non-null tmpString. |
| heap_.deallocateMemory((char*)tmpObjId->identifier); |
| statement_id->identifier = id; |
| } |
| |
| else |
| { |
| statement_id->identifier =0; |
| statement_id->identifier_len = 0; |
| } |
| heap_.deallocateMemory(tmpObjId); |
| } |
| |
| if(statement_id->identifier == 0) |
| { |
| // probably an error |
| } |
| |
| if (statement_id_->name_mode == curs_via_desc) |
| { |
| // now that the cursor name is set... we don't look in the descriptor |
| // again. The name_mode is now cursor_name *NOT* curs_via_desc. |
| |
| setCursorName(statement_id->identifier); |
| statement_id->name_mode = cursor_name; |
| } |
| else |
| { |
| // now that the statement name is set... we don't look in the descriptor |
| // again. The name_mode is now stmt_name *NOT* stmt_via_desc. |
| |
| cursor_name_ = 0; |
| statement_id->name_mode = stmt_name; |
| } |
| } |
| break; |
| |
| default: |
| // error |
| break; |
| } |
| |
| #ifdef _DEBUG |
| switch (statement_id->name_mode) |
| { |
| case stmt_handle: |
| StmtDebug0(" Name mode: stmt_handle"); |
| break; |
| |
| case stmt_name: |
| StmtDebug0(" Name mode: stmt_name"); |
| StmtDebug1(" Statement name: \"%s\"", statement_id->identifier); |
| break; |
| |
| case cursor_name: |
| StmtDebug0(" Name mode: cursor_name"); |
| StmtDebug1(" Cursor name: \"%s\"", statement_id->identifier); |
| break; |
| |
| default: |
| StmtDebug1(" *** UNKNOWN NAME MODE %d", |
| (Lng32) statement_id->name_mode); |
| break; |
| } |
| #endif |
| |
| if (statement_id_->version > SQLCLI_STATEMENT_VERSION_1) |
| statement_id->tag = statement_id_->tag; |
| else |
| statement_id->tag = 0; |
| |
| currentOfCursorStatement_ = NULL; |
| |
| source_str = 0; |
| source_length = 0; |
| charset_ = SQLCHARSETCODE_ISO88591; |
| |
| schemaName_ = NULL; |
| schemaNameLength_ = 0; |
| |
| recompControlInfo_ = NULL; |
| recompControlInfoLen_ = 0; |
| |
| root_tdb = 0; |
| root_tcb = 0; |
| root_tdb_size = 0; |
| |
| lnil_ = NULL; |
| inputData_ = NULL; |
| inputDatalen_ = 0; |
| |
| |
| uniqueStmtId_ = NULL; |
| uniqueStmtIdLen_ = 0; |
| parentQid_ = NULL; |
| parentQidSystem_[0] = '\0'; |
| |
| if (stmt_type_ == STATIC_STMT) |
| // STATIC statement. These are already 'prepared'. |
| // They are in Close Cursor state. |
| setState(CLOSE_); |
| |
| flags_ = 0; |
| |
| setComputeBulkMoveInfo(TRUE); |
| |
| statementGlobals_ = |
| new(&heap_) ExMasterStmtGlobals(0, /* fixup will add the real number */ |
| cliGlobals_, |
| this, |
| 1 /* create gui scheduler */, |
| &space_, |
| &heap_); |
| |
| // stored the type in statementGlobals |
| if(stmt_type_ == STATIC_STMT){ |
| statementGlobals_->setStmtType(ExExeStmtGlobals::STATIC); |
| } |
| |
| if (stmt_type == DYNAMIC_STMT) |
| aqrStmtInfo_ = new(&heap_) AQRStatementInfo(&heap_); |
| else |
| aqrStmtInfo_ = NULL; |
| |
| StmtDebug1("[END constructor] %p", this); |
| childQueryId_ = NULL; |
| childQueryIdLen_ = 0; |
| childQueryCostInfo_ = NULL; |
| childQueryCompStatsInfo_ = NULL; |
| } // Statement::Statement |
| |
| Statement::~Statement() |
| { |
| StmtDebug1("[BEGIN destructor] %p", this); |
| |
| dealloc(); |
| if (compileStatsArea_ != NULL) |
| { |
| NADELETE(compileStatsArea_, ExStatisticsArea, compileStatsArea_->getHeap()); |
| compileStatsArea_ = NULL; |
| } |
| if (statement_id) |
| { |
| if (statement_id->module) |
| { |
| if (statement_id->module->module_name) |
| { |
| heap_.deallocateMemory((char*)statement_id->module->module_name); |
| } |
| heap_.deallocateMemory((char*)statement_id->module); |
| statement_id->module = 0; |
| } |
| if (statement_id->identifier) |
| { |
| heap_.deallocateMemory((char*)statement_id->identifier); |
| statement_id->identifier = 0; |
| } |
| heap_.deallocateMemory(statement_id); |
| statement_id = 0; |
| } |
| |
| if (cursor_name_) |
| { |
| heap_.deallocateMemory(cursor_name_); |
| cursor_name_ = 0; |
| } |
| |
| if (source_str) |
| { |
| NADELETEBASIC(source_str, context_->exHeap()); |
| |
| // heap_.deallocateMemory(source_str); |
| source_str = NULL; |
| } |
| |
| // Release the TDB tree. assignRootTdb() does take into account |
| // statement cloning and will not actually deallocate memory |
| // occupied by the TDB tree if this statement is a clone. |
| assignRootTdb(NULL); |
| |
| // Delete cloned statements |
| // Note: Context should be used to deallocate cloned statements |
| // if the original statement being cloned from is deallocated... |
| clonedStatements->position(); |
| Statement *clone = (Statement *) clonedStatements->getNext(); |
| for (; clone != NULL; clone = (Statement *) clonedStatements->getNext()) |
| { |
| delete clone; |
| } |
| |
| NADELETE(clonedStatements, Queue, &heap_); |
| |
| // Delete child stored procedure result set statements. Note that |
| // the context has already removed these child statements from its |
| // statement lists before calling this Statement destructor. |
| ExRsInfo *rsInfo = getResultSetInfo(); |
| if (rsInfo) |
| { |
| ULng32 numChildren = rsInfo->getNumEntries(); |
| for (ULng32 i = 1; i <= numChildren; i++) |
| { |
| Statement *rsChild = NULL; |
| const char *proxySyntax = NULL; |
| NABoolean open = FALSE; |
| NABoolean closed = FALSE; |
| NABoolean prepared = FALSE; |
| NABoolean found = rsInfo->getRsInfo(i, rsChild, proxySyntax, |
| open, closed, prepared); |
| if (found && rsChild) |
| delete rsChild; |
| } |
| |
| // All the child statements have gone away and this statement is |
| // about to go away. We can destroy the ExRsInfo object that was |
| // storing information about the children. |
| statementGlobals_->deleteResultSetInfo(); |
| } |
| |
| // If this is a stored procedure result set, inform the parent that |
| // this child is going away |
| if (parentCall_) |
| { |
| ExRsInfo *rsInfo = parentCall_->getResultSetInfo(); |
| if (rsInfo) |
| { |
| StmtDebug1(" About to unbind proxy statement %p ", this); |
| rsInfo->unbind(this); |
| } |
| } |
| |
| if (default_input_desc) |
| { |
| delete default_input_desc; |
| default_input_desc = 0; |
| } |
| |
| if (default_output_desc) |
| { |
| delete default_output_desc; |
| default_output_desc = 0; |
| } |
| |
| if ( udrSecurity_ ) |
| { |
| if ( udrSecurity_->entries ()) |
| { |
| for ( UInt32 i=0; i < udrSecurity_->entries (); i++) |
| { |
| delete (*udrSecurity_)[i]; |
| } |
| udrSecurity_->clear (); |
| } // if udrSecurity_->entries () |
| |
| delete udrSecurity_; |
| udrSecurity_ = 0; |
| } // if udrSecurity_ |
| |
| StmtDebug1("[END destructor] %p", this); |
| |
| if (cliGlobals_->getStatsGlobals() != NULL) |
| { |
| if (stmtStats_ != NULL) |
| { |
| int error = cliGlobals_->getStatsGlobals()->getStatsSemaphore(cliGlobals_->getSemId(), |
| cliGlobals_->myPin()); |
| if (stmtStats_->getMasterStats() != NULL) |
| { |
| stmtStats_->getMasterStats()->setStmtState(Statement::DEALLOCATED_); |
| stmtStats_->getMasterStats()->setEndTimes(! stmtStats_->aqrInProgress()); |
| } |
| cliGlobals_->getStatsGlobals()->removeQuery(cliGlobals_->myPin(), stmtStats_); |
| cliGlobals_->getStatsGlobals()->releaseStatsSemaphore(cliGlobals_->getSemId(),cliGlobals_->myPin()); |
| } |
| } |
| else |
| { |
| if (stmtStats_ != NULL) |
| { |
| NADELETE(stmtStats_, StmtStats, stmtStats_->getHeap()); |
| } |
| } |
| stmtStats_ = NULL; |
| if (childQueryId_ != NULL) |
| { |
| NADELETEBASIC(childQueryId_, &heap_); |
| childQueryId_ = NULL; |
| } |
| if (childQueryCostInfo_ != NULL) |
| { |
| NADELETEBASIC(childQueryCostInfo_, &heap_); |
| childQueryCostInfo_ = NULL; |
| } |
| if (childQueryCompStatsInfo_ != NULL) |
| { |
| NADELETEBASIC(childQueryCompStatsInfo_, &heap_); |
| childQueryCompStatsInfo_ = NULL; |
| } |
| if (spaceObject_ != NULL) |
| { |
| delete spaceObject_; |
| spaceObject_ = NULL; |
| } |
| } // Statement::~Statement |
| |
| NABoolean Statement::updateInProgress () |
| { |
| // if there are incomplete insert, delete, update or prepare operations, |
| // return TRUE, otherwise return FALSE. |
| if (!isPubsubHoldable() |
| ) |
| { |
| if (((getState() == Statement::OPEN_ || |
| getState() == Statement::FETCH_ || |
| getState() == Statement::RELEASE_TRANS_) && |
| (getRootTdb() && |
| (getRootTdb()->updDelInsertQuery() || |
| getRootTdb()->isEmbeddedUpdateOrDelete()) ) |
| ) ) |
| return TRUE; |
| } |
| return FALSE; |
| } |
| |
| // releaseTransaction is called by Statement::close, Statement::dealloc, |
| // ContextCli::releaseAllTransactionalRequests and |
| // ContextCli::removeFromOpenstatementList. |
| |
| RETCODE Statement::releaseTransaction(NABoolean allWorkRequests, |
| NABoolean alwaysSendReleaseMsg, |
| NABoolean statementRemainsOpen) |
| { |
| |
| StmtDebug2("[BEGIN releaseTransaction] %p, allWorkRequests = %s", |
| this, (allWorkRequests ? "TRUE" : "FALSE")); |
| |
| // do not send new DML request. |
| statementGlobals_->setNoNewRequest(TRUE); |
| |
| if (!statementRemainsOpen && // is holdable cursor? |
| root_tcb && |
| root_tcb->needsDeregister()) |
| root_tcb->deregisterCB(); |
| |
| Int64 cbWaitStartTime = NA_JulianTimestamp(); |
| |
| statementGlobals_->setNoNewRequest(FALSE); |
| |
| ExRtFragTable *fragTable = statementGlobals_->getRtFragTable(); |
| if (fragTable) |
| { |
| if (root_tcb && root_tcb->fatalErrorOccurred()) |
| { |
| // We can easily get deadlocks from the ESPs at this |
| // point, due to producer ESPs' ex_split_bottom_tcb:: |
| // workState_ set to WAIT_FOR_MORE_REQUESTORS -- |
| // see solution 10-070108-1544. This entire statement |
| // will soon be deallocated, so place all IpcConnections |
| // with pending I/O into the error state. |
| fragTable->abandonPendingIOs(); |
| } |
| else |
| { |
| // For the ESPs, send out the word that we want to get out |
| // of the transaction (maybe just temporarily) |
| fragTable->releaseTransaction(allWorkRequests, |
| alwaysSendReleaseMsg); |
| |
| // Note that for the cancel broker, we have already sent the |
| // finish message -- see the call to deregisterCB in this |
| // method above. |
| |
| // wait until the messages have come back |
| // (that's all messages if allWorkRequests is set to TRUE, |
| // and only the transactional msges if it is FALSE) |
| |
| // how long should we wait for release work reply from esps? |
| // upon timeout we will abort all outstanding I/Os. |
| // some facts below: |
| // |
| // - default timeout is 15 minutes |
| // - minimum wait timeout is 5 minutes |
| // - timeout = -1 means wait indefinitely. but every 2 minutes |
| // write a timeout message to EMS log. |
| // - timeout = -2 means that we time out after 2 minutes, then |
| // abend up to 8 esps that are not responding so we can collect |
| // saveabend files for debug purpose. |
| // |
| bool waitForever = false; |
| bool abendEsps = false; |
| IpcTimeout timeout = context_->getSessionDefaults()-> |
| getEspReleaseWorkTimeout(); |
| if (timeout <= 0) |
| { |
| if (timeout == -1) |
| waitForever = true; |
| else if (timeout == -2) |
| abendEsps = true; |
| |
| timeout = 2*6000; // 2 minutes |
| } |
| else if (timeout < 5*60) |
| { |
| // minimum timeout is 5 minutes |
| timeout = 5*6000; |
| } |
| else |
| { |
| timeout *= 100; |
| } |
| |
| NABoolean hasTimedout; |
| while (fragTable->hasOutstandingTransactionalMsges() || |
| (allWorkRequests && fragTable->hasOutstandingWorkRequests())) |
| { |
| if (fragTable->getState() == ExRtFragTable::ERROR) |
| { |
| // some error occured |
| context_->diags() << DgSqlCode(-EXE_INTERNAL_ERROR); |
| ComDiagsArea *diagsPtr = statementGlobals_->getDiagsArea(); |
| if (diagsPtr) |
| context_->diags().mergeAfter(*diagsPtr); |
| |
| // abort all outstanding I/Os |
| fragTable->abandonPendingIOs(); |
| break; |
| } |
| |
| // wait for an I/O to complete, we know there are some |
| // transaction requests that should complete... |
| statementGlobals_->getIpcEnvironment()->getAllConnections()-> |
| waitOnAll(timeout, FALSE, &hasTimedout); |
| |
| if (hasTimedout) |
| { |
| // timed out waiting for esp reply. something is wrong. |
| char errMsg[200]; |
| str_sprintf(errMsg, "Statement still has %d connections with I/O outstanding - %d trans msgs and %d work reqs.", |
| statementGlobals_->getIpcEnvironment() |
| ->getAllConnections()->getNumPendingIOs(), |
| fragTable->numOutstandingTransactionalMsges(), |
| fragTable->numOutstandingWorkRequests()); |
| |
| if (!waitForever) |
| { |
| context_->diags() << DgSqlCode(-EXE_RELEASE_WORK_TIMEOUT) |
| << DgString0(errMsg); |
| |
| if (abendEsps) |
| { |
| // abend up to 8 esps so we can collect saveabend |
| // files for debug purpose. |
| CollIndex numEsps = 8; |
| GuaProcessHandle phandles[8]; |
| statementGlobals_->getIpcEnvironment()->getAllConnections()-> |
| fillInListOfPendingPhandles(phandles, numEsps); |
| |
| char logMsg[300]; |
| char *initMsg= (char *)" Executor abended non-responding ESPs "; |
| memset(logMsg, '\0', sizeof logMsg); |
| str_cat(logMsg, initMsg, logMsg); |
| for (CollIndex i = 0; i < numEsps; i++) |
| { |
| // abend esp with saveabend file |
| phandles[i].dumpAndStop(true, true); |
| if (i > 0) |
| str_cat(logMsg, ", ", logMsg); |
| phandles[i].toAscii(logMsg + str_len(logMsg), 300 - str_len(logMsg)); |
| } |
| str_cat(logMsg, ".", logMsg); |
| |
| context_->diags() << DgString1(logMsg); |
| } // if (abendEsps) |
| |
| // abort all outstanding I/Os |
| fragTable->abandonPendingIOs(); |
| break; |
| } // if (!waitForever) |
| else |
| { |
| // wait forever. log a warning msg and go back to wait. |
| char logMsg[500]; |
| const char *initMsg="Query looping while waiting for release work reply from ESPs "; |
| logMsg[0] = '\0'; |
| str_cat(logMsg, initMsg, logMsg); |
| // pins of waited processes are concatenated to logMsg |
| // each pin should be 28 bytes or less |
| statementGlobals_->getIpcEnvironment()->getAllConnections()-> |
| fillInListOfPendingPins(logMsg + str_len(initMsg), |
| 300 - str_len(initMsg), 8); |
| str_cat(logMsg, ". ", logMsg); |
| str_cat(logMsg, errMsg, logMsg); |
| SQLMXLoggingArea::logExecRtInfo("Statement.cpp", 0, logMsg, 0); |
| } |
| } // if (hasTimedout) |
| else |
| { |
| // we did not timeout because some I/O completed first. |
| // now do some work on the fragment dir. |
| if (fragTable->getState() != ExRtFragTable::ERROR) |
| { |
| fragTable->workOnRequests(); |
| } |
| else |
| { |
| // some error occured |
| context_->diags() << DgSqlCode(-EXE_INTERNAL_ERROR); |
| ComDiagsArea *diagsPtr = statementGlobals_->getDiagsArea(); |
| if (diagsPtr) |
| context_->diags().mergeAfter(*diagsPtr); |
| |
| // abort all outstanding I/Os |
| fragTable->abandonPendingIOs(); |
| break; |
| } |
| } |
| } // while (..) |
| } // if (root_tcb && ..) |
| } // if (fragTable) |
| |
| // In most case, the wait loop above will ensure that cancel broker messages |
| // are now complete. However, if there is no fragTable, or there was |
| // fatalErrorOccurred or if in the wait loop some problem happened that |
| // caused the loop to exit early, then here we must wait for cancel broker |
| // to reply to start and finish message. |
| if (root_tcb && !statementRemainsOpen) |
| root_tcb->cbMessageWait(cbWaitStartTime); |
| |
| // If there is an error either in Context diagsArea or |
| // in StatementGlobals_ diagsArea, do not update the end time |
| ComDiagsArea *diagsArea = context_->getDiagsArea(); |
| if (diagsArea->mainSQLCODE() >= 0 && stmtStats_ != NULL && |
| stmtStats_->getMasterStats() != NULL && (! stmtStats_->aqrInProgress()) |
| && stmtStats_->getMasterStats()->getExeEndTime() == -1) |
| { |
| Int64 exeEndTime = NA_JulianTimestamp(); |
| stmtStats_->getMasterStats()->setExeEndTime(exeEndTime); |
| stmtStats_->getMasterStats()->setElapsedEndTime(exeEndTime); |
| } |
| |
| // If this is a CALL statement that can produce result sets (as |
| // indicated by a non-NULL return value from getResultSetInfo()) |
| // then we may need to tell the UDR server to release this |
| // transaction. |
| ExRsInfo *rsInfo = getResultSetInfo(); |
| if (rsInfo) |
| { |
| StmtDebug0(" About to call ExRsInfo::suspendUdrTx()..."); |
| rsInfo->suspendUdrTx(*statementGlobals_); |
| StmtDebug0(" Done"); |
| } |
| |
| // Make sure there are no outstanding transactional UDR requests |
| completeUdrRequests(allWorkRequests); |
| |
| |
| // Ideally, we would want to set this state for all cursors. |
| // But we found that setting it for all results in an unnecessary call to |
| // removeFromOpenStatementList() when the state is changed from RELEASE_TRANS_ |
| // to CLOSE_ even when the cursor was already closed earlier. |
| if (isAnsiHoldable()) |
| setState(RELEASE_TRANS_); |
| else |
| if (isPubsubHoldable()) |
| { |
| if (! getRootTdb()->getPsholdUpdateBeforeFetch()) |
| setState(RELEASE_TRANS_); |
| } |
| |
| StmtDebug1("[END releaseTransaction] %p", this); |
| return SUCCESS; |
| } |
| |
| void Statement::releaseEsps(NABoolean closeAllOpens) |
| { |
| if (!statementGlobals_) |
| return; |
| |
| ExRtFragTable *fragTable = statementGlobals_->getRtFragTable(); |
| if (!fragTable) |
| return; |
| |
| fragTable->releaseEsps(closeAllOpens); |
| |
| // wait until all release esp replies have come back |
| NABoolean hasTimedout; |
| IpcTimeout espReleaseTimeout; |
| espReleaseTimeout = context_->getEspReleaseTimeout(); |
| if (espReleaseTimeout != -1) |
| espReleaseTimeout *= 6000; |
| while (fragTable->hasOutstandingReleaseEspMsges()) |
| { |
| statementGlobals_->getIpcEnvironment()->getAllConnections()-> |
| waitOnAll(5*6000, FALSE, &hasTimedout);// Wait up to 5 minutes |
| if (hasTimedout) |
| { |
| // not all replied after 5 minutes, something is wrong |
| char msg[300]; |
| str_sprintf(msg, "Timedout! Still have %d release esp messages and %d I/O outstanding.", |
| fragTable->numOutstandingReleaseEspMsges(), |
| statementGlobals_->getIpcEnvironment() |
| ->getAllConnections()->getNumPendingIOs()); |
| SQLMXLoggingArea::logExecRtInfo("Statement.cpp", 0, msg, 0); |
| // abort remaining release esp msgs |
| fragTable->abandonPendingIOs(); |
| } // if (hasTimedout) |
| } // while |
| } |
| |
| RETCODE Statement::bindTo(Statement * stmt) |
| { |
| StmtDebug2("[BEGIN bindTo] Binding clone %p to master %p", |
| this, stmt); |
| |
| root_tdb = stmt->root_tdb; |
| clonedFrom_ = stmt; |
| setState(CLOSE_); // does this work here???? |
| setCloned(); |
| stmt->clonedStatements->insert((void *) this); |
| |
| StmtDebug0("[END bindTo]"); |
| return SUCCESS; |
| } |
| |
| bool Statement::isOpen() |
| { |
| switch (stmt_state) |
| { |
| case OPEN_: |
| case FETCH_: |
| case EOF_: |
| case PREPARE_: |
| case RELEASE_TRANS_: |
| case INITIAL_: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| RETCODE Statement::close(ComDiagsArea &diagsArea, NABoolean inRollback) |
| { |
| StmtDebug2("[BEGIN close] %p, stmt state %s", this, stmtState(getState())); |
| RETCODE rc = SUCCESS; |
| NABoolean readyToReturn = FALSE; |
| |
| if (!isOpen()) |
| { |
| // trying to close a statement which is already in closed state |
| diagsArea << DgSqlCode(- CLI_STMT_NOT_OPEN); |
| rc = ERROR; |
| readyToReturn = TRUE; |
| } |
| |
| NABoolean closingEmbeddedIUDCursorPrematurely = FALSE; |
| |
| if (!readyToReturn) |
| { |
| if ((stmt_state == OPEN_) && |
| (root_tdb && root_tdb->isEmbeddedIUDWithLast1())) |
| { |
| closingEmbeddedIUDCursorPrematurely = TRUE; |
| } |
| |
| // Update stmtGlobals transid with current transid. |
| // The stmtGlobals transid is used in sending release msg to remote esps. |
| // The context transid can be obsolete if the transaction has been |
| // committed/aborted by the user. |
| Int64 transid; |
| if (!context_->getTransaction()->getCurrentXnId(&transid)) |
| getGlobals()->castToExExeStmtGlobals()->getTransid() = transid; |
| else |
| getGlobals()->castToExExeStmtGlobals()->getTransid() = (Int64)-1; |
| |
| // cancel the down request to my child |
| if (root_tcb) |
| { |
| ComDiagsArea *diagsPtr = NULL; |
| |
| // pass flag to cancel to collect diags from queue entries |
| // if stmt_state is OPEN_. This is needed to pick up diags |
| // info from failed DDL operations. |
| NABoolean pickUpDiags = |
| ( // False if DML, true otherwise |
| (root_tdb->getQueryType() < ComTdbRoot::SQL_SELECT_UNIQUE) || |
| (root_tdb->getQueryType() > ComTdbRoot::SQL_DELETE_NON_UNIQUE) |
| ); |
| |
| Int32 rc1 = |
| root_tcb->cancel(statementGlobals_, diagsPtr, pickUpDiags); |
| |
| StmtDebug1(" root_tcb->cancel() returned %s", |
| RetcodeToString((RETCODE) rc1)); |
| if (diagsPtr) |
| { |
| diagsArea.mergeAfter(*diagsPtr); |
| diagsPtr->decrRefCount(); |
| diagsPtr = NULL; |
| } |
| if (root_tcb->fatalErrorOccurred()) |
| { |
| dealloc(); |
| space_.freeBlocks(); |
| statementGlobals_->reAllocate(1); |
| setFixupState(0); |
| } |
| } |
| } |
| |
| if (!readyToReturn) |
| { |
| // If this statement has stored procedure result set children, |
| // then close each child. Ignore errors encountered while closing |
| // the children. |
| ExRsInfo *rsInfo = getResultSetInfo(); |
| if (rsInfo) |
| { |
| ULng32 numChildren = rsInfo->getNumEntries(); |
| StmtDebug0(" About to close all result set proxy statements"); |
| StmtDebug1(" Num RS children: %d", (Lng32) numChildren); |
| |
| ComDiagsArea *diagsFromProxy = NULL; |
| |
| for (ULng32 i = 1; i <= numChildren && !readyToReturn; i++) |
| { |
| Statement *proxyStmt = NULL; |
| const char *proxySyntax = NULL; |
| NABoolean open = FALSE; |
| NABoolean closed = FALSE; |
| NABoolean prepared = FALSE; |
| NABoolean found = rsInfo->getRsInfo(i, proxyStmt, proxySyntax, |
| open, closed, prepared); |
| |
| if (found && proxyStmt && proxyStmt->stmt_state != CLOSE_) |
| { |
| if (diagsFromProxy == NULL) |
| diagsFromProxy = ComDiagsArea::allocate(&heap_); |
| |
| RETCODE rcFromProxy = proxyStmt->close(*diagsFromProxy); |
| diagsFromProxy->clear(); |
| } |
| |
| } // for each proxy |
| |
| if (diagsFromProxy) |
| diagsFromProxy->deAllocate(); |
| |
| StmtDebug0(" About to call ExRsInfo::exitUdrTx()..."); |
| rsInfo->exitUdrTx(*statementGlobals_); |
| StmtDebug0(" Done"); |
| rsInfo->reset(); |
| } // if (rsInfo) |
| |
| releaseTransaction(TRUE, FALSE); |
| |
| setState(CLOSE_); |
| |
| if (closingEmbeddedIUDCursorPrematurely) |
| { |
| diagsArea << DgSqlCode(- CLI_IUD_IN_PROGRESS) << DgString0(getCursorName()->identifier); |
| if (rollbackTransaction(diagsArea)) |
| return ERROR; |
| } |
| |
| // stop the transaction, if one was started and if we are not |
| // currently rolling back this transaction |
| if (!inRollback) |
| { |
| if ((root_tdb && root_tdb->transactionReqd()) || |
| ((context_->getTransaction()->xnInProgress()) && |
| (context_->getTransaction()->exeStartedXn()) && |
| (context_->getTransaction()->autoCommit()) && |
| (autocommitXn()))) |
| { |
| if (commitTransaction(diagsArea)) |
| { |
| rc = ERROR; |
| readyToReturn = TRUE; |
| } |
| } |
| } // !inRollback |
| } // !readyToReturn |
| updateStatsAreaInContext(); |
| // Get rid of child query info if there is any |
| if (childQueryId_ != NULL) |
| { |
| NADELETEBASIC(childQueryId_, &heap_); |
| childQueryId_ = NULL; |
| childQueryIdLen_ = 0; |
| } |
| if (childQueryCostInfo_ != NULL) |
| { |
| NADELETEBASIC(childQueryCostInfo_, &heap_); |
| childQueryCostInfo_ = NULL; |
| } |
| if (childQueryCompStatsInfo_ != NULL) |
| { |
| NADELETEBASIC(childQueryCompStatsInfo_, &heap_); |
| childQueryCompStatsInfo_ = NULL; |
| } |
| |
| // clear transId from statement globals as it is no longer needed |
| // this is to avoid reuse the transId when deleting the statement |
| statementGlobals_->getTransid() = (Int64)-1; |
| |
| StmtDebug2("[END close] %p, result is %s", this, |
| RetcodeToString(rc)); |
| return rc; |
| } |
| |
| static inline void diagsTakeOver(ComDiagsArea &diagsArea, |
| CliGlobals *cliGlobals, |
| short indexIntoCompilerArray) |
| { |
| ComDiagsArea &exec_diags = |
| *cliGlobals->getArkcmp(indexIntoCompilerArray)->getDiags(); |
| if (&diagsArea != &exec_diags) |
| { |
| if (diagsArea.getFunction() == ComDiagsArea::NULL_FUNCTION) |
| diagsArea.setFunction(exec_diags.getFunction()); |
| diagsArea.mergeAfter(exec_diags); |
| exec_diags.clear(); |
| } |
| } |
| |
| NABoolean Statement::isDISPLAY() |
| { |
| QueryText qt(source_str, charset_); |
| return qt.isDISPLAY(); |
| } |
| |
| NABoolean Statement::isExeDebug(char *src, Lng32 charset) |
| { |
| if (!src) { |
| return FALSE; |
| } |
| else { |
| if (charset == SQLCHARSETCODE_UCS2) { |
| NAWchar* p = (NAWchar*)(src) + 7; |
| |
| return p[0] == NAWchar('$') && |
| p[1] == NAWchar('Z') && |
| p[2] == NAWchar('Z') && |
| p[3] == NAWchar('E') && |
| p[4] == NAWchar('X') && |
| p[5] == NAWchar('E') && |
| p[6] == NAWchar('D') && |
| p[7] == NAWchar('E') && |
| p[8] == NAWchar('B') && |
| p[9] == NAWchar('U') && |
| p[10] == NAWchar('G') ; |
| } |
| else { |
| return str_cmp(src+7, "$ZZEXEDEBUG", str_len("$ZZEXEDEBUG")) == 0; |
| } |
| } |
| } |
| |
| Int32 Statement::octetLen(char *s, Lng32 charset) |
| { |
| return charset==SQLCHARSETCODE_UCS2 ? |
| na_wcslen((const NAWchar*)s) * |
| CharInfo::maxBytesPerChar((CharInfo::CharSet)charset) : str_len(s); |
| } |
| |
| Int32 Statement::octetLenplus1(char *s, Lng32 charset) |
| { |
| return charset==SQLCHARSETCODE_UCS2 ? |
| (na_wcslen((const NAWchar*)s)+1)* |
| CharInfo::maxBytesPerChar((CharInfo::CharSet)charset) : str_len(s)+1; |
| } |
| |
| Int32 Statement::sourceLenplus1() |
| { |
| return (source_length+1)*CharInfo::maxBytesPerChar((CharInfo::CharSet)charset_); |
| } |
| |
| RETCODE Statement::prepareReturn ( const RETCODE retcode) |
| { |
| if (versionToUse_ != versionOnEntry_) |
| { |
| // We changed the current compiler, reset it |
| short index; |
| getContext()->setOrStartCompiler(versionOnEntry_, NULL, index); |
| } |
| |
| // Unconditionally reset version on entry. |
| versionOnEntry_ = COM_VERS_UNKNOWN; |
| if (retcode != NOT_FINISHED) |
| { |
| // We're done with the prepare, reset the version we used |
| versionToUse_ = COM_VERS_UNKNOWN; |
| } |
| |
| return retcode; |
| } |
| |
| /* |
| NABoolean Statement::isStandaloneQ() |
| { |
| NABoolean standaloneQ = FALSE; |
| |
| // if query from mxcs and the statement name starts with "SQL_CUR_", |
| // then it is a standalone query. This is standard and guaranteed |
| // mxcs behavior. |
| // or if query is from mxci and the statement name starts |
| // with "__SQLCI_", then it is a standalone query. |
| if ((getContext()->getSessionDefaults()->getOdbcProcess()) && |
| (statement_id->identifier_len > 0) && |
| (strncmp(statement_id->identifier, "SQL_CUR_", strlen("SQL_CUR_")) == 0)) |
| { |
| standaloneQ = TRUE; |
| } |
| else if ((getContext()->getSessionDefaults()->getMxciProcess()) && |
| (statement_id->identifier_len > 0) && |
| (strncmp(statement_id->identifier, "__SQLCI_DML_", strlen("__SQLCI_DML_")) == 0)) |
| { |
| standaloneQ = TRUE; |
| } |
| |
| return standaloneQ; |
| } |
| */ |
| RETCODE Statement::prepare(char *source, ComDiagsArea &diagsArea, |
| char *passed_gen_code, |
| ULng32 passed_gen_code_len, |
| Lng32 charset, |
| NABoolean unpackTdbs, |
| ULng32 cliFlags) |
| { |
| StmtDebug1("[BEGIN prepare] %p", this); |
| StmtDebug1(" Source: %s", |
| source ? source : (source_str ? source_str : "(NULL)")); |
| |
| state_ = INITIAL_STATE_; |
| |
| if (aqrReprepareNeeded()) |
| setAqrReprepareNeeded(FALSE); |
| |
| if (NOT ((! (cliFlags & PREPARE_AUTO_QUERY_RETRY)) && |
| (cliFlags & PREPARE_STANDALONE_QUERY) && |
| source && |
| (stmt_type == DYNAMIC_STMT) && |
| unpackTdbs && |
| (context_->getNumOfCliCalls() == 1))) |
| { |
| // do not use query text cache for explicitely prepared queries. |
| cliFlags |= PREPARE_NO_TEXT_CACHE; |
| } |
| |
| if (context_->aqrInfo()) |
| context_->aqrInfo()->setXnStartedAtPrepare(FALSE); |
| |
| RETCODE rc = prepare2(source, diagsArea, |
| passed_gen_code, passed_gen_code_len, |
| charset, unpackTdbs, cliFlags); |
| |
| StmtDebug2("[END prepare] %p, result is %s", this, RetcodeToString(rc)); |
| return rc; |
| } |
| |
| RETCODE Statement::prepare2(char *source, ComDiagsArea &diagsArea, |
| char *passed_gen_code, |
| ULng32 passed_gen_code_len, |
| Lng32 charset, |
| NABoolean unpackTdbs, |
| ULng32 cliFlags) |
| { |
| ULng32 fetched_gen_code_len = 0L; |
| char *fetched_gen_code = NULL; |
| short retcode = SUCCESS; |
| short indexIntoCompilerArray = 0; |
| |
| // if there is any error using embedded cmpiler and we will switch to regular compiler |
| NABoolean canUseEmbeddedArkcmp = ((cliFlags & PREPARE_USE_EMBEDDED_ARKCMP) != 0) ; // This flag |
| // will be set only by the master. If a Prepare call is made from the |
| // compiler(including the embedded compiler), it will use the regular compiler. |
| |
| ExSqlComp::OperationStatus status; |
| Dealloc dealloc; // DTOR calls NAHeap::deallocateMemory for an object |
| |
| // NABoolean newOperation = TRUE; |
| NABoolean newOperation = (NOT ((cliFlags & PREPARE_NOT_A_NEW_OPERATION) != 0)); |
| NABoolean reComp = (cliFlags & PREPARE_RECOMP) != 0; |
| NABoolean aqRetry = (cliFlags & PREPARE_AUTO_QUERY_RETRY) != 0; |
| NABoolean deCache = (cliFlags & PREPARE_WITH_DECACHE) != 0; |
| NABoolean noTextCache = (cliFlags & PREPARE_NO_TEXT_CACHE) != 0; |
| NABoolean standaloneQuery = (cliFlags & PREPARE_STANDALONE_QUERY) != 0; |
| NABoolean doNotCache = (cliFlags & PREPARE_DONT_CACHE) != 0; |
| this->setStandaloneQ(standaloneQuery); |
| |
| NABoolean wmsMonitoringNeeded = (cliFlags & PREPARE_MONITOR_THIS_QUERY) !=0; |
| |
| this->setWMSMonitorQuery(wmsMonitoringNeeded); |
| // Remove compileStatsArea, if it exists |
| if (compileStatsArea_ != NULL) |
| { |
| NADELETE(compileStatsArea_, ExStatisticsArea, compileStatsArea_->getHeap()); |
| compileStatsArea_ = NULL; |
| } |
| // We should not do prepares on a cloned statement. Clones share a |
| // TDB tree with some other statement and compiles/recompiles should |
| // always be done by that other statement. |
| ex_assert(!isCloned(), |
| "Statement::prepare() should not be called on a clone"); |
| |
| // We may be doing a prepare for two reasons: 1) as part of a |
| // dynamic SQL PREPARE operation or 2) as part of automatic |
| // recompilation. The first case includes such things as |
| // EXECUTE IMMEDIATE and internal calls to prepare statements. |
| // |
| // The value returned by the noWaitOpPending() member in the Statement |
| // object must be interpreted very carefully here, because its |
| // meaning depends on which of these two cases |
| // we are in. In the first case, where we are doing an explicit |
| // prepare, noWaitOpPending() TRUE implies that we are trying |
| // to start a new explicit operation on a statement that already |
| // has a no-wait operation in progress. This is a user error, |
| // and is raised as such in this method. |
| // |
| // In the second case, however, this method has been called |
| // from another Statement method to do an automatic recompilation. |
| // If noWaitOpPending() == TRUE in this case, it is because the |
| // calling method is processing an outstanding no-wait operation. |
| // So, for example, we might be attempting an automatic |
| // recompilation while redriving a no-wait fetch. This is not |
| // a user error; in fact we want the automatic recompilation |
| // to succeed. |
| // |
| // The way to distinguish these two cases is to consider the |
| // reComp parameter. If it is FALSE, we know it is the first |
| // case. If it is TRUE, we know it is the second. |
| // |
| // For the moment, we make all automatic recompilations waited, |
| // even if we are redriving a no-wait operation. The reason we |
| // do this is the Statement object lacks the state, and the |
| // calling methods lack the logic, to detect on a redrive that |
| // a no-wait automatic recompilation is in effect. This is a |
| // refactoring opportunity for a clever developer in a future |
| // release ;-). |
| // |
| // Note that since we make automatic recompilations waited, |
| // we treat the noWaitOpEnabled_ flag differently in this case |
| // as well. |
| // |
| // In this method, then, instead of testing noWaitOpPending_ |
| // directly, we use the following flag which takes reComp |
| // into account. |
| |
| |
| Lng32 rsa = getRowsetAtomicity(); |
| if (context_->getSessionDefaults()->getRowsetAtomicity() != -1) |
| rsa = context_->getSessionDefaults()->getRowsetAtomicity(); //NOT_ATOMIC_; |
| |
| if (canUseEmbeddedArkcmp && !context_->isEmbeddedArkcmpInitialized()) |
| { |
| Int32 embeddedArkcmpSetup; |
| // embeddedArkcmpSetup = arkcmp_main_entry(); |
| embeddedArkcmpSetup = context_->switchToCmpContext((Int32)0); |
| if (embeddedArkcmpSetup == 0) |
| { |
| context_->setEmbeddedArkcmpIsInitialized(TRUE); |
| } |
| else if (embeddedArkcmpSetup == -2) |
| { |
| diagsArea << DgSqlCode(-2079); |
| return ERROR; |
| } |
| else |
| { |
| context_->setEmbeddedArkcmpIsInitialized(FALSE); |
| context_->setEmbeddedArkcmpContext(NULL); |
| } |
| } |
| // Set the Global CmpContext from the one saved in the CLI context |
| // for proper operation |
| if (context_->isEmbeddedArkcmpInitialized() && |
| context_->getEmbeddedArkcmpContext()) |
| { |
| cmpCurrentContext = context_->getEmbeddedArkcmpContext(); |
| } |
| |
| if (newOperation) |
| assert ((aqRetry && source) || |
| ((!reComp) || (reComp && (!source)))); |
| |
| if ((reComp) && (stmt_type == STATIC_STMT)) |
| { |
| } |
| #ifdef _DEBUG |
| if (getenv("TEST_INFO_EVENT")) |
| { |
| |
| SQLMXLoggingArea::logSQLMXEventForError(0000, "Dummy Error with Dummy SQCode and QID", "DummyQID", FALSE); |
| |
| SQLMXLoggingArea::logExecRtInfo("Statement.cpp",999,"Testing info event", 999); |
| |
| SQLMXLoggingArea::logSQLMXAbortEvent("Statement.cpp",888, "testing abort event"); |
| SQLMXLoggingArea::logSQLMXAssertionFailureEvent("Statement.cpp",777,"testing assertion failure"); |
| SQLMXLoggingArea::logSQLMXDebugEvent("debug event" ,69,__LINE__); |
| |
| SQLMXLoggingArea::logMVRefreshInfoEvent("mv refresh info"); |
| SQLMXLoggingArea::logMVRefreshErrorEvent("mv refresh error"); |
| |
| SQLMXLoggingArea::logCliReclaimSpaceEvent(100,200,300,400); |
| |
| SQLMXLoggingArea::logCompNQCretryEvent("select * from t1 where a > 10"); |
| SQLMXLoggingArea::logSortDiskInfo("disk101", 25, 2080); |
| |
| const char msgT[] = "SQL compiler: Optimization failed at pass two or higher. " |
| "Execution will use the plan generated at pass one instead."; |
| |
| SQLMXLoggingArea::logSQLMXPredefinedEvent(msgT, LL_WARN); |
| } |
| #endif |
| |
| if (passed_gen_code) |
| { |
| // it is legal for passed_gen_code to be passed in as NULL here! |
| copyGenCode(passed_gen_code, passed_gen_code_len, FALSE); |
| |
| |
| } |
| else if ((source) || // source is passed in. Preparing a stmt. |
| ((!source) && (reComp)) || // recompiling a statement at runtime. |
| !newOperation) |
| { |
| if (isExeDebug(source, charset)) |
| { |
| NADebug(); |
| diagsArea << DgSqlCode(- CLI_STMT_NOT_PREPARED); |
| // OK to return directly, we haven't done anything to |
| // the current compiler settings yet |
| return ERROR; |
| } |
| |
| if (context_->checkAndSetCurrentDefineContext()) |
| { |
| |
| // define context changed, kill arkcmps, if they are runing. |
| for (short i = 0; |
| i < getContext()->getNumArkcmps(); |
| i++) |
| cliGlobals_->getArkcmp(i)->endConnection(); |
| |
| } |
| |
| // Get the version of compiler to use initially. There are several possibilities. |
| // a) No particular version compiler is required, we use whatever is the current version. |
| // b) The compiler version was explicitly set from outside, using setOrStartCompiler. |
| // This may happen for automatic retry of plan version errors, and for certain DDL |
| // operations. |
| // c) The compiler version was requested, using the versionToUse_ data member. |
| // This may happen if a non-retryable statement encountered a plan version error. |
| // In all cases, save whatever version was the default for our caller since prepare |
| // may automatically set the compiler version further down the road. |
| // |
| versionOnEntry_ = context_->getVersionOfCompiler(); |
| if (newOperation && (versionToUse_ == COM_VERS_UNKNOWN)) |
| { |
| // This is a new prepare, and a particular version was not requested. |
| // Use the version from the context. |
| indexIntoCompilerArray = context_->getIndexToCompilerArray(); |
| versionToUse_ = versionOnEntry_; |
| } |
| else |
| { |
| // This is a redrive of a nowait prepare, or a particular version was requested. |
| // Use whatever we used originally/whatever is requested. |
| getContext()->setOrStartCompiler(versionToUse_, NULL, indexIntoCompilerArray); |
| } |
| |
| // From this point on, do not return directly as that will mess up the current compiler version. |
| // Instead, do |
| // return prepareReturn (<retcode>); |
| short retry = TRUE; |
| char * data = NULL; |
| ULng32 dataLen = 0; |
| ExSqlComp::Operator op; |
| while (retry) |
| { |
| if (newOperation) |
| { |
| |
| // Build request and send to arkcmp. |
| if ((reComp) || (aqRetry && deCache)) |
| { |
| CmpCompileInfo c((reComp ? source_str : source), |
| (reComp ? sourceLenplus1() : octetLenplus1(source, charset)), |
| (Lng32) (reComp ? charset_ : charset), |
| schemaName_, schemaNameLength_+1, |
| getInputArrayMaxsize(), (short)rsa); |
| |
| if (aqRetry) |
| c.setAqrPrepare(aqRetry); |
| else if (noTextCache) |
| c.setNoTextCache(noTextCache); |
| c.setDoNotCachePlan(doNotCache); |
| |
| if (standaloneQuery) |
| c.setStandaloneQuery(standaloneQuery); |
| |
| // if this statement was statically compiled with odbc process |
| // on, then set it here. This will be used at auto recomp time. |
| c.setOdbcProcess(odbcProcess()); |
| c.setSystemModuleStmt(systemModuleStmt()); |
| c.setAnsiHoldable(isAnsiHoldable()); |
| c.setPubsubHoldable(isPubsubHoldable()); |
| dataLen = c.getLength(); |
| // Dealloc dtor will deallocate the memory allocated by next statement |
| data = (char *)dealloc.getAddr |
| (new(dealloc.setHeap(&heap_)) char[dataLen]); |
| c.pack(data); |
| |
| // Release the existing TDB tree if one exists |
| assignRootTdb(NULL); |
| |
| op = (getStatementType() == DYNAMIC_STMT) ? |
| EXSQLCOMP::SQLTEXT_RECOMPILE : |
| EXSQLCOMP::SQLTEXT_STATIC_RECOMPILE; |
| |
| |
| // The R1.8 compiler does not understand the above type |
| // EXSQLCOMP::SQLTEXT_STATIC_RECOMPILE. So if we are talking |
| // to an R1.8 compiler then we have to change the type to |
| // EXSQLCOMP::SQLTEXT_STATIC_COMPILE |
| |
| if (cliGlobals_->getArkcmp(indexIntoCompilerArray)-> |
| getVersion() == COM_VERS_R1_8 ) |
| { |
| op = (getStatementType() == DYNAMIC_STMT) ? |
| EXSQLCOMP::SQLTEXT_RECOMPILE : |
| EXSQLCOMP::SQLTEXT_STATIC_COMPILE; |
| } |
| } // recompiling statement |
| else |
| { |
| CmpCompileInfo c(source, octetLenplus1(source, charset), (Lng32) charset, |
| NULL, 0, |
| getInputArrayMaxsize(), (short)rsa); |
| |
| if (aqRetry) |
| c.setAqrPrepare(aqRetry); |
| else if (noTextCache) |
| c.setNoTextCache(noTextCache); |
| c.setDoNotCachePlan(doNotCache); |
| |
| if (standaloneQuery) |
| c.setStandaloneQuery(standaloneQuery); |
| c.setAnsiHoldable(isAnsiHoldable()); |
| c.setPubsubHoldable(isPubsubHoldable()); |
| dataLen = c.getLength(); |
| // Dealloc dtor will deallocate the memory allocated by next statement |
| data = (char *)dealloc.getAddr |
| (new(dealloc.setHeap(&heap_)) char[dataLen]); |
| c.pack(data); |
| |
| op = EXSQLCOMP::SQLTEXT_COMPILE; |
| } |
| |
| |
| // send request |
| // request is nowaited if noWaitOpEnabled and not recompile |
| NABoolean waited = TRUE; |
| |
| // Use the embedded compiler first |
| if (context_->getSessionDefaults()->callEmbeddedArkcmp() && |
| canUseEmbeddedArkcmp && |
| context_->isEmbeddedArkcmpInitialized() && |
| CmpCommon::context() && (CmpCommon::context()->getRecursionLevel() == 0)) |
| //!aqRetry && cliGlobals_->isEmbeddedArkcmpInitialized()) |
| { |
| Int32 compStatus; |
| ComDiagsArea *da = NULL; |
| |
| // clean up diags area of regular arkcmp, it could contain |
| // old errors from last use |
| if (cliGlobals_->getArkcmp() && |
| cliGlobals_->getArkcmp()->getDiags()) |
| cliGlobals_->getArkcmp()->getDiags()->clear(); |
| |
| compStatus = CmpCommon::context()->compileDirect( |
| (char *)data, dataLen, |
| // use arkcmp heap to store the plan |
| // check why indexIntoCompilerArray is used here? |
| cliGlobals_->getArkcmp(indexIntoCompilerArray)->getHeap(), |
| charset, op, |
| fetched_gen_code, fetched_gen_code_len, |
| context_->getSqlParserFlags(), |
| NULL, 0, da); |
| if (da != NULL) |
| { |
| diagsArea.mergeAfter(*da); |
| da->decrRefCount(); |
| } |
| |
| if (compStatus == ExSqlComp::SUCCESS) |
| { |
| // break from the while (retry) loop |
| break; |
| } |
| else |
| { |
| diagsArea << DgSqlCode(- CLI_STMT_NOT_PREPARED); |
| if (diagsArea.getRollbackTransaction()) |
| rollbackTransaction(diagsArea); |
| return prepareReturn (ERROR); |
| } |
| } |
| |
| ExSqlComp::ReturnStatus sendStatus = |
| cliGlobals_->getArkcmp(indexIntoCompilerArray)->sendRequest( |
| op, data, dataLen, |
| waited, |
| 0, charset, TRUE); |
| |
| if ( sendStatus == ExSqlComp::ERROR || |
| sendStatus == ExSqlComp::WARNING ) |
| { |
| diagsTakeOver(diagsArea, cliGlobals_,indexIntoCompilerArray); |
| if (sendStatus == ExSqlComp::ERROR) |
| { |
| setState(INITIAL_); |
| #ifndef _DEBUG |
| // For release, if the mxcmp could not reply |
| // to the request, use error -2005, to let the |
| // user know that a dialout has been generated. |
| diagsArea << DgSqlCode(arkcmpErrorNoDiags); |
| diagsArea << DgString0(source); |
| #endif |
| diagsArea << DgSqlCode(- CLI_STMT_NOT_PREPARED); |
| if (diagsArea.getRollbackTransaction()) |
| rollbackTransaction(diagsArea); |
| return prepareReturn (ERROR); |
| } |
| else //if (sendStatus == ExSqlComp::WARNING) |
| retcode = WARNING; |
| } |
| } // if new operation |
| |
| // check for completion of request. |
| status = cliGlobals_->getArkcmp(indexIntoCompilerArray)->status(); |
| |
| if ( status != ExSqlComp::FINISHED ) |
| { |
| // waited request should complete. Return error. |
| diagsTakeOver(diagsArea, cliGlobals_,indexIntoCompilerArray); |
| diagsArea << DgSqlCode(- CLI_IO_REQUESTS_PENDING) |
| << DgString0("SQLTEXT_COMPILE"); |
| return prepareReturn (ERROR); |
| } |
| |
| // initialize pointer to returned data(from reply) in private state. |
| ExSqlComp::ReturnStatus replyStatus = |
| cliGlobals_->getArkcmp(indexIntoCompilerArray)->getReply( |
| fetched_gen_code, |
| fetched_gen_code_len); |
| |
| retry = FALSE; |
| |
| if (replyStatus != ExSqlComp::SUCCESS) |
| { |
| diagsTakeOver (diagsArea, cliGlobals_, indexIntoCompilerArray); |
| |
| if (replyStatus == ExSqlComp::ERROR) |
| { |
| |
| if (retry == FALSE) |
| { |
| // An error other than 25304, or failure to start downrev compiler |
| setState(INITIAL_); |
| |
| if (ABS(diagsArea.mainSQLCODE()) != 4074 |
| || |
| diagsArea.getNumber() != 1) |
| diagsArea << DgSqlCode(- CLI_STMT_NOT_PREPARED); |
| return prepareReturn (ERROR); |
| } |
| } |
| else |
| { |
| retcode = WARNING; |
| } |
| } |
| |
| } // while retry |
| context_->killIdleMxcmp(); |
| assignRootTdb((ex_root_tdb *)fetched_gen_code); |
| root_tdb_size = (Lng32) fetched_gen_code_len; |
| } |
| |
| if (root_tdb) |
| { |
| diagsArea.setCost(root_tdb->getCost()); |
| } |
| if (unpackTdbs) |
| retcode = (short)unpackAndInit(diagsArea, indexIntoCompilerArray); |
| |
| return (RETCODE)retcode; |
| } // Statement::prepare |
| |
| Lng32 Statement::unpackAndInit(ComDiagsArea &diagsArea, |
| short indexIntoCompilerArray) |
| { |
| Lng32 retcode = 0; |
| |
| // Do not unpack the root tdb if the statement is from showplan. |
| // CmpDescribePlan will unpack it. |
| ComTdbRoot *thisTdb = root_tdb; |
| |
| if (root_tdb && !thisTdb->isFromShowplan()) |
| { |
| // Here, we have just freshly (re)prepared the plan using the latest |
| // version of the compiler. Unpacking should be uneventful (i.e. no |
| // object version migration should happen). Therefore, it should not |
| // be necessary to reallocate space. |
| // |
| ComTdb dummyTdb; |
| ex_root_tdb *newRoot = (ex_root_tdb *) |
| root_tdb->driveUnpack((void *) root_tdb, &dummyTdb, unpackSpace_); |
| |
| if (newRoot == NULL) |
| { |
| // ERROR during unpacking. |
| if ((indexIntoCompilerArray >= 0) && |
| (cliGlobals_->getArkcmp(indexIntoCompilerArray)->getDiags())) |
| diagsArea.mergeAfter(*cliGlobals_->getArkcmp()->getDiags()); |
| diagsArea << DgSqlCode(- CLI_STMT_NOT_PREPARED); |
| return prepareReturn (ERROR); |
| } |
| |
| assignRootTdb(newRoot); |
| } |
| |
| |
| if ( (versionToUse_ != COM_VERS_COMPILER_VERSION) && // Used a downrev compiler |
| (returnRecompWarn()) && // Will return recomp warnings |
| (root_tdb->getQueryType() >= ComTdbRoot::SQL_SELECT_UNIQUE) && // Query is DML |
| (root_tdb->getQueryType() <= ComTdbRoot::SQL_DELETE_NON_UNIQUE) |
| ) |
| { |
| // We compiled a DML query with a downrev compiler - save information so that we can |
| // issue warning 25304 if required. We do this rather than issue the warning directly, |
| // because we want warning 8576: Query was recompiled, to precede warning 25304 if applicable. |
| mxcmpErrorCode_ = VERSION_COMPILER_USED_TO_COMPILE_QUERY; |
| mxcmpStartedVersion_ = (COM_VERSION) versionToUse_; |
| } |
| |
| // Reset state in this statement, then loop through all clones and |
| // make corresponding state changes in each. |
| if (root_tdb) |
| { |
| setComputeBulkMoveInfo(TRUE); |
| } |
| setFixupState(0); |
| setState(CLOSE_); |
| setFirstResolveDone(FALSE); |
| lnil_ = NULL; |
| |
| clonedStatements->position(); |
| Statement * clone = (Statement*)clonedStatements->getNext(); |
| for (; clone != NULL; clone = (Statement*)clonedStatements->getNext()) |
| { |
| if (clone->root_tdb) |
| { |
| clone->setComputeBulkMoveInfo(TRUE); |
| } |
| clone->setFixupState(0); |
| clone->setState(CLOSE_); |
| clone->setFirstResolveDone(FALSE); |
| clone->lnil_ = NULL; |
| } |
| |
| if (indexIntoCompilerArray >= 0) |
| diagsTakeOver(diagsArea, cliGlobals_,indexIntoCompilerArray); |
| |
| if (root_tdb) |
| diagsArea.setCost(root_tdb->getCost()); |
| StatsGlobals *statsGlobals = cliGlobals_->getStatsGlobals(); |
| Lng32 fragOffset; |
| Lng32 fragLen; |
| Lng32 topNodeOffset; |
| SessionDefaults *sessionDefaults = |
| context_->getSessionDefaults(); |
| if (statsGlobals != NULL && stmtStats_ != NULL && root_tdb != NULL |
| && getUniqueStmtId() != NULL) |
| { |
| ex_root_tdb *rootTdb = getRootTdb(); |
| //root_tdb is not unpacked for SHOWPLAN and |
| // explain fragment can't be obtained for such prepared queries |
| if (!rootTdb->isPacked() && rootTdb->explainInRms() && |
| rootTdb->getFragDir()->getExplainFragDirEntry |
| (fragOffset, fragLen, topNodeOffset) == 0) |
| { |
| int error = statsGlobals->getStatsSemaphore(cliGlobals_->getSemId(), |
| cliGlobals_->myPin()); |
| stmtStats_->setExplainFrag((void *)(((char *)root_tdb)+fragOffset), fragLen, topNodeOffset); |
| statsGlobals->releaseStatsSemaphore(cliGlobals_->getSemId(),cliGlobals_->myPin()); |
| } |
| } |
| return prepareReturn ((RETCODE)retcode); |
| } |
| |
| RETCODE Statement::closeTables(ComDiagsArea &diagsArea) |
| { |
| // cancel the down request to my child |
| ComDiagsArea *diagsPtr = NULL; |
| Int32 retcode = root_tcb->cancel(statementGlobals_,diagsPtr); |
| StmtDebug1(" root_tcb->cancel() returned %s", |
| RetcodeToString((RETCODE) retcode)); |
| if (diagsPtr) |
| { |
| diagsArea.mergeAfter(*diagsPtr); |
| diagsPtr->decrRefCount(); |
| diagsPtr = NULL; |
| } |
| |
| if (retcode) |
| return ERROR; |
| |
| retcode = root_tcb->closeTables(statementGlobals_, |
| statementGlobals_->getRtFragTable()); |
| if (retcode) |
| return ERROR; |
| |
| return SUCCESS; |
| } |
| |
| RETCODE Statement::reOpenTables(ComDiagsArea &diagsArea) |
| { |
| closeTables(diagsArea); |
| |
| Int32 retcode = |
| root_tcb->reOpenTables( |
| statementGlobals_, |
| statementGlobals_->getRtFragTable()); |
| if (retcode) |
| return ERROR; |
| |
| return SUCCESS; |
| } |
| |
| ///////////////////////////////////////////////////////////////////// |
| // Searches for the given cursorName in the statement list. If found, |
| // and the cursor is an updatable cursor, returns pointer to that |
| // statement. Else, returns NULL. |
| ///////////////////////////////////////////////////////////////////// |
| Statement * Statement::getCurrentOfCursorStatement(char * cursorName) |
| { |
| ComDiagsArea &diags = context_->diags(); |
| |
| if (!cursorName) |
| return NULL; |
| |
| char nameBuf[ComMAX_1_PART_EXTERNAL_UTF8_NAME_LEN_IN_BYTES + 1 + 16]; // a null terminator + a few extra bytes |
| char *pName = nameBuf; |
| Lng32 len = str_len(cursorName); |
| |
| if (len > sizeof(nameBuf) - 1) |
| pName = (char *)heap_.allocateMemory(len + 1); |
| |
| str_cpy_all(pName, cursorName, len); |
| pName[len] = '\0'; |
| str_strip_blanks(pName,len); |
| // Convert to ansi id format |
| if (str_to_ansi_id(cursorName,pName,len)) |
| { |
| diags << DgSqlCode(-CLI_INVALID_SQL_ID) |
| << DgString0(pName); |
| return NULL; |
| } |
| HashQueue * cursorList = |
| context_->getCursorList(); |
| |
| Statement *pStmt = NULL; |
| Statement * stmt; |
| |
| cursorList->position(pName, len); |
| while (stmt = (Statement *)cursorList->getNext()) |
| { |
| //char * stmtCursorName = stmt->getCursorName(); |
| SQLSTMT_ID* stmtCursorName = stmt->getCursorName(); |
| |
| // Need to use length in comparison when the |
| // new length argument is added for "cursorName". |
| if (stmtCursorName && |
| (strcmp(pName, stmtCursorName->identifier) == 0)) |
| { |
| if (stmt->getRootTdb() && (stmt->getRootTdb()->updatableSelect() )) |
| { |
| pStmt = stmt; |
| break; |
| } |
| else |
| break; |
| |
| } |
| } |
| |
| if (pName != nameBuf) |
| heap_.deallocateMemory(pName); |
| return pStmt; |
| |
| } |
| |
| RETCODE Statement::doHiveTableSimCheck(TrafSimilarityTableInfo *si, |
| NABoolean &simCheckFailed, |
| ComDiagsArea &diagsArea) |
| { |
| simCheckFailed = FALSE; |
| |
| if ((si->hdfsRootDir() == NULL) || (si->modTS() == -1)) |
| return SUCCESS; |
| |
| char *tmpBuf = new (&heap_) char[ComMAX_3_PART_EXTERNAL_UTF8_NAME_LEN_IN_BYTES+6]; |
| Lng32 numParts = 0; |
| char *parts[4]; |
| Int64 redefTime; |
| |
| LateNameInfo::extractParts(si->tableName(), tmpBuf, numParts, parts, FALSE); |
| switch (numParts) { |
| case 1: |
| parts[2] = parts[0]; |
| parts[1] = (char *)"default"; |
| parts[0] = (char *)"HIVE"; |
| break; |
| case 2: |
| parts[2] = parts[1]; |
| parts[1] = parts[0]; |
| parts[0] = (char *)"HIVE"; |
| break; |
| case 3: |
| break; |
| default: |
| diagsArea << DgSqlCode(-24114); |
| return ERROR; |
| } |
| if (stricmp(parts[1], "HIVE") == 0) |
| parts[1] = (char *)"default"; |
| HVC_RetCode hvcRetcode = HiveClient_JNI::getRedefTime(parts[1], parts[2], redefTime); |
| if (hvcRetcode == HVC_OK) { |
| if (redefTime > si->modTS()) { |
| simCheckFailed = TRUE; |
| char errStr[strlen(si->tableName()) + 100 + strlen(si->hdfsRootDir())]; |
| snprintf(errStr,sizeof(errStr), |
| "compiledModTS = %ld, failedModTS = %ld, failedLoc = %s", |
| si->modTS(), redefTime, |
| si->hdfsRootDir()); |
| diagsArea << DgSqlCode(-EXE_HIVE_DATA_MOD_CHECK_ERROR) |
| << DgString0(errStr); |
| NADELETEBASIC(tmpBuf, &heap_); |
| return ERROR; |
| } |
| } else if (hvcRetcode == HVC_DONE) { |
| char errBuf[strlen(si->tableName()) + 100 + strlen(si->hdfsRootDir())]; |
| snprintf(errBuf,sizeof(errBuf), "%s (fileLoc: %s)", si->tableName(), si->hdfsRootDir()); |
| diagsArea << DgSqlCode(-EXE_TABLE_NOT_FOUND) |
| << DgString0(errBuf); |
| NADELETEBASIC(tmpBuf, &heap_); |
| return ERROR; |
| } else { |
| diagsArea << DgSqlCode(-1192) |
| << DgString0("HiveClient_JNI::getRedefTime") |
| << DgString1("") |
| << DgInt0(hvcRetcode) |
| << DgString2(getSqlJniErrorStr()); |
| NADELETEBASIC(tmpBuf, &heap_); |
| return ERROR; |
| } |
| NADELETEBASIC(tmpBuf, &heap_); |
| return SUCCESS; |
| } |
| |
| RETCODE Statement::doQuerySimilarityCheck(TrafQuerySimilarityInfo * qsi, |
| NABoolean &simCheckFailed, |
| ComDiagsArea &diagsArea |
| ) |
| { |
| RETCODE retcode; |
| |
| simCheckFailed = FALSE; |
| if ((! qsi) || |
| (qsi->disableSimCheck()) || |
| (! qsi->siList()) || |
| (qsi->siList()->numEntries() == 0)) |
| return SUCCESS; |
| |
| qsi->siList()->position(); |
| for (Lng32 i = 0; i < qsi->siList()->numEntries(); i++) |
| { |
| TrafSimilarityTableInfo *si = |
| (TrafSimilarityTableInfo *)qsi->siList()->getCurr(); |
| |
| simCheckFailed = FALSE; |
| if (si->isHive()) |
| { |
| retcode = doHiveTableSimCheck(si,simCheckFailed, diagsArea); |
| if (retcode == ERROR) |
| { |
| goto error_return; // diagsArea is set |
| } |
| } |
| } // for |
| |
| return SUCCESS; |
| |
| error_return: |
| return ERROR; |
| } |
| |
| RETCODE Statement::fixup(CliGlobals * cliGlobals, Descriptor * input_desc, |
| ComDiagsArea &diagsArea, NABoolean &doSimCheck, |
| NABoolean &partitionUnavailable, const NABoolean donePrepare) |
| { |
| Int32 retcode; |
| ExMasterStats *masterStats; |
| |
| if ((stmtStats_) && ((masterStats = stmtStats_->getMasterStats()) != NULL)) |
| masterStats->setStmtState(STMT_FIXUP_); |
| |
| // Initialize this method's output parameters |
| doSimCheck = FALSE; |
| partitionUnavailable = FALSE; |
| |
| if (fixupState() != 0) |
| return ERROR; |
| |
| /* fixup the generated code */ |
| statementGlobals_->setStartAddr((void *)root_tdb); |
| statementGlobals_->setCliGlobals(cliGlobals_); |
| |
| // Keep timeout data locally for this statement |
| // (The "root build" builds the messages to the ESPs by copying the |
| // relevant timeout data from the master globals.) |
| statementGlobals_->setLocalTimeoutData(root_tdb); |
| |
| // if this is part of an auto query retry and esp need to be cleaned up, |
| // set that indication in root tcb. |
| if ((context_->aqrInfo()) && |
| (context_->aqrInfo()->espCleanup())) |
| { |
| // set flag for verify ESPs - make sure esp in cache is alive before |
| // reuse it. |
| statementGlobals_->setVerifyESP(); |
| } |
| |
| root_tcb = (ex_root_tcb *)(root_tdb->build(cliGlobals, statementGlobals_)); |
| |
| if ((context_->aqrInfo()) && |
| (context_->aqrInfo()->espCleanup())) |
| { |
| // reset flag for verify ESPs |
| statementGlobals_->resetVerifyESP(); |
| } |
| |
| statementGlobals_->takeGlobalDiagsArea(diagsArea); |
| |
| if ((diagsArea.mainSQLCODE() < 0) || (!root_tcb)) |
| { |
| if ((diagsArea.contains(-EXE_TIMESTAMP_MISMATCH) && |
| (NOT tsMismatched())) |
| // if table not found, primary partition may have moved. |
| // so, try a similarity check to verify it. |
| || diagsArea.contains(-EXE_TABLE_NOT_FOUND)) |
| { |
| setTsMismatched(TRUE); |
| doSimCheck = TRUE; |
| return SUCCESS; |
| } |
| |
| if (diagsArea.contains(-EXE_PARTITION_UNAVAILABLE)) |
| // if partition availability error, use the catalog manager |
| // to attempt to find another partition. |
| { |
| partitionUnavailable = TRUE; |
| return SUCCESS; |
| } |
| |
| setTsMismatched(FALSE); |
| rollbackTransaction(diagsArea); |
| return ERROR; |
| } |
| |
| if (inputData_) |
| { |
| root_tcb->setInputData(inputData_); |
| } |
| |
| // QStuff ^^ |
| if (isPubsubHoldable() || isAnsiHoldable()) |
| { |
| root_tcb->propagateHoldable(TRUE); |
| } |
| // QStuff __ |
| |
| retcode = root_tcb->fixup(); |
| |
| if (retcode) |
| { |
| statementGlobals_->takeGlobalDiagsArea(diagsArea); |
| |
| // do similarity check if timestamp mismatch is returned at |
| // fixup time. |
| if (((diagsArea.contains(-EXE_TIMESTAMP_MISMATCH)) && |
| (NOT tsMismatched())) |
| || (diagsArea.contains(-EXE_TABLE_NOT_FOUND))) |
| { |
| setTsMismatched(TRUE); |
| doSimCheck = TRUE; |
| return SUCCESS; |
| } |
| |
| if (diagsArea.contains(-EXE_PARTITION_UNAVAILABLE)) |
| // if partition availability error, use the catalog manager |
| // to attempt to find another partition. |
| { |
| partitionUnavailable = TRUE; |
| return SUCCESS; |
| } |
| |
| setTsMismatched(FALSE); |
| rollbackTransaction(diagsArea); |
| return ERROR; |
| } |
| |
| setTsMismatched(FALSE); |
| |
| // if this is an 'update where current of' query, and |
| // the cursor statement was specified as a literal, |
| // then find the cursor statement and hook it up here. |
| if (root_tdb->updateCurrentOfQuery() && |
| root_tdb->fetchedCursorName()) |
| { |
| currentOfCursorStatement_ = |
| getCurrentOfCursorStatement(root_tdb->fetchedCursorName()); |
| |
| if (currentOfCursorStatement_ == NULL) |
| { |
| diagsArea << DgSqlCode(- CLI_NON_UPDATABLE_SELECT_CURSOR) |
| << DgString0(root_tdb->fetchedCursorName()); |
| return ERROR; |
| } |
| } |
| |
| setFixupState(-1); |
| |
| return SUCCESS; |
| } |
| |
| static |
| NABoolean compareTransModes(ex_root_tdb *root_tdb, |
| ExTransaction *currTransaction, |
| ComDiagsArea *diags = NULL, |
| NABoolean isSysModuleStmt = FALSE) |
| { |
| TransMode |
| &tmCompile = *root_tdb->getTransMode(), |
| &tmRuntime = *currTransaction->getTransMode(); |
| |
| #ifdef _DEBUG |
| static NABoolean dbg = !!getenv("TRANSMODE_DEBUG"); |
| if (dbg) |
| cerr << "##tm: exe= " << tmRuntime.display() |
| << " cmp= " << tmCompile.display() |
| << " " << tmCompile.stmtLevelAccessOptions() |
| << " " << root_tdb->readonlyTransactionOK() |
| << endl; |
| #endif |
| |
| NABoolean recompileDueToRollbackMode = |
| ( |
| ((tmCompile.getRollbackMode() == TransMode::NO_ROLLBACK_IN_IUD_STATEMENT_) && |
| (!currTransaction->autoCommit())) |
| || |
| (((tmCompile.getRollbackMode() != TransMode::NO_ROLLBACK_IN_IUD_STATEMENT_) && |
| (tmCompile.getRollbackMode() != TransMode::NO_ROLLBACK_)) && |
| (tmRuntime.getRollbackMode() == TransMode::NO_ROLLBACK_)) |
| ) ; |
| |
| NABoolean recompileDueToMultiCommitMode = !(tmCompile.multiCommitCompatible(tmRuntime)); |
| |
| if (tmCompile.accessModeCompatible(tmRuntime) || |
| root_tdb->readonlyTransactionOK()) |
| { |
| if (tmCompile.isolationLevelCompatible(tmRuntime) || |
| tmCompile.stmtLevelAccessOptions()) |
| { |
| if( !recompileDueToRollbackMode && !recompileDueToMultiCommitMode) |
| return FALSE; |
| } |
| } |
| |
| // The run-time transaction mode ($0~int0) differs the compile-time ($1~int1). |
| if (diags && !isSysModuleStmt) |
| *diags << DgSqlCode(- CLI_TRANS_MODE_MISMATCH) |
| << DgInt0(tmRuntime.display()) |
| << DgInt1(tmCompile.display()) |
| ; |
| return TRUE; |
| } |
| |
| inline static |
| void recompileReasonIsTransMode(ex_root_tdb *root_tdb, |
| ExTransaction *currTransaction, |
| Lng32 recompileReason[]) |
| { |
| recompileReason[0] = CLI_TRANS_MODE_MISMATCH; |
| recompileReason[1] = currTransaction->getTransMode()->display(); |
| recompileReason[2] = root_tdb->getTransMode()->display(); |
| } |
| |
| RETCODE Statement::error(ComDiagsArea &diagsArea) |
| { |
| // Reset the autocommit flag to the user's view. |
| resetAutoCommit(); |
| |
| // abort the transaction, if auto commit is on or |
| // rollbackTransaction flag is set in the diags area or |
| // this is a query that would have dirtied the disk(like, |
| // insert/update/delete). |
| rollbackTransaction(diagsArea); |
| |
| // if no other diags, as a last resort emit this rather |
| // cryptic message |
| if (diagsArea.getNumber(DgSqlCode::ERROR_) == 0) |
| diagsArea << DgSqlCode(- CLI_TCB_EXECUTE_ERROR); |
| |
| dealloc(); |
| space_.freeBlocks(); |
| statementGlobals_->reAllocate(1); |
| |
| setState(CLOSE_); |
| |
| setFixupState(0); |
| |
| return ERROR; |
| } |
| |
| RETCODE Statement::execute(CliGlobals * cliGlobals, Descriptor * input_desc, |
| ComDiagsArea &diagsArea, ExecState execute_state, |
| NABoolean fixupOnly, ULng32 cliflags) |
| { |
| StmtDebug2("[BEGIN execute] %p, stmt_state %s", this, stmtState(getState())); |
| |
| RETCODE retcode = SUCCESS; |
| NABoolean schemaFileLabelTSChecked = 0; |
| |
| NABoolean recompWarn; |
| NABoolean partitionUnavailable = FALSE; |
| NABoolean partitionAvailabilityChecked = FALSE; |
| |
| // This boolean indicates whether ANSI to Guardian name mappings |
| // have failed following a "partition not available" error. The |
| // first time we encounter such a failure we attempt an automatic |
| // recompile and set this variable to TRUE. If the same failure is |
| // encountered again after the recompile then we return an error to |
| // the user. |
| NABoolean partitionNameLookupsFailed = FALSE; |
| |
| // This boolean indicates whether a prepare has been done as part |
| // of this execution. If that is the case, visibility checks can |
| // be skipped because the prepare will have done that already. |
| NABoolean donePrepare = FALSE; |
| |
| Lng32 recompileReason[3]; |
| recompileReason[0] = recompileReason[1] = recompileReason[2] = 0; |
| Int64 reCompileTime = (Int64)0; |
| NABoolean reExecute=FALSE; |
| ExMasterStats *masterStats = NULL; |
| |
| if (state_ != FIXUP_DONE_) |
| state_ = execute_state; |
| |
| // To ensure correct handling of the implicit transactions (and |
| // autocommit clearing and restoring) which happen when the CLI |
| // makes recursive calls via CatMapAnsiNameToGuardianName, |
| // CatMapGetCatalogVisibility, RTMD fetches, etc, please be sure to |
| // call Statement::commitImplicitTransAndResetTmodes before |
| // leaving this function. So it is best not to code a "return" from |
| // inside this while loop, but instead, set the retcode local |
| // variable, and set the readyToReturnVariable to TRUE, then break |
| // from the switch statement. |
| |
| NABoolean readyToReturn = FALSE; |
| if (stmtStats_ != NULL) |
| masterStats = stmtStats_->getMasterStats(); |
| while (readyToReturn == FALSE) |
| { |
| #ifdef _DEBUG |
| if (getenv("SHOW_STATE")) |
| { |
| char buf[40]; |
| |
| switch (state_) |
| { |
| case INITIAL_STATE_: strcpy(buf, "INITIAL_STATE_"); break; |
| case DO_SIM_CHECK_: strcpy(buf, "DO_SIM_CHECK_"); break; |
| case CHECK_DYNAMIC_SETTINGS_: strcpy(buf, "CHECK_DYNAMIC_SETTINGS_"); break; |
| case FIXUP_: strcpy(buf, "FIXUP_"); break; |
| case FIXUP_DONE_: strcpy(buf, "FIXUP_DONE_"); break; |
| case EXECUTE_: strcpy(buf, "EXECUTE_"); break; |
| case ERROR_: strcpy(buf, "ERROR_"); break; |
| case ERROR_RETURN_: strcpy(buf, "ERROR_RETURN_"); break; |
| default: strcpy(buf, "Unknown state!"); break; |
| } |
| cout << "State " << buf << endl; |
| } |
| #endif |
| |
| switch (state_) |
| { |
| case INITIAL_STATE_: |
| { |
| // Reclaim Statements if it is available only when the |
| // parent statements are executed |
| if (context_->getNumOfCliCalls() == 1) |
| { |
| // Reclaim Statements if it is available |
| context_->reclaimStatements(); |
| } |
| if (masterStats != NULL) |
| { |
| Int64 jts = NA_JulianTimestamp(); |
| if (NOT masterStats->isPrepAndExec() && (!fixupOnly)) |
| { |
| masterStats->setElapsedStartTime(jts); |
| } |
| if (! stmtStats_->aqrInProgress()) |
| aqrInitialExeStartTime_ = -1; |
| if (! fixupOnly) |
| masterStats->setExeStartTime(aqrInitialExeStartTime_ == -1 ? |
| jts : aqrInitialExeStartTime_); |
| masterStats->initBeforeExecute(jts); |
| if (! stmtStats_->aqrInProgress()) |
| masterStats->resetAqrInfo(); |
| } |
| |
| if (stmt_state != CLOSE_) |
| { |
| // Report an error because we are trying to execute a |
| // statement which is already in an open state, or one |
| // that was never prepared. One exception is a stored |
| // procedure result set in the INITIAL_ state. It can |
| // be described and executed without the application |
| // first having done a prepare. |
| if (!(parentCall_ && stmt_state == INITIAL_)) |
| { |
| //ADebug(); |
| diagsArea << DgSqlCode(- CLI_STMT_NOT_CLOSE); |
| state_ = ERROR_; |
| statementGlobals_->setCancelState(CLI_CANCEL_TCB_READY); |
| break; |
| } |
| } |
| |
| if (aqrReprepareNeeded()) |
| { |
| diagsArea << DgSqlCode(-EXE_USER_PREPARE_NEEDED); |
| state_ = ERROR_; |
| break; |
| } |
| |
| if (!root_tdb && |
| !allocated() && |
| (statement_id->name_mode == cursor_name) && |
| (statement_id->identifier) && |
| (!isEqualByName((SQLSTMT_ID *)statement_id, cursor_name_)) |
| ) |
| { |
| SQLSTMT_ID tmpStmtId = *(SQLSTMT_ID *)statement_id; |
| tmpStmtId.name_mode = stmt_name; |
| Statement * bindToStmt = |
| context_->getStatement(&tmpStmtId); |
| if (!bindToStmt) |
| { |
| diagsArea << DgSqlCode(-CLI_STMT_NOT_EXISTS); |
| state_ = ERROR_; |
| break; |
| } |
| bindTo(bindToStmt); |
| } |
| |
| // Two cases to consider for CALL statements |
| // a) This is a CALL statement. We need to reset the state |
| // of all child result sets before doing this execute. |
| // The state should have already been reset when the |
| // CALL was closed but we do it again to be safe. |
| // b) This is a stored procedure result set. We may need to |
| // to trigger an internal prepare before doing this |
| // execute. We also need to prevent a result set from |
| // being opened multiple times following a single |
| // execution of the CALL. |
| ExRsInfo *rsInfo = getResultSetInfo(); |
| if (rsInfo) |
| { |
| // Case a) |
| rsInfo->reset(); |
| } |
| if (parentCall_) |
| { |
| // Case b) |
| ExRsInfo *parentRsInfo = parentCall_->getResultSetInfo(); |
| ex_assert(parentRsInfo, "No parent RS info available"); |
| |
| ULng32 myIndex = parentRsInfo->getIndex(this); |
| NABoolean openWasAttempted = |
| parentRsInfo->openAttempted(myIndex); |
| |
| if (!openWasAttempted) |
| { |
| parentRsInfo->setOpenAttempted(myIndex); |
| } |
| else |
| { |
| diagsArea << DgSqlCode(-EXE_UDR_RS_REOPEN_NOT_ALLOWED) |
| << DgInt0((Lng32) myIndex); |
| state_ = ERROR_; |
| break; |
| } |
| |
| RETCODE proxyRetcode = rsProxyPrepare(*parentRsInfo, myIndex, |
| diagsArea); |
| if (proxyRetcode == ERROR) |
| { |
| state_ = ERROR_; |
| break; |
| } |
| } |
| |
| // Recompile if this stmt did not get compiled at |
| // compilation time, |
| // or if the current transaction mode is different than the |
| // mode specified at compile time. |
| if (! root_tdb) |
| { |
| // A special case: |
| // if DISPLAY query and no root_tdb, return. In this |
| // case user does not want to execute the query. |
| if (isDISPLAY()) |
| { |
| // change state so fetch can 'succeed' with an EOF. |
| stmt_state = EOF_; |
| retcode = SUCCESS; |
| readyToReturn = TRUE; |
| break; |
| } |
| |
| // AQR has been enabled. |
| if ((allocated()) && |
| (context_->getSessionDefaults()->getAqrType() != 0)) |
| { |
| // return error. AQR will handle recompile and retry. |
| diagsArea << DgSqlCode(-8583); |
| state_ = ERROR_; |
| break; |
| } |
| |
| state_ = ERROR_; |
| break; |
| } |
| else if ((root_tdb->transactionReqd() || root_tdb->isLRUOperation()) |
| && |
| compareTransModes(root_tdb, context_->getTransaction(), |
| (root_tdb->aqrEnabled() ? &diagsArea : NULL)) |
| && |
| !systemModuleStmt() ) |
| { |
| if (root_tdb->aqrEnabled()) |
| { |
| // return error. AQR will handle recompile and retry. |
| state_ = ERROR_; |
| } |
| else |
| { |
| state_ = ERROR_; |
| } |
| break; |
| } |
| |
| if (root_tdb->inMemoryObjectDefn()) |
| { |
| // trying to executed a query which refers to an inMemory |
| // object definition. |
| // Return error. |
| diagsArea << DgSqlCode(-CLI_CANNOT_EXECUTE_IN_MEM_DEFN); |
| state_ = ERROR_; |
| break; |
| } |
| |
| // if statistics were previously returned for this statement |
| // (could happen for multiple executions of the same prepared |
| // or an embedded static statement), then re-initizalize the stat |
| // area. |
| ExStatisticsArea *statsArea = getStatsArea(); |
| if (statsArea != NULL) |
| { |
| StatsGlobals *statsGlobals = cliGlobals->getStatsGlobals(); |
| if (statsGlobals != NULL) |
| { |
| int error = statsGlobals->getStatsSemaphore(cliGlobals->getSemId(), |
| cliGlobals->myPin()); |
| statsArea->initEntries(); |
| statsArea->restoreDop(); |
| statsGlobals->releaseStatsSemaphore(cliGlobals->getSemId(),cliGlobals->myPin()); |
| } |
| else |
| { |
| statsArea->initEntries(); |
| statsArea->restoreDop(); |
| } |
| } |
| if (stmtStats_ != NULL) |
| stmtStats_->setAqrInProgress(FALSE); |
| // LRU cannot run in a user transaction |
| if (root_tdb && root_tdb->isLRUOperation() && context_->getTransaction()->xnInProgress()) |
| { |
| diagsArea << DgSqlCode(-EXE_CANT_BEGIN_USER_TRANS_WITH_LRU); |
| retcode = ERROR; |
| state_ = ERROR_RETURN_; |
| break; |
| } |
| |
| // Start a transaction, if needed and one not already running. |
| // if(!context_->getSuppressAutoXactStartFlag()) |
| if ((NOT fixupOnly) || |
| (cliflags & PREPARE_STANDALONE_QUERY)) |
| { |
| if (beginTransaction(diagsArea)) |
| { |
| retcode = ERROR; |
| state_ = ERROR_RETURN_; |
| break; |
| } |
| } |
| |
| setTsMismatched(FALSE); |
| state_ = DO_SIM_CHECK_; |
| } |
| break; |
| |
| case DO_SIM_CHECK_: |
| { |
| if ((! root_tdb) || (! root_tdb->querySimilarityInfo())) |
| { |
| state_ = CHECK_DYNAMIC_SETTINGS_; |
| break; |
| } |
| |
| NABoolean simCheckFailed = FALSE; |
| retcode = |
| doQuerySimilarityCheck(root_tdb->querySimilarityInfo(), |
| simCheckFailed, diagsArea); |
| if (retcode == ERROR) |
| { |
| state_ = ERROR_; |
| break; |
| } |
| |
| state_ = CHECK_DYNAMIC_SETTINGS_; |
| } |
| break; |
| |
| case CHECK_DYNAMIC_SETTINGS_: |
| { |
| if (fixupState()) |
| { |
| // If this fixed up statement was affected by SET TIMEOUT |
| // or by a change in UDR runtime options then we want to |
| // make sure it gets fixed up again |
| if (statementGlobals_->timeoutSettingChanged() || |
| statementGlobals_->udrRuntimeOptionsChanged()) |
| { |
| setFixupState(0); |
| } |
| } |
| |
| if (!fixupState()) |
| state_ = FIXUP_; |
| else |
| state_ = EXECUTE_; |
| |
| } |
| break; |
| |
| case FIXUP_: |
| { |
| // We want to ignore any errors that occur as part of dealloc |
| // when called from here. |
| // So, we mark the DiagsArea before making the call to dealloc(), |
| // and then rewind |
| // back to there afterwards. |
| Lng32 oldDiagsAreaMark = diagsArea.mark(); |
| Lng32 oldGlobalDiagsAreaMark = 0; |
| if (statementGlobals_->getDiagsArea()) |
| { |
| oldGlobalDiagsAreaMark = statementGlobals_->getDiagsArea()->mark(); |
| } |
| |
| if (dealloc()) |
| { |
| //Leave the diagsArea as it is in case of error during the dealloc(). |
| state_ = ERROR_; |
| break; |
| } |
| |
| // Rewind to ignore all errors that occurred during a successful dealloc() |
| if (statementGlobals_->getDiagsArea()) |
| { |
| statementGlobals_->getDiagsArea()->rewind(oldGlobalDiagsAreaMark, TRUE); |
| } |
| diagsArea.rewind(oldDiagsAreaMark, TRUE); |
| space_.freeBlocks(); |
| |
| statementGlobals_->reAllocate(1); |
| |
| setState(CLOSE_); |
| |
| setFixupState(0); |
| |
| StmtDebug1("[BEGIN fixup] %p", this); |
| |
| NABoolean doSimCheck = FALSE; |
| retcode = |
| fixup(cliGlobals, input_desc, diagsArea, doSimCheck, |
| partitionUnavailable, donePrepare); |
| |
| |
| StmtDebug2("[END fixup] %p, result is %s", this, |
| RetcodeToString(retcode)); |
| |
| if (((fixupOnly) || |
| (root_tdb->aqrEnabled())) && |
| ((retcode == ERROR) || |
| (diagsArea.mainSQLCODE() < 0) || |
| (doSimCheck))) |
| { |
| state_ = ERROR_; |
| break; |
| } |
| |
| if ((retcode == ERROR) || |
| (partitionUnavailable && partitionAvailabilityChecked)) |
| { |
| state_ = ERROR_; |
| break; |
| } |
| |
| if (doSimCheck || partitionUnavailable) |
| { |
| state_ = DO_SIM_CHECK_; |
| } |
| else |
| { |
| state_ = EXECUTE_; |
| |
| if (fixupOnly) |
| { |
| if (NOT(cliflags & PREPARE_STANDALONE_QUERY)) |
| { |
| // if an explicitly prepared query, commit any |
| // transaction that was started during fixup |
| // stage. We don't want to hold onto this Xn |
| // between this stmt's prepare and its execute. |
| // Later, when this query is executed, a |
| // transaction would be started. |
| |
| // Wait for UDR transactional replies before |
| // attempting to commit |
| NABoolean allRequests = FALSE; |
| completeUdrRequests(allRequests); |
| |
| // The following call will only reset autoCommit |
| // which is needed for the commitTransaction() |
| // to actually commit. |
| resetAutoCommit(); |
| |
| // now call commitTransaction since autoCommit has been |
| // reset |
| if (commitTransaction(diagsArea)) |
| { |
| state_ = ERROR_; |
| retcode = ERROR; |
| readyToReturn = TRUE; |
| break; |
| } |
| retcode = SUCCESS; |
| readyToReturn = TRUE; |
| state_ = FIXUP_DONE_; |
| |
| } |
| else |
| { |
| retcode = SUCCESS; |
| readyToReturn = TRUE; |
| state_ = INITIAL_STATE_; |
| } |
| |
| } // if (fixupOnly && (state_ != ERROR_)) |
| } // if (doSimCheck || partitionUnavailable) else ... |
| |
| // Transition to the ERROR_ state if errors were recorded |
| // in statement globals. Also set readyToReturn to FALSE |
| // so we don't return out of this method before processing |
| // the state transition. |
| if (state_ != ERROR_) |
| { |
| ComDiagsArea *stmtGlobDiags = statementGlobals_->getDiagsArea(); |
| if (stmtGlobDiags && stmtGlobDiags->mainSQLCODE() < 0) |
| { |
| statementGlobals_->takeGlobalDiagsArea(diagsArea); |
| state_ = ERROR_; |
| readyToReturn = FALSE; |
| } |
| } |
| |
| } // case FIXUP_ |
| break; |
| |
| case FIXUP_DONE_: |
| { |
| // Begin a transaction before going to EXECUTE_ state since |
| // the transaction may have been committed |
| if (beginTransaction(diagsArea)) |
| { |
| retcode = ERROR; |
| state_ = ERROR_RETURN_; |
| break; |
| } |
| if (masterStats != NULL) |
| { |
| Int64 jts = NA_JulianTimestamp(); |
| if (NOT masterStats->isPrepAndExec()) |
| { |
| masterStats->setElapsedStartTime(jts); |
| } |
| masterStats->setExeStartTime(aqrInitialExeStartTime_ == -1 |
| ? jts : aqrInitialExeStartTime_); |
| } |
| state_ = EXECUTE_; |
| } |
| break; |
| |
| case EXECUTE_: |
| { |
| if (masterStats != NULL) |
| { |
| masterStats-> |
| setFixupEndTime(NA_JulianTimestamp()); |
| if (!masterStats->getValidDDL()) |
| { |
| diagsArea << DgSqlCode(-CLI_DDL_REDEFINED); |
| state_ = ERROR_; |
| break; |
| } |
| if (!masterStats->getValidPrivs()) |
| { |
| diagsArea << DgSqlCode(-CLI_INVALID_QUERY_PRIVS); |
| state_ = ERROR_; |
| break; |
| } |
| } |
| // In case of master, the unused memory quota needs to be reset |
| // with every statement execution. |
| statementGlobals_->resetMemoryQuota(); |
| /* execute it */ |
| if( root_tdb ) |
| { |
| // check if we have triggers |
| if (root_tdb->getTriggersCount() > 0) |
| { |
| retcode = |
| getTriggersStatus(root_tdb->stoiStoiList(), diagsArea); |
| if (retcode == ERROR) |
| { |
| state_ = ERROR_; |
| break; |
| } |
| } |
| |
| // check for statements with uninitialized mvs |
| char * pMvName; |
| if( doesUninitializedMvExist( &pMvName, diagsArea ) ) |
| { |
| diagsArea << DgSqlCode(- CLI_MV_EXECUTE_UNINITIALIZED) |
| << DgTableName( (const char *)pMvName); |
| state_ = ERROR_; |
| break; |
| } |
| } |
| |
| // in case this is a holdable cursor propagate flag to master executor |
| // leaf nodes |
| if (isPubsubHoldable() && |
| !root_tdb->isEmbeddedUpdateOrDelete() && |
| !root_tdb->isStreamScan()) |
| { |
| // Holdable cursors are only supported for streaming cursors |
| // and destructive cursors because there are some strange |
| // side-effects (locks disappear after commit) that we don't |
| // want to inflict on non-Publich/Subscribe functionality at |
| // this time. There are no source-code changes needed to allow |
| // holdable cursors in these other cases though. |
| diagsArea << DgSqlCode(-CLI_CURSOR_CANNOT_BE_HOLDABLE); |
| state_ = ERROR_; |
| break; |
| } |
| // BertBert ^^ |
| |
| ComDiagsArea* diagsPtr = NULL; |
| |
| // if this is an 'update where current of' query, and |
| // the cursor statement name was specified via a hvar, |
| // then find the cursor statement and hook it up here. |
| // In this case, fetched statement has to be searched |
| // before each execute because the hvar value may |
| // change between calls to Exec. |
| // Also validate that the tablename specified in the upd/del |
| // stmt is the same as the one in the declare cursor stmt. |
| if (root_tdb->updateCurrentOfQuery()) |
| { |
| if (root_tdb->fetchedCursorName() == NULL) // spec via hvar |
| { |
| // find the hvar that contains the cursor name in the |
| // input desc list |
| Lng32 cursor_name = 0; |
| input_desc->getDescItem(root_tdb->fetchedCursorHvar(), |
| SQLDESC_VAR_PTR, &cursor_name, |
| 0, 0, 0, 0); |
| char * cursor_name_copy = NULL; |
| if (cursor_name == 0) |
| { |
| diagsArea << DgSqlCode(-CLI_TCB_EXECUTE_ERROR); |
| state_ = ERROR_; |
| break; |
| } |
| else |
| { |
| Lng32 string_length = 0; |
| input_desc->getDescItem(root_tdb->fetchedCursorHvar(), |
| SQLDESC_LENGTH, &string_length, |
| 0, 0, 0, 0); |
| cursor_name_copy = |
| Descriptor::getCharDataFromCharHostVar |
| (diagsArea, |
| heap_, |
| (char *)((long)cursor_name), |
| string_length, |
| "CURSOR NAME", |
| input_desc, |
| root_tdb->fetchedCursorHvar() - 1, // Pass a zero-based index. |
| REC_BYTE_V_ANSI); |
| } |
| |
| currentOfCursorStatement_ = |
| getCurrentOfCursorStatement((char *)cursor_name_copy); |
| if (cursor_name_copy) heap_.deallocateMemory(cursor_name_copy); |
| |
| if (currentOfCursorStatement_ == NULL) |
| { |
| diagsArea << DgSqlCode(- CLI_NON_UPDATABLE_SELECT_CURSOR) |
| << DgString0((char *)((long)cursor_name)); |
| state_ = ERROR_; |
| break; |
| } |
| } |
| |
| // make sure that the table name in the update/del stmt is the |
| // same as the tablename specified in the cursor. |
| ex_root_tdb *cursorTdb = currentOfCursorStatement_->getRootTdb(); |
| Int16 cursorTableNameLen = |
| str_len( cursorTdb->getLateNameInfoList()-> |
| getLateNameInfo(cursorTdb->baseTablenamePosition()). |
| lastUsedAnsiName()); |
| |
| Int16 updelTableNameLen = |
| str_len( root_tdb->getLateNameInfoList()-> |
| getLateNameInfo(root_tdb->baseTablenamePosition()). |
| lastUsedAnsiName()); |
| if ( (cursorTableNameLen != updelTableNameLen ) || |
| |
| (str_cmp(root_tdb->getLateNameInfoList()-> |
| getLateNameInfo(root_tdb->baseTablenamePosition()). |
| lastUsedAnsiName(), |
| cursorTdb->getLateNameInfoList()-> |
| getLateNameInfo(cursorTdb->baseTablenamePosition()). |
| lastUsedAnsiName(), |
| updelTableNameLen |
| ) != 0) |
| ) |
| { |
| diagsArea << DgSqlCode(- CLI_NON_CURSOR_UPDEL_TABLE) |
| << DgString0(root_tdb->fetchedCursorName()); |
| retcode = ERROR; |
| state_ = ERROR_RETURN_; |
| // readyToReturn = TRUE; |
| break; |
| } |
| } |
| |
| // Get the row of primary key values from the referenced cursor and |
| // move it to my root tcb and ensure the update columns are valid. |
| if (root_tdb->updateCurrentOfQuery()) |
| { |
| if (currentOfCursorStatement_ == NULL) |
| { |
| diagsArea << DgSqlCode(- CLI_NON_UPDATABLE_SELECT_CURSOR); |
| state_ = ERROR_; |
| break; |
| } |
| |
| if (currentOfCursorStatement_->getState() != FETCH_) |
| { |
| // Must have fetched a row to do an update...current of. |
| // Add a better error message here. TBD. |
| diagsArea << DgSqlCode(- EXE_CURSOR_NOT_FETCHED); |
| state_ = ERROR_; |
| break; |
| } |
| |
| if (currentOfCursorStatement_->isDeletedCursor()) |
| { |
| // Must have fetched a row to do an update...current of. |
| // Cannot update or delete a row that was previously |
| // deleted using this cursor. |
| diagsArea << DgSqlCode(- EXE_CURSOR_NOT_FETCHED); |
| state_ = ERROR_; |
| break; |
| } |
| |
| char * pkeyRow = |
| currentOfCursorStatement_->getRootTcb()->getPkeyRow(); |
| if (pkeyRow == NULL) |
| { |
| // Add a better error message here. TBD. |
| diagsArea << DgSqlCode(- CLI_TCB_EXECUTE_ERROR); |
| state_ = ERROR_; |
| break; |
| } |
| |
| root_tcb->inputPkeyRow(pkeyRow); |
| |
| if (!currentOfCursorStatement_->root_tdb->isUpdateCol(root_tdb)) |
| { |
| diagsArea << DgSqlCode(- CLI_INVALID_UPDATE_COLUMN); |
| state_ = ERROR_; |
| break; |
| } |
| } |
| |
| // NOT_ATOMIC_FAILURE_LIMIT was specified through the statement attribute |
| // override any value we get from the CQD |
| if ((getNotAtomicFailureLimit() != 0) && |
| (root_tdb->isNonFatalErrorTolerated())) { |
| root_tdb->setNotAtomicFailureLimit(getNotAtomicFailureLimit()); |
| } |
| |
| // nothing happened yet. |
| statementGlobals_->setGlobDiagsArea(0); |
| statementGlobals_->setRowsAffected(0); |
| diagsArea.setRowCount(0); |
| |
| // Now that the statement is completely name-resolved, |
| // catalog-visiblity-checked, fixed up, compiled, recompiled, |
| // etc, etc, see if we started an implicit transaction to |
| // perform any of these tasks and if so, commit it so that |
| // the statement can run without a trans (since if *we* |
| // started the trans then this must be a READ UNCOMMITTED |
| // statement. Also, reset the autocommit setting if we |
| // had to temporarily turn it off. |
| commitImplicitTransAndResetTmodes(); |
| |
| NABoolean parentIsCanceled = updateChildQid(); |
| if (parentIsCanceled) |
| { |
| diagsArea << DgSqlCode(-EXE_CANCELED); |
| state_ = ERROR_; |
| break; |
| } |
| SQL_QUERY_COST_INFO query_cost_info; |
| SQL_QUERY_COMPILER_STATS_INFO query_comp_stats_info; |
| query_cost_info.cpuTime = 0; |
| query_cost_info.ioTime = 0; |
| query_cost_info.msgTime = 0; |
| query_cost_info.idleTime = 0; |
| query_cost_info.totalTime = 0; |
| query_cost_info.cardinality = 0; |
| query_cost_info.estimatedTotalMem = 0; |
| query_cost_info.resourceUsage = 0; |
| query_cost_info.maxCpuUsage = 0; |
| if (getRootTdb()) |
| { |
| if (getRootTdb()->getQueryCostInfo()) |
| { |
| getRootTdb()->getQueryCostInfo()->translateToExternalFormat(&query_cost_info); |
| } |
| |
| } |
| else |
| query_cost_info.totalTime = getRootTdb()->getCost(); |
| |
| |
| if (getRootTdb()) |
| { |
| CompilerStatsInfo *cmpStatsInfo = |
| getRootTdb()->getCompilerStatsInfo(); |
| |
| if (cmpStatsInfo) |
| { |
| short xnNeeded = (transactionReqd() ? 1 : 0); |
| cmpStatsInfo->translateToExternalFormat(&query_comp_stats_info,xnNeeded); |
| // CompilationStatsData. |
| CompilationStatsData *cmpData = |
| getRootTdb()->getCompilationStatsData(); |
| |
| |
| SQL_COMPILATION_STATS_DATA *query_cmp_data = |
| &query_comp_stats_info.compilationStats; |
| |
| if( cmpData ) |
| { |
| Int64 cmpStartTime = -1; |
| Int64 cmpEndTime = NA_JulianTimestamp(); |
| if (masterStats != NULL) |
| cmpStartTime = masterStats->getCompStartTime(); |
| cmpData->translateToExternalFormat(query_cmp_data, |
| cmpStartTime, cmpEndTime); |
| setCompileEndTime(cmpEndTime); |
| } |
| } |
| |
| } |
| |
| // done deciding if this query needs to be monitored and |
| // registered with WMS. |
| // now execute it. |
| if (masterStats != NULL) |
| { |
| masterStats->setIsBlocking(); |
| masterStats->setStmtState(STMT_EXECUTE_); |
| } |
| |
| Int32 rc = root_tcb->execute(cliGlobals, statementGlobals_, |
| input_desc, diagsPtr, reExecute); |
| |
| if (masterStats != NULL) |
| masterStats->setNotBlocking(); |
| if (rc < 0) |
| retcode = ERROR; |
| // "diagsPtr" is modified by the foregoing call. |
| // If "diagsPtr" is NULL, there are no diags to merge |
| // into "diagsArea". Otherwise, "diagsPtr" is not NULL and does |
| // point to a diags area from which we: 1) avoid the SQL function |
| // 2) copy the ComCondition objects over. Then we decrement |
| // the reference count to indicate we're done with that |
| // ComDiagsArea. |
| |
| if (diagsPtr) |
| { |
| diagsArea.mergeAfter(*diagsPtr); |
| diagsPtr->decrRefCount(); |
| diagsPtr = NULL; |
| } |
| |
| if (retcode == ERROR) |
| { |
| root_tcb->cancel(statementGlobals_,diagsPtr); |
| state_ = ERROR_; |
| break; |
| } |
| else |
| { |
| if (retcode == 0 && diagsArea.mainSQLCODE() > 0) |
| // It's a warning. So return 1. |
| retcode = (RETCODE)1; |
| setState(OPEN_); |
| readyToReturn = TRUE; |
| break; |
| } |
| } |
| break; |
| |
| case RE_EXECUTE_: |
| { |
| // if input descriptor was passed in at execute time, recreate |
| // the input data |
| // Setting the boolean reExecute to true, will cause the |
| // inputData from the previous execution to be used since we |
| // have no input_desc anymore at this stage. |
| if (inputData_) |
| heap_.deallocateMemory (inputData_) ; |
| inputData_ = NULL; |
| char * inputData; |
| root_tcb->getInputData(inputData, inputDatalen_); |
| if (inputData) |
| { |
| inputData_ = (char*) heap_.allocateMemory (inputDatalen_) ; |
| str_cpy_all(inputData_, inputData, (Lng32) inputDatalen_); |
| } |
| |
| setFixupState(0); |
| reExecute=TRUE; |
| state_ = CHECK_DYNAMIC_SETTINGS_; |
| } |
| break; |
| |
| case ERROR_: |
| { |
| retcode = error(diagsArea); |
| state_ = ERROR_RETURN_; |
| } |
| break; |
| |
| case ERROR_RETURN_: |
| { |
| readyToReturn = TRUE; |
| } |
| break; |
| |
| default: |
| state_ = ERROR_; |
| break; |
| |
| } // switch |
| } // while return |
| |
| commitImplicitTransAndResetTmodes(); |
| |
| StmtDebug2("[END execute] %p, result is %s", this, |
| RetcodeToString(retcode)); |
| return retcode; |
| |
| } // Statement::execute diags |
| |
| RETCODE Statement::fetch(CliGlobals * cliGlobals, Descriptor * output_desc, |
| ComDiagsArea &diagsArea, |
| NABoolean newOperation) |
| { |
| StmtDebug2("[BEGIN fetch] %p, stmt state %s", this, stmtState(getState())); |
| |
| Int32 timeout = 0; |
| Int32 retcode = SUCCESS; |
| if (stmt_state == CLOSE_) |
| { |
| // trying to fetch from a statement which is in CLOSE state |
| diagsArea << DgSqlCode(- CLI_STMT_CLOSE); |
| StmtDebug3("[END fetch] %p, result is %s, stmt state %s", this, |
| RetcodeToString(ERROR), stmtState(getState())); |
| return ERROR; |
| } |
| |
| if (stmt_state == EOF_) // already returned EOF once. |
| { |
| if (output_desc && output_desc->rowwiseRowsetEnabled()) |
| output_desc->setDescItem(0, SQLDESC_ROWSET_NUM_PROCESSED, |
| 0, NULL); |
| StmtDebug3("[END fetch] %p, result is %s, stmt state %s", this, |
| RetcodeToString(SQL_EOF), stmtState(getState())); |
| return SQL_EOF; |
| } |
| |
| if (!root_tcb) |
| { |
| // trying to fetch from a statement with a null root_tcb; |
| // probably, a bug elsewhere. For example, sqlc translates |
| // exec sql prepare S1 from :buf; |
| // into |
| // if ((SQLCODE = SQL_EXEC_ClearDiagnostics(&__SQL_id0)) < 0 |
| // ||(SQLCODE = SQL_EXEC_SetDescPointers |
| // (&__SQL_id1,1,1,&(buf[0]),NULL)) < 0 |
| // ||(SQLCODE = SQL_EXEC_AllocStmt(&__SQL_id0,NULL)) < 0 |
| // ||(SQLCODE = SQL_EXEC_Prepare(&__SQL_id0,&__SQL_id1)) < 0); |
| // Note that Prepare() will be short-circuited if any of the preceding |
| // CLI calls fail silently and the result can be a null root_tcb which |
| // will crash arkcmp unless we bulletproof against FETCH S1! |
| diagsArea << DgSqlCode(- CLI_INTERR_NULL_TCB); |
| StmtDebug3("[END fetch] %p, result is %s, stmt state %s", this, |
| RetcodeToString(ERROR), stmtState(getState())); |
| return ERROR; |
| } |
| |
| if (newOperation) |
| { |
| if ((isAnsiHoldable() || isPubsubHoldable()) && transactionReqd()) |
| { |
| if (! context_->getTransaction()->xnInProgress()) |
| { |
| if (beginTransaction(diagsArea)) |
| return ERROR; |
| } |
| } |
| } |
| else |
| { |
| if ((isAnsiHoldable() || isPubsubHoldable()) && transactionReqd()) |
| { |
| if (! context_->getTransaction()->xnInProgress()) |
| { |
| ex_assert(0, "Transaction is Not Valid"); |
| } |
| } |
| } |
| timeout = -1 ; |
| |
| // Check for suspended query. The other check called in the |
| // Scheduler::work. We do it here simply because some users |
| // expect if the query is suspended while the client is not |
| // blocked in SQL, the next fetch should not return any row. |
| // Without this check, a row could be returned if it was ready, |
| // since it would not be needed to call the Scheduler::work(). |
| statementGlobals_->getScheduler()->checkSuspendAndLimit(); |
| |
| /*fetch row*/ |
| ComDiagsArea* diagsPtr = NULL; |
| NABoolean closeCursorOnError = TRUE; |
| ExMasterStats *masterStats = |
| (stmtStats_ ? stmtStats_->getMasterStats() : NULL); |
| if (masterStats) |
| masterStats->setIsBlocking(); |
| if (output_desc && output_desc->rowwiseRowsetEnabled()) |
| { |
| NABoolean eodSeen=FALSE; |
| retcode = root_tcb->fetchMultiple(cliGlobals, statementGlobals_, |
| output_desc, |
| diagsPtr, timeout, newOperation, |
| closeCursorOnError,eodSeen); |
| if ((retcode >0) && (eodSeen)) |
| { |
| // If a warning is returned along with EOD,we have to return the |
| // warning retcode to MXCS otherwise they will not retrieve the diags |
| // But remember that eod was returned so we don't go through fetch |
| // again when we are redriven |
| setState(EOF_); |
| } |
| StmtDebug1(" root_tcb->fetchMultiple() returned %s", |
| RetcodeToString((RETCODE) retcode)); |
| |
| } |
| else |
| { |
| retcode = root_tcb->fetch(cliGlobals, statementGlobals_, output_desc, |
| diagsPtr, timeout, newOperation, |
| closeCursorOnError); |
| if (output_desc && output_desc->rowwiseRowset()) |
| { |
| if ((retcode >= 0) && |
| (retcode != 100)) |
| { |
| output_desc->setDescItem(0, SQLDESC_ROWSET_NUM_PROCESSED, |
| 1, NULL); |
| } |
| else |
| { |
| output_desc->setDescItem(0, SQLDESC_ROWSET_NUM_PROCESSED, |
| 0, NULL); |
| } |
| } |
| |
| StmtDebug1(" root_tcb->fetch() returned %s", |
| RetcodeToString((RETCODE) retcode)); |
| |
| } |
| |
| if (masterStats) |
| masterStats->setNotBlocking(); |
| |
| // "diagsPtr" is modified by the foregoing call. |
| // If "diagsPtr" is NULL, there are no diags to merge |
| // into "diagsArea". Otherwise, "diagsPtr" is not NULL and does |
| // point to a diags area from which we: 1) avoid the SQL function |
| // 2) copy the ComCondition objects over. Then we decrement |
| // the reference count to indicate we're done with that ComDiagsArea. |
| |
| if (diagsPtr) |
| { |
| /* if (diagsPtr->getNumber(DgSqlCode::WARNING_) > 0 && |
| diagsPtr->getNumber(DgSqlCode::ERROR_) == 0 && |
| retcode == ERROR && |
| diagsPtr->mainSQLCODE() >= 20000 && |
| diagsPtr->mainSQLCODE() <= 20999) |
| { |
| // Don't know why root_tcb->fetch returns retcode -1 (ERROR) |
| // when the SQL/MX Utility internal stored procedure issues |
| // warning messages but executes successfully otherwise |
| // (i.e., error messages was issued). Need to set retcode |
| // to SQL_EOF so this routine does not issue the unwanted |
| // CLI_TCB_FETCH_ERROR message. Cannot set retcode to either |
| // SUCCESS or WARNING because mxci will issue the following |
| // rows right after the warning messages if retcode is set |
| // to SUCCESS or WARNING: |
| // |
| // RECOVERSTATUS |
| // ------------- |
| // |
| // 134936872 |
| // |
| // If retcode is set to SQL_EOF, only the warning messages |
| // will be printed by mxci. Note that error/warning numbers |
| // range between 20000 and 20999 are used by the MODIFY utility |
| // and several other SQL/MX utilities (e.g., DUP, PURGEDATA). |
| // |
| retcode = SQL_EOF; |
| }*/ |
| diagsArea.mergeAfter(*diagsPtr); |
| diagsPtr->decrRefCount(); |
| diagsPtr = NULL; |
| } |
| |
| if (retcode < 0) |
| { |
| if (stmtStats_ != NULL && stmtStats_->getMasterStats() != NULL) |
| { |
| stmtStats_->getMasterStats()->setRowsAffected(statementGlobals_->getRowsAffected()); |
| } |
| updateStatsAreaInContext(); |
| |
| // Error case. |
| |
| // cancel the down request to my child. |
| // Do it only if closeCursorOnError is set to TRUE, the cursor |
| // remains open otherwise. |
| |
| if (closeCursorOnError) |
| { |
| // We must do this to clean up the messages from the queues |
| // (Else there are messages left over and these are incorrectly |
| // read if the statement is re-executed.) |
| diagsPtr = NULL; |
| if (root_tcb) |
| // We could have deallocated the root tcb earlier |
| retcode = root_tcb->cancel(statementGlobals_,diagsPtr); |
| StmtDebug1(" root_tcb->cancel() returned %s", |
| RetcodeToString((RETCODE) retcode)); |
| |
| if (diagsPtr) |
| { |
| diagsArea.mergeAfter(*diagsPtr); |
| diagsPtr->decrRefCount(); |
| diagsPtr = NULL; |
| } |
| |
| setState(CLOSE_); |
| } |
| |
| // rollback savepoints, if they are being done. |
| NABoolean doXnRollback = FALSE; |
| rollbackSavepoint(diagsArea, doXnRollback); |
| |
| // abort the transaction, if auto commit is on or |
| // rollbackTransaction flag is set in the diags area or |
| // this is a query that would have dirtied the disk(like, |
| // insert/update/delete). |
| if (rollbackTransaction(diagsArea, doXnRollback)) |
| { |
| StmtDebug3("[END fetch] %p, result is %s, stmt state %s", this, |
| RetcodeToString(ERROR), stmtState(getState())); |
| return ERROR; |
| } |
| |
| // If fatal error, we need to start over again |
| if (root_tcb && root_tcb->fatalErrorOccurred()) |
| { |
| dealloc(); |
| space_.freeBlocks(); |
| statementGlobals_->reAllocate(1); |
| setFixupState(0); |
| } |
| |
| if (diagsArea.getNumber(DgSqlCode::ERROR_) == 0) |
| { |
| // add an error indicating no error in diags. |
| // We will aqr retry on this error. |
| diagsArea << DgSqlCode(-CLI_NO_ERROR_IN_DIAGS); |
| } |
| |
| StmtDebug3("[END fetch] %p, result is %s, stmt state %s", this, |
| RetcodeToString(ERROR), stmtState(getState())); |
| return ERROR; |
| } |
| |
| if ( !newOperation |
| ) |
| { |
| if (handleUpdDelCurrentOf(diagsArea) == SQL_EOF) |
| retcode = SQL_EOF; |
| } |
| |
| if ((retcode != NOT_FINISHED) && (retcode != SQL_EOF) && |
| (getRootTdb()->isEmbeddedIUDWithLast1())) |
| { |
| // Add the affected row count that was directly placed into |
| // the master globals to those that are already in the diags |
| // area. |
| diagsArea.setRowCount(statementGlobals_->getRowsAffected()); |
| } |
| |
| if (retcode == WARNING) |
| { |
| StmtDebug3("[END fetch] %p, result is %s, stmt state %s", this, |
| RetcodeToString(WARNING), stmtState(getState())); |
| return WARNING; |
| } |
| else if (retcode == SQL_EOF) |
| { |
| StmtDebug1(" [EOF] Fetched SQL_EOF for statement %p", this); |
| |
| if (!(getRootTdb()->isEmbeddedIUDWithLast1())) |
| { |
| // Add the affected row count that was directly placed into |
| // the master globals to those that are already in the diags |
| // area. Make both row counts (diags and statementGlobals_) the same. |
| diagsArea.addRowCount(statementGlobals_->getRowsAffected()); |
| statementGlobals_->setRowsAffected(diagsArea.getRowCount()); |
| } |
| if (stmtStats_ != NULL && stmtStats_->getMasterStats() != NULL) |
| { |
| stmtStats_->getMasterStats()->setRowsAffected(statementGlobals_->getRowsAffected()); |
| } |
| updateStatsAreaInContext(); |
| |
| setState(FETCH_); |
| |
| // If this is a stored procedure result set, do bookkeeping for |
| // this EOF and if this is EOF for the final result set, then put |
| // the parent CALL in an EOF state (which has the side effect of |
| // ending an AUTOCOMMIT transaction that may be associated with |
| // the CALL). |
| if (parentCall_) |
| { |
| StmtDebug1(" [EOF] Parent CALL %p", parentCall_); |
| |
| ExRsInfo *parentRsInfo = parentCall_->getResultSetInfo(); |
| ex_assert(parentRsInfo, "No parent RS info available"); |
| |
| ULng32 rsIndex = parentRsInfo->getIndex(this); |
| parentRsInfo->setCloseAttempted(rsIndex); |
| StmtDebug2(" [EOF] RS index %u, Num closed since CALL %u", |
| rsIndex, parentRsInfo->getNumClosedSinceLastCall()); |
| |
| if (parentRsInfo->allResultsAreClosed()) |
| { |
| RETCODE closeResult = parentCall_->close(diagsArea); |
| StmtDebug1(" [EOF] parentCall_->close() returned %s", |
| RetcodeToString(closeResult)); |
| if (closeResult != ERROR) |
| { |
| parentCall_->setState(EOF_); |
| } |
| else |
| { |
| StmtDebug3("[END fetch] %p, result is %s, stmt state %s", this, |
| RetcodeToString(ERROR), stmtState(getState())); |
| return ERROR; |
| } |
| } |
| } |
| |
| StmtDebug0(" [EOF] About to call commitTransaction()..."); |
| |
| // end the transaction, if auto commit is on. |
| if (commitTransaction(diagsArea)) |
| { |
| StmtDebug3("[END fetch] %p, result is %s, stmt state %s", this, |
| RetcodeToString(ERROR), stmtState(getState())); |
| // In case there is a commit conflict, we need to reset the rowcount |
| // since none of the rows would have got committed. |
| diagsArea.setRowCount(0); |
| stmtStats_->getMasterStats()->setRowsAffected(0); |
| return ERROR; |
| } |
| |
| StmtDebug0(" [EOF] Done. commitTransaction() was successful"); |
| |
| setState(EOF_); |
| |
| if (output_desc) |
| { |
| // move EOF condition to diags area. |
| diagsArea << DgSqlCode(SQL_EOF); |
| } |
| |
| StmtDebug3("[END fetch] %p, result is %s, stmt state %s", this, |
| RetcodeToString(SQL_EOF), stmtState(getState())); |
| return SQL_EOF; |
| } |
| |
| |
| else |
| { |
| setState(FETCH_); |
| |
| if (getRootTdb()->updatableSelect()) |
| resetDeletedCursor(); |
| |
| StmtDebug3("[END fetch] %p, result is %s, stmt state %s", this, |
| RetcodeToString(SUCCESS), stmtState(getState())); |
| return SUCCESS; |
| } |
| |
| } // Statement::fetch() |
| |
| short Statement::handleUpdDelCurrentOf(ComDiagsArea &diags) |
| { |
| short retcode = SUCCESS; |
| |
| // if this is an update, delete, or insert query and no rows were |
| // affected and there are no warnings, return SQL_EOF. |
| // If there is a warning, return the warning. |
| // This is an ANSI requirement. |
| if (noRowsAffected(diags)) |
| { |
| // if this is an "update/delete where current of" operator |
| // and no rows were returned, |
| // raise a warning. This is done since an absence of this |
| // row at this point indicates that someone deleted this |
| // row between the time it was fetched (using the |
| // FETCH <cursor> INTO... statement) and now. |
| if ((isDeleteCurrentOf()) || (isUpdateCurrentOf())) |
| { |
| diags << DgSqlCode(EXE_CURSOR_UPDATE_CONFLICT); |
| } |
| |
| retcode = ((diags.getNumber(DgSqlCode::WARNING_) > 0) ? 0 : SQL_EOF); |
| // move EOF warning to diags area. |
| if (retcode == SQL_EOF) |
| diags << DgSqlCode(SQL_EOF); |
| |
| } |
| else |
| { |
| // if delete where current of, mark the cursor that this fetched |
| // row was deleted. This is to prevent another cursor delete/update |
| // (ANSI positioned delete/update) on this deleted row. |
| if (isDeleteCurrentOf()) |
| { |
| currentOfCursorStatement()->setDeletedCursor(); |
| } |
| } |
| return retcode; |
| } |
| |
| void Statement::updateTModeValues() |
| { |
| // Get the current transmode and update the values |
| // for transaction typing. |
| TransMode * cmpTransMode = root_tdb->getTransMode(); |
| TransMode * runTransMode = context_->getTransaction()->getTransMode(); |
| |
| // In the unlikely event that we don't have a runtime transMode |
| // just use the compiletimeTransMode. |
| if (!runTransMode) |
| runTransMode = cmpTransMode ; |
| |
| short roval = runTransMode->getAccessMode(); |
| short rbval = runTransMode->getRollbackMode(); |
| Lng32 aival = runTransMode->getAutoAbortIntervalInSeconds(); |
| |
| // if AccessMode is not set at runtime then the the compile time |
| // setting is applied. Note that roval has an effect on the |
| // transaction only if its value is READ_ONLY or READ_ONLY_SPECIFIED_BY_USER |
| if ((roval != TransMode::READ_ONLY_) && |
| (roval != TransMode::READ_ONLY_SPECIFIED_BY_USER_)) |
| roval = cmpTransMode->getAccessMode(); |
| |
| //if No rollback is specified through an IUD statement then it gets precedence |
| // in compareTransMode we check that autocommit is on at runtime for this setting |
| if (cmpTransMode->getRollbackMode() == TransMode::NO_ROLLBACK_IN_IUD_STATEMENT_) |
| rbval = TransMode::NO_ROLLBACK_ ; |
| |
| // if AutoAbortInterval is not set at runtime then the the compile time |
| // setting is applied. |
| if (aival == -1) |
| aival = cmpTransMode->getAutoAbortIntervalInSeconds(); |
| |
| context_->getTransaction()->updateROVal(roval); |
| context_->getTransaction()->updateRBVal(rbval); |
| context_->getTransaction()->updateAIVal(aival); |
| |
| } |
| |
| |
| RETCODE Statement::doOltExecute(CliGlobals *cliGlobals, |
| Descriptor * input_desc, |
| Descriptor * output_desc, |
| ComDiagsArea &diagsArea, |
| NABoolean &doNormalExecute, |
| NABoolean &reExecute) |
| { |
| Int32 retcode; |
| |
| if (!root_tdb) |
| { |
| diagsArea << DgSqlCode(-CLI_STMT_NOT_EXISTS); |
| return error(diagsArea); |
| } |
| |
| if (stmt_state != CLOSE_) |
| { |
| // trying to execute a statement which is already in open |
| // state |
| diagsArea << DgSqlCode(- CLI_STMT_NOT_CLOSE); |
| return ERROR; |
| } |
| |
| ExMasterStats * masterStats = NULL; |
| if (root_tdb && getStmtStats() && |
| (NULL != (masterStats = getStmtStats()->getMasterStats()))) |
| { |
| if (!masterStats->getValidDDL()) |
| { |
| diagsArea << DgSqlCode(-CLI_DDL_REDEFINED); |
| return ERROR; |
| } |
| if (!masterStats->getValidPrivs()) |
| { |
| diagsArea << DgSqlCode(-CLI_INVALID_QUERY_PRIVS); |
| return ERROR; |
| } |
| if (root_tdb->qCacheInfoIsClass() && root_tdb->qcInfo()) |
| masterStats->setCompilerCacheHit( |
| root_tdb->qcInfo()->cacheWasHit()); |
| Int64 jts = NA_JulianTimestamp(); |
| if (NOT masterStats->isPrepAndExec()) |
| masterStats-> setElapsedStartTime(jts); |
| masterStats->setFixupStartTime(-1); |
| masterStats->setFreeupStartTime(-1); |
| masterStats->setExeStartTime(aqrInitialExeStartTime_ == -1 ? jts : |
| aqrInitialExeStartTime_); |
| masterStats->setSqlErrorCode(0); |
| masterStats->setStmtState(STMT_EXECUTE_); |
| } |
| |
| // Get the current transmode and update the values |
| // for transaction typing. Code is here so values |
| // will be updated for BEGIN WORK statements. |
| updateTModeValues(); |
| |
| // Start a transaction, if needed and one not already running. |
| if (root_tdb->transactionReqd()) |
| { |
| // A user started transaction must be present for olt execution. |
| // if no user started transaction, then return and do normal |
| // execution. |
| if ((! context_->getTransaction()->xnInProgress()) || |
| ((context_->getTransaction()->exeStartedXn()) && |
| (context_->getTransaction()->implicitXn()))) |
| { |
| doNormalExecute = TRUE; |
| reExecute = FALSE; |
| return SUCCESS; |
| } |
| else |
| { |
| if (compareTransModes(root_tdb, context_->getTransaction(), |
| (root_tdb->aqrEnabled() ? &diagsArea : NULL)) |
| && !systemModuleStmt() ) |
| { |
| if (root_tdb->aqrEnabled()) |
| { |
| // return error. AQR will handle recompile and retry. |
| return(ERROR); |
| } |
| else |
| { |
| doNormalExecute = TRUE; |
| reExecute = FALSE; |
| return SUCCESS; |
| } |
| } |
| |
| // move the transid from executor globals to statement globals, |
| // if a transaction is running. |
| statementGlobals_->getTransid() = |
| context_->getTransaction()->getExeXnId(); |
| |
| if (root_tdb->getUpdSavepointOnError()) |
| { |
| // get a savepoint id which will be sent to DP2. |
| context_->getTransaction()->generateSavepointId(); |
| |
| statementGlobals_->getSavepointId() = |
| context_->getTransaction()->getSavepointId(); |
| } |
| } |
| } |
| |
| ExStatisticsArea *statsArea = getStatsArea(); |
| if (statsArea != NULL) |
| { |
| StatsGlobals *statsGlobals = cliGlobals->getStatsGlobals(); |
| if (statsGlobals != NULL) |
| { |
| int error = statsGlobals->getStatsSemaphore(cliGlobals->getSemId(), |
| cliGlobals->myPin()); |
| statsArea->initEntries(); |
| statsArea->restoreDop(); |
| statsGlobals->releaseStatsSemaphore(cliGlobals->getSemId(),cliGlobals->myPin()); |
| } |
| else |
| { |
| statsArea->initEntries(); |
| statsArea->restoreDop(); |
| } |
| } |
| |
| // do similarity check |
| if (root_tdb && root_tdb->querySimilarityInfo()) |
| { |
| NABoolean simCheckFailed = FALSE; |
| retcode = |
| doQuerySimilarityCheck(root_tdb->querySimilarityInfo(), |
| simCheckFailed, diagsArea); |
| |
| if (retcode == ERROR) |
| { |
| return ERROR; |
| } |
| } |
| |
| ComDiagsArea* diagsPtr = NULL; |
| if (! fixupState()) // first time |
| { |
| if ((stmtStats_) && (stmtStats_->getMasterStats())) |
| { |
| stmtStats_->getMasterStats()-> |
| setFixupStartTime(NA_JulianTimestamp()); |
| } |
| if (dealloc()) |
| goto retError; |
| |
| space_.freeBlocks(); |
| |
| statementGlobals_->reAllocate(1); |
| |
| setState(CLOSE_); |
| |
| setFixupState(0); |
| |
| NABoolean doSimCheck = FALSE; |
| NABoolean partitionUnavailable = FALSE; |
| retcode = fixup(cliGlobals, input_desc, diagsArea, doSimCheck, partitionUnavailable, FALSE); |
| if (doSimCheck || partitionUnavailable) |
| { |
| commitImplicitTransAndResetTmodes(); |
| |
| // Redrive the execution if fixup said so. |
| // It is OK to clear the diags area, because |
| // normal execution will perform the visibility check again. |
| diagsArea.clear(); |
| |
| setTsMismatched(FALSE); |
| |
| doNormalExecute = TRUE; |
| reExecute = FALSE; |
| return SUCCESS; |
| } |
| if ((stmtStats_) && (stmtStats_->getMasterStats())) |
| { |
| stmtStats_->getMasterStats()-> |
| setFixupEndTime(NA_JulianTimestamp()); |
| } |
| |
| commitImplicitTransAndResetTmodes(); |
| if (retcode == ERROR) |
| goto retError; |
| |
| } |
| |
| /* execute it */ |
| |
| // nothing happened yet. |
| statementGlobals_->setGlobDiagsArea(0); |
| statementGlobals_->setRowsAffected(0); |
| setState(OPEN_); |
| updateChildQid(); |
| if (masterStats) |
| masterStats->setIsBlocking(); |
| retcode = root_tcb->oltExecute(statementGlobals_, input_desc, |
| output_desc, diagsPtr); |
| StmtDebug1(" root_tcb->oltExecute() returned %s", |
| RetcodeToString((RETCODE) retcode)); |
| |
| if (masterStats) |
| masterStats->setNotBlocking(); |
| if (diagsPtr) |
| { |
| NABoolean lostOpen = diagsPtr->contains(-EXE_LOST_OPEN); |
| |
| if (lostOpen) |
| { |
| NABoolean retryableStatement = root_tdb->retryableStmt(); |
| |
| if (retryableStatement) |
| { |
| retcode = root_tcb->cancel(statementGlobals_,diagsPtr); |
| StmtDebug1(" root_tcb->cancel() returned %s", |
| RetcodeToString((RETCODE) retcode)); |
| |
| setState(CLOSE_); |
| doNormalExecute = TRUE; |
| reExecute = TRUE; |
| return SUCCESS; |
| } |
| else |
| { |
| // lost the open on a non-retryable statement; |
| // force a fresh fix-up on next execution. |
| setFixupState(0); |
| } |
| } |
| |
| diagsArea.mergeAfter(*diagsPtr); |
| diagsPtr->decrRefCount(); |
| diagsPtr = NULL; |
| } |
| |
| if (retcode >= 0 && root_tcb->anyCbMessages()) |
| { |
| Int32 cancelRetcode = root_tcb->cancel(statementGlobals_,diagsPtr); |
| StmtDebug1(" root_tcb->cancel() returned %s", |
| RetcodeToString((RETCODE) cancelRetcode)); |
| |
| setState(CLOSE_); |
| releaseTransaction(TRUE); |
| } |
| |
| updateStatsAreaInContext(); |
| if (retcode < 0) |
| { |
| // rollback savepoints, if they are being done. |
| NABoolean doXnRollback = FALSE; |
| rollbackSavepoint(diagsArea, doXnRollback); |
| |
| retcode = root_tcb->cancel(statementGlobals_,diagsPtr); |
| StmtDebug1(" root_tcb->cancel() returned %s", |
| RetcodeToString((RETCODE) retcode)); |
| |
| setState(CLOSE_); |
| |
| // abort the transaction, if auto commit is on or |
| // rollbackTransaction flag is set in the diags area or |
| // this is a query that would have dirtied the disk(like, |
| // insert/update/delete). |
| if (rollbackTransaction(diagsArea, doXnRollback)) |
| return ERROR; |
| |
| // if no other diags, as a last resort emit this rather |
| // cryptic message |
| if (diagsArea.getNumber(DgSqlCode::ERROR_) == 0) |
| diagsArea << DgSqlCode(- CLI_TCB_EXECUTE_ERROR); |
| |
| return ERROR; |
| // return error(diagsArea); |
| } |
| else if (retcode == SUCCESS) |
| return SUCCESS; |
| else if (retcode == WARNING) |
| return WARNING; |
| else |
| { |
| diagsArea.setRowCount(statementGlobals_->getRowsAffected()); |
| if (getStmtStats() && (getStmtStats()->getMasterStats())) |
| stmtStats_->getMasterStats()->setRowsAffected(statementGlobals_->getRowsAffected()); |
| if ((statementGlobals_->getRowsAffected() > 0) || |
| (root_tdb->thereIsACompoundStatement())) |
| return SUCCESS; |
| else |
| { |
| if ((output_desc) || |
| (root_tdb && (root_tdb->updDelInsertQuery()))) |
| { |
| diagsArea << DgSqlCode(SQL_EOF); |
| return SQL_EOF; |
| } |
| else |
| return SUCCESS; |
| } |
| } |
| retError: |
| return error(diagsArea); |
| } |
| |
| Lng32 Statement::cancel() |
| { |
| StmtDebug2("[BEGIN cancel] %p, stmt state %s", this, stmtState(getState())); |
| |
| CancelState s = statementGlobals_->getCancelState(); |
| Lng32 retcode = 0; |
| |
| switch (s) |
| { |
| case CLI_CANCEL_TCB_INVALID: |
| // |
| statementGlobals_->setCancelState(CLI_CANCEL_REQUESTED); |
| break; |
| |
| case CLI_CANCEL_TCB_READY: |
| // request to cancel down the tcb tree. |
| root_tcb->requestCancel(); |
| break; |
| |
| case CLI_CANCEL_DISABLE: |
| retcode = -CLI_CANCEL_REJECTED; |
| break; |
| |
| default: |
| // CLI_CANCEL_REQUESTED |
| // ignore since an outstanding cancel request exists. |
| break; |
| } |
| |
| StmtDebug3("[END cancel] %p, result is %s, stmt state %s", |
| this, RetcodeToString((RETCODE) retcode), stmtState(getState())); |
| return retcode; |
| } |
| |
| void Statement::releaseStats() |
| { |
| ExStatisticsArea *myStats = getStatsArea(); |
| ExStatisticsArea *myOrigStatsArea = getOrigStatsArea(); |
| ExStatisticsArea *ctxStats = context_->getStats(); |
| ExStatisticsArea *newStats; |
| StatsGlobals *statsGlobals = cliGlobals_->getStatsGlobals(); |
| if (ctxStats && (ctxStats == myStats) && |
| ((Int32)myStats->getCollectStatsType() != SQLCLI_NO_STATS)) |
| { |
| if (statsGlobals != NULL && stmtStats_ != NULL && |
| (Int32)myStats->getCollectStatsType() != SQLCLI_ALL_STATS) |
| { |
| int error = statsGlobals->getStatsSemaphore(cliGlobals_->getSemId(), |
| cliGlobals_->myPin()); |
| // Make sure the ex_globals doesn't delete this stats area |
| // in case of dynamic statements, since stmtStats is also |
| // pointing to the same area |
| getGlobals()->setStatsArea(NULL); |
| // Context is pointing to it. Remove query shouldn't |
| // deallocate it |
| stmtStats_->setStmtStatsUsed(TRUE); |
| context_->setStatsArea(myStats, FALSE, TRUE, FALSE); |
| // Set the StmtStats that can be used to reset the used flag |
| context_->setPrevStmtStats(stmtStats_); |
| statsGlobals->releaseStatsSemaphore(cliGlobals_->getSemId(), |
| cliGlobals_->myPin()); |
| } |
| else |
| { |
| // Resources for this statement are being deallocated. If the |
| // context is pointing to my stats area, make a new copy of the |
| // stats area and attach it to context. |
| newStats = new (context_->exHeap()) |
| ExStatisticsArea(context_->exHeap(), 0, |
| myStats->getCollectStatsType()); |
| newStats->setStatsEnabled(myStats->statsEnabled()); |
| newStats->merge(myStats); |
| if (newStats->getMasterStats() == NULL && stmtStats_ != NULL |
| && stmtStats_->getMasterStats() != NULL) |
| { |
| ExMasterStats * ems = new(context_->exHeap()) |
| ExMasterStats(context_->exHeap()); |
| ems->copyContents(stmtStats_->getMasterStats()); |
| newStats->setMasterStats(ems); |
| } |
| if (stmtStats_ != NULL) |
| stmtStats_->setStatsArea(NULL); |
| // Attach the new stats area to the context |
| context_->setStatsArea(newStats, TRUE, FALSE, TRUE); |
| } |
| } |
| else |
| if (myOrigStatsArea != NULL) |
| { |
| // Make sure the ex_globals doesn't delete this stats area |
| // in case of dynamic statements, since stmtStats is also |
| // pointing to the same area |
| if (stmtStats_ != NULL && myOrigStatsArea != NULL && |
| (Int32)myOrigStatsArea->getCollectStatsType() != SQLCLI_ALL_STATS) |
| getGlobals()->setStatsArea(NULL); |
| } |
| } |
| |
| RETCODE Statement::releaseTcbs(NABoolean closeAllOpens) |
| { |
| |
| if (root_tcb) |
| { |
| releaseStats(); |
| // The root tcb takes care of deleting itself and of deleting |
| // the statement globals. |
| if (statementGlobals_) |
| { |
| ExRtFragTable *ft = statementGlobals_->getRtFragTable(); |
| |
| // finish any transactional messages outstanding |
| // for this statement, since the statement won't |
| // be found anymore on the context's statement list |
| // Should do this before releaseEsps, so that they |
| // will not be sending work or continue messages to |
| // each other after the deallocation of fragment |
| // instances has begun (see Soln 10-060504-6268). |
| releaseTransaction(TRUE); |
| |
| // release all esps next |
| releaseEsps(closeAllOpens); |
| |
| // Deallocate TCB trees for all stored procedure result set |
| // children. The UDR leaf nodes in the result set TCB tree are |
| // not valid once the UDR leaf node in this CALL statement |
| // goes away, so those result set TCBs must be deallocated first. |
| ExRsInfo *rsInfo = getResultSetInfo(); |
| if (rsInfo) |
| { |
| StmtDebug0(" About to call ExRsInfo::reset()"); |
| rsInfo->reset(); |
| |
| ULng32 numChildren = rsInfo->getNumEntries(); |
| StmtDebug1(" Num RS children: %d", (Lng32) numChildren); |
| |
| for (ULng32 i = 1; i <= numChildren; i++) |
| { |
| Statement *rsChild = NULL; |
| const char *proxySyntax = NULL; |
| NABoolean open = FALSE; |
| NABoolean closed = FALSE; |
| NABoolean prepared = FALSE; |
| |
| NABoolean found = rsInfo->getRsInfo(i, rsChild, proxySyntax, |
| open, closed, prepared); |
| |
| if (found && rsChild) |
| { |
| StmtDebug1(" About to call dealloc() on RS %p", rsChild); |
| rsChild->dealloc(); |
| } |
| |
| } // for each RS child |
| } // if (rsInfo) |
| |
| // This call to deallocAndDelete() will trigger destructor calls |
| // for all TCBs |
| Int32 retcode = root_tcb->deallocAndDelete(statementGlobals_,ft); |
| |
| if (retcode < 0) |
| { |
| context_->diags() << DgSqlCode(- EXE_INTERNAL_ERROR); |
| return ERROR; |
| } |
| |
| root_tcb = NULL; |
| |
| // |
| // Deallocating the TCB tree may have caused more messages |
| // to be sent. To be safe, we once again wait for completion |
| // of transactional messages. |
| // |
| releaseTransaction(TRUE); |
| |
| } // if (statementGlobals_) |
| |
| } // if (root_tcb) |
| |
| root_tcb = NULL; |
| return SUCCESS; |
| } |
| |
| RETCODE Statement::dealloc(NABoolean closeAllOpens) |
| { |
| StmtDebug2("[BEGIN dealloc] %p, stmt state %s", this, stmtState(getState())); |
| |
| RETCODE result = SUCCESS; |
| |
| // stop the transaction, if once was started |
| if ((context_->aqrInfo()->xnStartedAtPrepare()) && |
| (context_->getTransaction()->xnInProgress()) && |
| (context_->getTransaction()->exeStartedXn()) && |
| (context_->getTransaction()->autoCommit()) && |
| (autocommitXn())) |
| { |
| context_->aqrInfo()->setXnStartedAtPrepare(FALSE); |
| |
| if (commitTransaction(context_->diags())) |
| { |
| // ignore error and proceed to deallocate |
| } |
| } |
| |
| context_->removeFromStatementWithEspsList(this); |
| |
| result = releaseTcbs(closeAllOpens); |
| |
| if (result == SUCCESS) |
| { |
| setState(CLOSE_); |
| setFixupState(0); |
| setComputeBulkMoveInfo(TRUE); |
| |
| // deallocate all cloned statements |
| clonedStatements->position(); |
| Statement * clone = (Statement*)clonedStatements->getNext(); |
| for (; clone != NULL; clone = (Statement*)clonedStatements->getNext()) |
| { |
| StmtDebug1(" About to call dealloc on clone %p", clone); |
| clone->dealloc(); |
| } |
| |
| context_->removeFromCloseStatementList(this, FALSE); |
| |
| if (extractConsumerQueryTemplate_) |
| { |
| heap_.deallocateMemory(extractConsumerQueryTemplate_); |
| extractConsumerQueryTemplate_ = NULL; |
| } |
| } // if (result == SUCCESS) |
| |
| StmtDebug2("[END dealloc] %p, stmt state %s", this, stmtState(getState())); |
| return result; |
| } |
| |
| static Lng32 getMaxParamIdx(ex_root_tdb *rootTdb){ |
| Lng32 maxParamIdx = 0; |
| if(rootTdb->inputExpr()){ |
| maxParamIdx = rootTdb->inputExpr()->getMaxParamIdx(); |
| } |
| |
| if(rootTdb->outputExpr()){ |
| Lng32 outputParamIdx = rootTdb->outputExpr()->getMaxParamIdx(); |
| if(outputParamIdx > maxParamIdx) |
| maxParamIdx = outputParamIdx; |
| } |
| return maxParamIdx; |
| } |
| |
| RETCODE Statement::describe(Descriptor * desc, Lng32 what_desc, |
| ComDiagsArea &diagsArea) |
| { |
| // One special case is for stored procedure result set proxy |
| // statements. CLI callers do not prepare them. We do internal |
| // prepares whenever the CLI caller describes or executes them. |
| if (parentCall_) |
| { |
| ExRsInfo *rsInfo = parentCall_->getResultSetInfo(); |
| if (rsInfo) |
| { |
| ULng32 rsIndex = rsInfo->getIndex(this); |
| RETCODE proxyRetcode = rsProxyPrepare(*rsInfo, rsIndex, diagsArea); |
| if (proxyRetcode == ERROR) |
| return ERROR; |
| } |
| } |
| |
| if (!root_tdb) |
| return SUCCESS; |
| |
| InputOutputExpr *expr; |
| enum ex_expr::exp_return_type expRType; |
| |
| Int32 retcode = 0; // be optimistic |
| |
| // A special case for DESCRIBE is when the SQL statement is a CALL |
| // statement and the CLI caller wants to use a "wide" descriptor for |
| // the CALL. |
| if (root_tdb->hasCallStmtExpressions() && desc->isDescTypeWide()) |
| { |
| // First populate the caller's descriptor with information from |
| // the INPUT expression, the overlay information from the OUTPUT |
| // expression into the same descriptor. |
| |
| Lng32 eNumEntries = getMaxParamIdx(root_tdb); |
| if (desc->getMaxEntryCount() < eNumEntries){ |
| diagsArea << DgSqlCode(CLI_TDB_DESCRIBE_ERROR); |
| return ERROR; |
| } |
| else if (desc->getUsedEntryCount() < eNumEntries) { |
| desc->dealloc(); |
| desc->alloc(eNumEntries); |
| } |
| |
| // The code in expr->describe sets the entry count in case of a Call stmt |
| // only if the new count is greater than the old count. This |
| // causes the old count to be returned during describe, if the |
| // same descriptor was reused and the number of entries in the new |
| // query is less than the old count. |
| // Setting the entry count to 0 before calling describe. |
| desc->setUsedEntryCount(0); |
| |
| if (expr = root_tdb->inputExpr()) |
| { |
| UInt32 flags = 0; |
| expr->setIsODBC(flags, root_tdb->odbcQuery()); |
| expr->setInternalFormatIO(flags, |
| context_->getSessionDefaults()-> |
| getInternalFormatIO()); |
| expr->setIsRWRS(flags, (root_tdb->getRWRSInfo() != NULL)); |
| expr->setIsDBTR(flags, |
| context_->getSessionDefaults()->getDbtrProcess()); |
| |
| expRType = expr->describeInput(desc, NULL, flags); |
| if(expRType != ex_expr::EXPR_OK) |
| retcode = -1; |
| } |
| |
| if ((retcode == 0) && (expr = root_tdb->outputExpr())) |
| { |
| UInt32 flags = 0; |
| expr->setIsODBC(flags, root_tdb->odbcQuery()); |
| expr->setInternalFormatIO(flags, |
| context_->getSessionDefaults()-> |
| getInternalFormatIO()); |
| expr->setIsDBTR(flags, |
| context_->getSessionDefaults()->getDbtrProcess()); |
| expRType = expr->describeOutput(desc, flags); |
| if(expRType != ex_expr::EXPR_OK) |
| retcode = -1; |
| } |
| } |
| else{ |
| if (what_desc == SQLWHAT_INPUT_DESC) |
| { |
| /* describe input */ |
| // retcode = root_tdb->describe(desc, 0); |
| expr = root_tdb->inputExpr(); |
| if (expr == NULL) |
| { |
| // set the entry count to zero. Otherwise desc will contain the |
| // entry count of the last describe. |
| desc->setUsedEntryCount(0); |
| return SUCCESS; |
| } |
| |
| if (desc->getMaxEntryCount() < expr->getNumEntries()) |
| { |
| desc->setMaxEntryCount(expr->getNumEntries()); |
| } |
| |
| if (desc->getUsedEntryCount() < expr->getNumEntries()) |
| { |
| desc->dealloc(); |
| desc->alloc(expr->getNumEntries()); |
| } |
| |
| UInt32 flags = 0; |
| expr->setIsODBC(flags, root_tdb->odbcQuery()); |
| expr->setInternalFormatIO(flags, |
| context_->getSessionDefaults()-> |
| getInternalFormatIO()); |
| expr->setIsRWRS(flags, (root_tdb->getRWRSInfo() != NULL)); |
| expr->setIsDBTR(flags, |
| context_->getSessionDefaults()->getDbtrProcess()); |
| if (expr->describeInput(desc, root_tdb->getRWRSInfo(), flags) |
| != ex_expr::EXPR_OK) |
| retcode = -1; |
| } |
| else |
| { |
| /* describe output */ |
| // retcode = root_tdb->describe(desc, -1); |
| expr = root_tdb->outputExpr(); |
| if (expr == NULL) |
| { |
| // set the entry count to zero. Otherwise desc will contain the |
| // entry count of the last describe. |
| desc->setUsedEntryCount(0); |
| return SUCCESS; |
| } |
| |
| if (desc->getMaxEntryCount() < expr->getNumEntries()) |
| { |
| desc->setMaxEntryCount(expr->getNumEntries()); |
| } |
| |
| if (desc->getUsedEntryCount() < expr->getNumEntries()) |
| { |
| desc->dealloc(); |
| desc->alloc(expr->getNumEntries()); |
| } |
| |
| UInt32 flags = 0; |
| expr->setIsODBC(flags, root_tdb->odbcQuery()); |
| expr->setInternalFormatIO(flags, |
| context_->getSessionDefaults()-> |
| getInternalFormatIO()); |
| expr->setIsDBTR(flags, |
| context_->getSessionDefaults()->getDbtrProcess()); |
| if (expr->describeOutput(desc, flags) != ex_expr::EXPR_OK) |
| retcode = -1; |
| } |
| } |
| if (retcode) |
| { |
| diagsArea << DgSqlCode(- CLI_TDB_DESCRIBE_ERROR); |
| return ERROR; |
| } |
| |
| return SUCCESS; |
| } |
| |
| void Statement::copyGenCode(char * gen_code, ULng32 gen_code_len, |
| NABoolean unpackTDBs) |
| { |
| StmtDebug3("[BEGIN copyGenCode] this %p, gen_code %p, len %u", |
| this, gen_code, gen_code_len); |
| |
| ex_assert(!isCloned(), |
| "copyGenCode() should not be called on a cloned statement"); |
| |
| // Release the TDB tree if one exists |
| assignRootTdb(NULL); |
| |
| // do some basic sanity checking |
| if ((gen_code == 0) || (gen_code_len <= 0)) |
| { |
| // gen_code = 0; |
| // gen_code_len = 0; |
| return; |
| } |
| |
| CollHeap * h = cliGlobals_->getArkcmp()->getHeap(); |
| char *newTdbTree = (char *) (new (h) char[gen_code_len]); |
| str_cpy_all(newTdbTree, gen_code, (Lng32) gen_code_len); |
| |
| assignRootTdb((ex_root_tdb *) newTdbTree); |
| root_tdb_size = (Lng32) gen_code_len; |
| |
| if (unpackTDBs) |
| { |
| // Set up the reallocator for use when object version migration occurs. |
| // |
| ComTdb dummyTdb; |
| ex_root_tdb *newRoot = (ex_root_tdb *) |
| root_tdb->driveUnpack((void *)root_tdb,&dummyTdb, unpackSpace_); |
| |
| assignRootTdb(newRoot); |
| |
| if (root_tdb == NULL) |
| { |
| // ERROR during unpacking. Most likely case is verison-unsupported. |
| // How do I report an error here ??? |
| // |
| // diagsArea << DgSqlCode(- CLI_STMT_NOT_PREPARED); |
| // return ERROR; |
| // |
| // Alternative: add code to drive a recompilation. Invalidate the plan. |
| // |
| } |
| |
| if ((stmt_type == STATIC_STMT) && (root_tdb)) |
| setRecompWarn(root_tdb->recompWarn()); |
| |
| } // if (unpackTDBs) |
| |
| StmtDebug1("[END copyGenCode] this %p", this); |
| } |
| |
| void Statement::copyInSourceStr(char * in_source_str_, Lng32 src_len_in_octets, |
| Lng32 charset) |
| { |
| charset_ = charset; |
| NAHeap * heap = context_->exHeap(); |
| if (source_str) |
| NADELETEBASIC(source_str, heap); |
| |
| // do some basic sanity checking |
| if ((in_source_str_ == 0) || (src_len_in_octets <= 0)) |
| { |
| source_str = 0; |
| source_length = 0; |
| return; |
| } |
| |
| Int32 maxBytesPerChar = CharInfo::maxBytesPerChar((CharInfo::CharSet)charset); |
| source_str = new (heap) char[src_len_in_octets+maxBytesPerChar]; |
| str_cpy_all(source_str, in_source_str_, src_len_in_octets); |
| if (charset == SQLCHARSETCODE_UCS2) |
| ((NAWchar*)source_str)[src_len_in_octets/maxBytesPerChar] = 0; |
| else |
| source_str[src_len_in_octets] = '\0'; |
| source_length = src_len_in_octets; |
| } |
| |
| void Statement::copyOutSourceStr(char * out_source_str, |
| Lng32 &out_src_len_in_octets) // INOUT |
| { |
| // do some basic sanity checking |
| if ((out_source_str == NULL) || (out_src_len_in_octets <= 0) || |
| (out_src_len_in_octets < source_length)) |
| { |
| out_src_len_in_octets = 0; |
| return; |
| } |
| |
| Int32 maxBytesPerChar = CharInfo::maxBytesPerChar((CharInfo::CharSet)charset_); |
| str_cpy_all(out_source_str, source_str, source_length); |
| if (charset_ == SQLCHARSETCODE_UCS2) |
| ((NAWchar*)out_source_str)[source_length/maxBytesPerChar] = 0; |
| else |
| out_source_str[source_length] = '\0'; |
| |
| out_src_len_in_octets = source_length; |
| } |
| |
| void Statement::copySchemaName(char * schemaName, Lng32 schemaNameLength) |
| { |
| if (schemaName_) |
| NADELETEBASIC(schemaName_, (&heap_)); |
| |
| // do some basic sanity checking |
| if ((schemaName == 0) || (schemaNameLength <= 0)) |
| { |
| schemaName_ = 0; |
| schemaNameLength_ = 0; |
| return; |
| } |
| |
| schemaName_ = new (&heap_) char[schemaNameLength + 1]; |
| str_cpy_all(schemaName_, schemaName, schemaNameLength); |
| schemaName_[schemaNameLength] = '\0'; |
| schemaNameLength_ = schemaNameLength; |
| } |
| |
| void Statement::copyRecompControlInfo(char * basePtr, |
| char * recompControlInfo, |
| Lng32 recompControlInfoLength) |
| { |
| } |
| |
| |
| void Statement::setCursorName(const char * cn) |
| { |
| // as a side effect of this method the statement is added |
| // to the cursorList of the current context. If the cursorName |
| // changes, the statement is removed from the cursorList and |
| // then re-inserted with the new name. This is neccessary, |
| // because the name is used for hashing into the cursorList. |
| |
| StmtDebug2(" Setting cursor name for statement %p to \"%s\"", |
| this, (cn ? cn : "(NULL)")); |
| |
| ComDiagsArea &diags = context_->diags(); |
| |
| Lng32 nameLength = 0; |
| char *copy_of_cn; |
| |
| |
| if (cursor_name_) { |
| |
| // Need to use length in comparison when |
| // the length argument is added for "cn". |
| |
| // if we are changing it to the same name, save some work... |
| if (cn && !strcmp(cn, cursor_name_->identifier)) |
| return; |
| context_->removeCursor(cursor_name_, statement_id->module); |
| heap_.deallocateMemory(cursor_name_); |
| } |
| |
| if (cn) { |
| nameLength = str_len(cn); |
| copy_of_cn = new (&heap_) char [nameLength+1]; |
| str_cpy_all(copy_of_cn,cn,nameLength); |
| copy_of_cn[nameLength] = '\0'; |
| |
| cursor_name_ = (SQLSTMT_ID *)(heap_.allocateMemory(sizeof(SQLSTMT_ID))); |
| init_SQLCLI_OBJ_ID(cursor_name_, SQLCLI_CURRENT_VERSION, |
| cursor_name, 0, copy_of_cn, 0, |
| SQLCHARSETSTRING_ISO88591, str_len(cn)); |
| |
| StmtDebug1(" Adding statement %p to the cursor list", this); |
| context_->addToCursorList(*this); |
| } |
| else |
| cursor_name_ = 0; |
| } |
| |
| short Statement::transactionReqd() |
| { |
| return ((short) (root_tdb && root_tdb->transactionReqd())); |
| } |
| |
| Int64 Statement::getRowsAffected() |
| { |
| return statementGlobals_->getRowsAffected(); |
| } |
| |
| ////////////////////// |
| // PRIVATE methods |
| ////////////////////// |
| |
| |
| // starts a transaction, if one has not already been started |
| // and if needed. |
| short Statement::beginTransaction(ComDiagsArea &diagsArea) |
| { |
| |
| // Get the current transmode and update the values |
| // for transaction typing. Code is here so values |
| // will be updated for BEGIN WORK statements. |
| updateTModeValues(); |
| |
| // implicit xns for ddl stmts will be started and committed/aborted |
| // in arkcmp if auto commit is on. Otherwise, it will be started here. |
| if ((root_tdb->transactionReqd()) && |
| ((NOT root_tdb->ddlQuery()) || |
| (NOT context_->getTransaction()->autoCommit()))) |
| { |
| // the trans mode at compile time of this query must be the |
| // same as the transaction mode at execution time. |
| if (!systemModuleStmt() && |
| compareTransModes( |
| root_tdb, |
| context_->getTransaction(), |
| &diagsArea, systemModuleStmt())) |
| return ERROR; |
| if (context_->getTransaction()->userEndedExeXn()) |
| { |
| if (!diagsArea.contains(-CLI_USER_ENDED_EXE_XN)) |
| diagsArea << DgSqlCode(- CLI_USER_ENDED_EXE_XN); |
| return ERROR; |
| } |
| if (! context_->getTransaction()->xnInProgress()) |
| { |
| // if no auto begin, then do not start the transaction. |
| if (context_->getTransaction()->getTransMode()->getAutoBeginOff()) |
| { |
| // return error. A transaction is needed, one is not |
| // running and autobegin has been disabled. |
| diagsArea << DgSqlCode(- CLI_AUTO_BEGIN_TRANSACTION_ERROR); |
| return ERROR; |
| } |
| |
| StmtDebug1(" About to BEGIN TX for statement %p...", this); |
| |
| // start a transaction since one is not already running. |
| short taRetcode = |
| context_->getTransaction()->beginTransaction(); |
| |
| StmtDebug1(" Return code is %d", (Lng32) taRetcode); |
| |
| if (taRetcode != 0) |
| { |
| diagsArea.mergeAfter(*context_->getTransaction()->getDiagsArea()); |
| // diagsArea << DgSqlCode(- CLI_BEGIN_TRANSACTION_ERROR); |
| return ERROR; |
| } |
| |
| context_->getTransaction()->implicitXn() = TRUE; |
| |
| // this statement started a transaction in autocommit mode. |
| // It means that this xn will only be committed when this |
| // statement ends execution. |
| if (context_->getTransaction()->autoCommit()) |
| { |
| setAutocommitXn(TRUE); |
| StmtDebug0(" AUTOCOMMIT transaction successfully started"); |
| } |
| else |
| { |
| setAutocommitXn(FALSE); |
| StmtDebug0(" Transaction successfully started"); |
| } |
| |
| } // if (! context_->getTransaction()->xnInProgress()) |
| |
| // move the transid from executor globals to statement globals, |
| // if a transaction is running. |
| statementGlobals_->getTransid() = |
| context_->getTransaction()->getExeXnId(); |
| |
| StmtDebug2(" Statement %p now has trans ID %s", this, |
| TransIdToText(statementGlobals_->getTransid())); |
| |
| // We might have suspended work on this or a previous transaction |
| // during the last CLOSE operation. Indicate to the root TCB that |
| // we are willing to work on a transaction again and that it is |
| // ok to send out transactional requests (to DP2 and to ESPs) |
| if (statementGlobals_ && statementGlobals_->getRtFragTable()) |
| { |
| statementGlobals_->getRtFragTable()->continueWithTransaction(); |
| } |
| |
| if (root_tdb->getUpdSavepointOnError()) |
| { |
| // get a savepoint id which will be sent to DP2. |
| context_->getTransaction()->generateSavepointId(); |
| |
| statementGlobals_->getSavepointId() = |
| context_->getTransaction()->getSavepointId(); |
| } |
| } // if (root_tdb->transactionReqd()) |
| |
| else |
| { |
| // Browse access cursors or the load phase of index will |
| // have the transaction passed (to ESPs?) if one exists |
| // in its statement globals |
| if (context_->getTransaction()->xnInProgress()) |
| { |
| statementGlobals_->getTransid() = |
| context_->getTransaction()->getExeXnId(); |
| } |
| } |
| if (stmtStats_ && stmtStats_->getMasterStats() != NULL) |
| stmtStats_->getMasterStats()->setTransId(statementGlobals_->getTransid()); |
| return 0; |
| } |
| |
| // ends(commits) transaction, if one is running and auto commit is on. |
| short Statement::commitTransaction(ComDiagsArea &diagsArea) |
| { |
| if (context_->getTransaction()->userEndedExeXn()) |
| { |
| if (!diagsArea.contains(-CLI_USER_ENDED_EXE_XN)) |
| diagsArea << DgSqlCode(- CLI_USER_ENDED_EXE_XN); |
| return ERROR; |
| } |
| |
| if ((context_->getTransaction()->xnInProgress()) && |
| (context_->getTransaction()->exeStartedXn()) && |
| (context_->getTransaction()->autoCommit()) && |
| (autocommitXn())) |
| { |
| if (context_->aqrInfo()) |
| context_->aqrInfo()->setXnStartedAtPrepare(FALSE); |
| |
| // get current context and close all statements |
| // started under the current transaction |
| context_->closeAllCursors(ContextCli::CLOSE_ALL, ContextCli::CLOSE_CURR_XN); |
| // Capture any errors that happened and return eg. transaction |
| // related errors that happen during |
| // Statement::close-> ExTransaction::commitTransaction that get called |
| // in ::closeAllCursors |
| if (diagsArea.mainSQLCODE() <0) |
| { |
| return ERROR; |
| } |
| |
| // if transaction is still active(it may have been committed at |
| // close cursor time if auto commit is on), commit it. |
| if (context_->getTransaction()->xnInProgress()) |
| { |
| StmtDebug2(" About to COMMIT, stmt %p, tx %s...", this, |
| TransIdToText(statementGlobals_->getTransid())); |
| // do waited commit for DDL queries |
| short taRetcode = context_->commitTransaction(); |
| |
| StmtDebug1(" Return code is %d", (Lng32) taRetcode); |
| |
| setAutocommitXn(FALSE); |
| |
| if (taRetcode != 0) |
| { |
| // If there are diagnostics in the statement globals then add |
| // them to the caller's diags area first. They may contain |
| // information about something that went wrong before the |
| // COMMIT was attempted. Then add information about the COMMIT |
| // failure that just occurred. |
| statementGlobals_->takeGlobalDiagsArea(diagsArea); |
| diagsArea.mergeAfter(*context_->getTransaction()->getDiagsArea()); |
| return ERROR; |
| } |
| StmtDebug0(" COMMIT was successful"); |
| } |
| } |
| else |
| { |
| StmtDebug0(" No AUTOCOMMIT transaction for this stmt"); |
| } |
| |
| return 0; |
| } |
| |
| short Statement::rollbackSavepoint(ComDiagsArea & diagsArea, |
| NABoolean &rollbackXn) |
| { |
| rollbackXn = FALSE; |
| |
| NABoolean rollbackSP = FALSE; |
| |
| if (context_->getTransaction()->xnInProgress()) |
| { |
| if ((context_->getTransaction()->exeStartedXn()) && |
| (context_->getTransaction()->autoCommit())) |
| { |
| rollbackSP = FALSE; |
| } |
| else if (diagsArea.getRollbackTransaction()) |
| rollbackSP = FALSE; |
| else if (root_tdb && |
| (root_tdb->transactionReqd() != 0)) |
| { |
| if (root_tdb->getUpdAbortOnError()) |
| rollbackSP = FALSE; |
| else if (root_tdb->getUpdPartialOnError()) |
| rollbackSP = FALSE; |
| else if (root_tdb->getUpdSavepointOnError()) |
| rollbackSP = TRUE; |
| else if ((NOT root_tdb->getUpdErrorOnError()) && |
| (NOT diagsArea.getNoRollbackTransaction())) |
| rollbackSP = FALSE; |
| } |
| } |
| |
| if (rollbackSP) |
| { |
| short retcode = root_tcb->rollbackSavepoint(); |
| if (retcode) |
| { |
| diagsArea.mergeAfter(*statementGlobals_->getDiagsArea()); |
| diagsArea << DgSqlCode(-CLI_SAVEPOINT_ROLLBACK_FAILED); |
| rollbackXn = TRUE; |
| } |
| else |
| { |
| // do not abort this transaction. |
| rollbackXn = FALSE; |
| diagsArea << DgSqlCode(CLI_SAVEPOINT_ROLLBACK_DONE); |
| // clr count of affected rows, CLI_SAVEPOINT_ROLLBACK_DONE. |
| statementGlobals_->setRowsAffected(0); |
| diagsArea.setRowCount(0); |
| } |
| } |
| |
| return 0; |
| } |
| |
| // abort (rollback) transaction, if one is running |
| short Statement::rollbackTransaction(ComDiagsArea & diagsArea, |
| NABoolean doXnRollback) |
| { |
| ContextCli::CloseCursorType closeCursorType; |
| |
| if (context_->getTransaction()->userEndedExeXn()) |
| { |
| if (!diagsArea.contains(-CLI_USER_ENDED_EXE_XN)) |
| diagsArea << DgSqlCode(- CLI_USER_ENDED_EXE_XN); |
| return ERROR; |
| } |
| |
| if (context_->getTransaction()->xnInProgress()) |
| { |
| if (context_->aqrInfo()) |
| context_->aqrInfo()->setXnStartedAtPrepare(FALSE); |
| |
| NABoolean rollbackXn = FALSE; |
| NABoolean autoCommitRollback = FALSE; |
| if ((context_->getTransaction()->exeStartedXn()) && |
| (context_->getTransaction()->autoCommit())) |
| { |
| rollbackXn = TRUE; |
| autoCommitRollback = TRUE; |
| } |
| else if (diagsArea.getRollbackTransaction()) |
| rollbackXn = TRUE; |
| else if (root_tdb && |
| (root_tdb->transactionReqd() != 0)) |
| { |
| if (root_tdb->getUpdAbortOnError()) |
| rollbackXn = TRUE; |
| else if (root_tdb->getUpdPartialOnError()) |
| rollbackXn = FALSE; |
| else if ((NOT root_tdb->getUpdSavepointOnError()) && |
| ((NOT root_tdb->getUpdErrorOnError()) && |
| (NOT diagsArea.getNoRollbackTransaction()))) |
| rollbackXn = TRUE; |
| else if (doXnRollback) |
| rollbackXn = TRUE; |
| |
| // If aqr has been enabled and this query returned a |
| // special error due to a concurrent ddl operation, |
| // then do not abort the transaction. This query will be retried |
| // and if this error persists, then the Xn will be aborted. |
| if (root_tdb->aqrEnabled() && |
| ((diagsArea.mainSQLCODE() == -EXE_TIMESTAMP_MISMATCH) || |
| (diagsArea.mainSQLCODE() == -CLI_INVALID_QUERY_PRIVS) || |
| (diagsArea.mainSQLCODE() == -CLI_DDL_REDEFINED) || |
| (diagsArea.mainSQLCODE() == -EXE_SCHEMA_SECURITY_CHANGED))) |
| { |
| rollbackXn = FALSE; |
| } |
| |
| // If aqr has been enabled and this query returned a lost open |
| // error due to a concurrent ddl operation, allow transaction |
| // rollback if IUD. IUD operations may have updated a subset of |
| // partitions before the lost open error was raised. For example, |
| // an earlier execution updated partitions 2, 3, and 4. A ddl |
| // operation now blows away these opens. The re-exec updates |
| // partition #1, and the lost open error is raised for 2, 3, |
| // and 4. The result can be data corruption unless we roll |
| // back the transaction. See solution 10-100604-0887. |
| // |
| // But if not IUD, This query will be retried and if this error |
| // persists, then the Xn will be aborted. |
| if (root_tdb->aqrEnabled() && |
| (diagsArea.mainSQLCODE() == -EXE_LOST_OPEN) && |
| (NOT root_tdb->updDelInsertQuery())) |
| { |
| rollbackXn = FALSE; |
| } |
| } |
| if (root_tdb && root_tdb->getPsholdCloseOnRollback()) |
| closeCursorType = ContextCli::CLOSE_ALL_INCLUDING_ANSI_PUBSUB_HOLDABLE; |
| else |
| closeCursorType = ContextCli::CLOSE_ALL_INCLUDING_ANSI_HOLDABLE; |
| if (rollbackXn) |
| { |
| short taRetcode; |
| |
| // clr count of affected rows. |
| // ALM CR 6903 -- although some rows may have been affected |
| // if an IUD used NO ROLLBACK, we don' currently have an accurate |
| // way to count these, due to the way EXE cancels sessions |
| // during error handling and the way the TSE does not dispatch |
| // exe-in-dp2 sessions when raising errors such as lock timeouts. |
| // So the fix for 6903 is to consistently return 0 rows affected. |
| // In the future, we might enhance this. |
| statementGlobals_->setRowsAffected(0); |
| diagsArea.setRowCount(0); |
| |
| NABoolean implicitTran = |
| context_->getTransaction()->implicitXn(); |
| // get current context and close all statements |
| // started under the current transaction |
| context_->closeAllCursors(closeCursorType, ContextCli::CLOSE_CURR_XN,0,TRUE); |
| |
| if (! context_->getTransaction()->xnInProgress()) |
| return 0; |
| |
| if (NOT autoCommitRollback) |
| { |
| ////////////////////////////////////////////////////////////// |
| // An error occurred while doing an upd/ins/del stmt. |
| // We really need to roll back stmt, but until stmt atomicity |
| // support is not in, we must abort Xn. |
| // RollbackStatement ALWAYS rollbacks Xn. RollbackTransaction |
| // only rollbacks if executor started the Xn. |
| ////////////////////////////////////////////////////////////// |
| if (context_->getTransaction()->exeStartedXn()) |
| { |
| StmtDebug1(" About to rollback statement %p...", this); |
| taRetcode = context_->getTransaction() |
| ->rollbackStatement(); |
| } |
| else |
| { |
| // doom the transaction |
| StmtDebug1(" About to DOOM TX for statement %p...", this); |
| taRetcode = context_->getTransaction() |
| ->doomTransaction(); |
| } |
| } |
| else |
| { |
| StmtDebug1(" About to ROLLBACK TX for statement %p...", this); |
| taRetcode = context_->getTransaction() |
| ->rollbackTransactionWaited(); |
| } |
| |
| StmtDebug1(" Return code is %d", (Lng32) taRetcode); |
| |
| // release all outstanding I/Os with transactions, just like |
| // we do when we commit the transaction |
| context_->releaseAllTransactionalRequests(); |
| |
| // fix 10-040526-4462. We need to force the transid in the context_ |
| // and the statement global to be in sync. Otherwise, the obsolete transid |
| // in the statement global will be passed to the ESP during releaseESPs() |
| // calls. For any remote ESP, the bad transid will be rejected by the TMF |
| // (inside the body of FS2_transid_to_buffer()) and the releasing operation |
| // on that ESP will fail, including the termination of the remote ESP. |
| // In some cases, the master will hung. |
| // |
| // After setting the transid in the statement global to -1 here, we just |
| // bypass the call to FS2_transid_to_buffer(). Since remote ESPs caches |
| // the transid, the cleanup operation will still use the "good" transid. |
| // Note here the transid is obsolete from TMF point of view, but still |
| // valid (good) in terms of SQL/MX data structures (e.g., transid as the key |
| // for table lookup). |
| // |
| // Note the transid in statement global should be to -1 after calling |
| // context::releaseAllTransactionalREquests() because that method calls |
| // statement::releaseTransaction() which uses the transid. |
| statementGlobals_->getTransid() = (Int64)-1; |
| |
| if (taRetcode != 0) |
| { |
| diagsArea.mergeAfter(*context_-> |
| getTransaction()->getDiagsArea()); |
| return ERROR; |
| } |
| else |
| { |
| // Fix for solution 10-090908-4444 requires an extra |
| // diags condition, error 8839. To avoid changing lots |
| // of expected files, don't do this in the regression |
| // test environment. |
| if (! implicitTran) |
| { |
| diagsArea << DgSqlCode(- CLI_VALIDATE_TRANSACTION_ERROR); |
| return ERROR; |
| } |
| else |
| { |
| if (context_->aqrInfo()) |
| context_->aqrInfo()->setAbortedTransWasImplicit(); |
| } |
| } |
| } |
| else if ((root_tdb) && |
| (root_tdb->getUpdPartialOnError())) |
| { |
| diagsArea << DgSqlCode(CLI_PARTIAL_UPDATED_DATA); |
| |
| // Add the affected row count that was directly placed into |
| // the master globals to those that are already in the diags |
| // area. Make both row counts (diags and statementGlobals_)the same. |
| diagsArea.addRowCount(statementGlobals_->getRowsAffected()); |
| statementGlobals_->setRowsAffected(diagsArea.getRowCount()); |
| } |
| } |
| |
| return 0; |
| } |
| |
| static |
| const NAWchar* get_name_mode(Lng32 name_mode) |
| { |
| switch (name_mode) |
| { |
| case stmt_handle: |
| return L"stmt_handle"; |
| break; |
| |
| case stmt_name: |
| return L"stmt_name"; |
| break; |
| |
| case cursor_name: |
| return L"cursor_name"; |
| break; |
| |
| case stmt_via_desc: |
| return L"stmt_via_desc"; |
| |
| case curs_via_desc: |
| return L"curs_via_desc"; |
| break; |
| |
| default: |
| return L"unknown"; |
| break; |
| } |
| } |
| |
| void Statement::dump(ostream * outstream) |
| { |
| *outstream << "Statement: " << this << '\n'; |
| |
| *outstream << "Module: "; |
| if ((statement_id->module) && (statement_id->module->module_name)) |
| // *outstream << getModNameInLocale(statement_id->module); |
| *outstream << (char *)(statement_id->module->module_name); |
| *outstream << '\n'; |
| |
| *outstream << "Statement Name: "; |
| if (statement_id->identifier) |
| // *outstream << getIdInLocale((SQLCLI_OBJ_ID*)statement_id); |
| *outstream << (char *)(statement_id->identifier); |
| *outstream << '\n'; |
| |
| *outstream << "Cursor Name: "; |
| if (cursor_name_) |
| // *outstream << getIdInLocale((SQLCLI_OBJ_ID*)cursor_name_); |
| *outstream << (char *)(cursor_name_); |
| *outstream << '\n'; |
| |
| *outstream << "Name Mode: " << get_name_mode(statement_id->name_mode) << '\n'; |
| |
| *outstream << "Type: " << (allocated() ? "DYNAMIC" : "STATIC") << '\n'; |
| |
| *outstream << "Heap (total): " << heap_.getTotalSize() << '\n'; |
| *outstream << "Heap (alloc): " << heap_.getAllocSize() << '\n'; |
| *outstream << "Space (total): " << space_.getAllocatedSpaceSize() << '\n'; |
| |
| if (source_str) |
| *outstream << source_str << '\n'; |
| |
| *outstream << "----------------------------------------------------\n"; |
| } |
| |
| void Statement::setState(State newState) { |
| // change the state of the statement. As a side effect the statement |
| // might be removed or added from/to the openStatementList() |
| switch (stmt_state) { |
| case OPEN_: |
| case EOF_: |
| case FETCH_: //gps Add FETCH_ to "transition from" cases. (4/21/99) |
| case RELEASE_TRANS_: |
| // remove from list if newState is CLOSE_ or DEALLOCATED_ |
| if ((newState == CLOSE_) || (newState == DEALLOCATED_)) |
| { |
| State originalState = stmt_state; |
| |
| // In certain cases, this statement is not added to the open |
| // statement list. This is a performance optimization for |
| // unique OLT queries. These queries are non-cursor queries |
| // that are completed within one CLI call (ClearExecFetchClose). |
| // See PerformTasks method in Cli.cpp for details. |
| // Do not add them to open stmt list, |
| stmt_state = newState; |
| if (NOT oltOpt()) |
| context_->removeFromOpenStatementList(statement_id); |
| |
| // If this is a stored procedure result set and it is |
| // transitioning out of an open state, then we have some |
| // bookkeeping to do |
| if (parentCall_ && originalState != EOF_) |
| { |
| ExRsInfo *parentRsInfo = parentCall_->getResultSetInfo(); |
| if (parentRsInfo) |
| { |
| StmtDebug2(" setState() changing RS state to %s, this %p", |
| stmtState(getState()), this); |
| ULng32 rsIndex = parentRsInfo->getIndex(this); |
| parentRsInfo->setCloseAttempted(rsIndex); |
| StmtDebug2(" RS index %u, Num closed since CALL %u", |
| rsIndex, parentRsInfo->getNumClosedSinceLastCall()); |
| } |
| } |
| |
| } |
| break; |
| |
| case PREPARE_: |
| // remove from list if newState is CLOSE_ or DEALLOCATED_ or INITIAL_ |
| if ((newState == CLOSE_) || (newState == DEALLOCATED_) || (newState == INITIAL_)) |
| { |
| context_->removeFromOpenStatementList(statement_id); |
| } |
| break; |
| |
| case INITIAL_: |
| case CLOSE_: |
| // add to openStatementList if new state is OPEN_ or PREPARE_ |
| if ((newState == OPEN_) || (newState == PREPARE_)) |
| { |
| if (NOT oltOpt()) |
| context_->addToOpenStatementList(statement_id, this); |
| else |
| context_->removeFromCloseStatementList(this, FALSE); |
| } |
| break; |
| } |
| |
| if (stmt_state != newState) |
| { |
| StmtDebug3("*** Statement %p state change: %s -> %s", |
| this, stmtState(stmt_state), stmtState(newState)); |
| } |
| |
| stmt_state = newState; |
| |
| if (stmtStats_ != NULL) |
| { |
| if (stmtStats_->getMasterStats() != NULL) |
| stmtStats_->getMasterStats()->setStmtState(newState); |
| stmtStats_->setMergeReqd(TRUE); |
| } |
| } |
| |
| NABoolean Statement::noRowsAffected(ComDiagsArea & diagsArea) |
| { |
| // ANSI says that an EOF is to be returned if no rows were affected |
| // by an update/delete/insert operation. |
| if ((root_tdb) && |
| (root_tdb->updDelInsertQuery()) && |
| (diagsArea.getRowCount() == 0) && |
| getRowsAffected() == 0) |
| return TRUE; |
| else |
| return FALSE; |
| } |
| |
| |
| NABoolean Statement::isSelectInto() |
| { |
| return (getRootTdb() && getRootTdb()->selectIntoQuery()); |
| } |
| |
| NABoolean Statement::isDeleteCurrentOf() |
| { |
| return (getRootTdb() && getRootTdb()->deleteCurrentOfQuery()); |
| } |
| |
| NABoolean Statement::isUpdateCurrentOf() |
| { |
| return (getRootTdb() && getRootTdb()->updateCurrentOfQuery()); |
| } |
| // BertBert VV |
| NABoolean Statement::isEmbeddedUpdateOrDelete() |
| { |
| return (getRootTdb() && getRootTdb()->isEmbeddedUpdateOrDelete()); |
| } |
| NABoolean Statement::isStreamScan() |
| { |
| return (getRootTdb() && getRootTdb()->isStreamScan()); |
| } |
| // BertBert ^^ |
| |
| RETCODE Statement::setHoldable(ComDiagsArea &diagsArea, NABoolean h) |
| { |
| if (root_tdb == NULL) |
| return setAnsiHoldable(diagsArea, h); |
| else |
| return setPubsubHoldable(diagsArea, h); |
| } |
| |
| RETCODE Statement::setPubsubHoldable(ComDiagsArea &diagsArea, NABoolean h) |
| { |
| // tbd -- error for open stmt?? |
| if (h && |
| root_tdb && |
| !root_tdb->isEmbeddedUpdateOrDelete() && |
| !root_tdb->isStreamScan()) |
| { |
| // Holdable cursors are only supported for streaming cursors |
| // and destructive cursors because there are some strange |
| // side-effects (locks disappear after commit) that we don't |
| // want to inflict on non-Publich/Subscribe functionality at |
| // this time. There are no source-code changes needed to allow |
| // holdable cursors in these other cases though. |
| diagsArea << DgSqlCode(-CLI_CURSOR_CANNOT_BE_HOLDABLE); |
| return ERROR; |
| } |
| if (stmt_state == OPEN_ || stmt_state == FETCH_ || stmt_state == RELEASE_TRANS_) |
| { |
| // Cannot change holdable attribute while cursor is open. |
| diagsArea << DgSqlCode(-CLI_CURSOR_ATTR_CANNOT_BE_SET); |
| return ERROR; |
| } |
| |
| if (h) |
| holdable_ = SQLCLIDEV_PUBSUB_HOLDABLE; |
| else |
| holdable_ = SQLCLIDEV_NONHOLDABLE; |
| return SUCCESS; |
| } |
| |
| RETCODE Statement::setAnsiHoldable(ComDiagsArea &diagsArea, NABoolean h) |
| { |
| // SQL_Exec_SetStmtAttr for setting the holdable cursor can't be called |
| // after the statement is prepared |
| if (root_tdb || stmt_state == OPEN_ || stmt_state == FETCH_ || stmt_state == RELEASE_TRANS_) |
| { |
| // Cannot change holdable attribute while cursor is open. |
| diagsArea << DgSqlCode(-CLI_CURSOR_ATTR_CANNOT_BE_SET); |
| return ERROR; |
| } |
| |
| if (h) |
| holdable_ = SQLCLIDEV_ANSI_HOLDABLE; |
| else |
| holdable_ = SQLCLIDEV_NONHOLDABLE; |
| return SUCCESS; |
| } |
| |
| RETCODE Statement::setInputArrayMaxsize(ComDiagsArea &diagsArea, const Lng32 inpArrSize) |
| { |
| if (inpArrSize < 0) { |
| diagsArea << DgSqlCode(-CLI_ARRAY_MAXSIZE_INVALID_ENTRY); |
| return ERROR; |
| } |
| |
| inputArrayMaxsize_ = inpArrSize ; |
| return SUCCESS ; |
| } |
| |
| RETCODE Statement::setRowsetAtomicity(ComDiagsArea &diagsArea, const AtomicityType atomicity) |
| { |
| if ((atomicity != UNSPECIFIED_) && (atomicity != ATOMIC_) && (atomicity != NOT_ATOMIC_)) { |
| diagsArea << DgSqlCode(-CLI_INVALID_ATTR_VALUE); |
| return ERROR; |
| } |
| |
| rowsetAtomicity_ = atomicity ; |
| return SUCCESS ; |
| } |
| |
| RETCODE Statement::setNotAtomicFailureLimit(ComDiagsArea &diagsArea, const Lng32 limit) |
| { |
| if (limit < 30) { |
| diagsArea << DgSqlCode(-CLI_INVALID_ATTR_VALUE); |
| return ERROR; |
| } |
| |
| notAtomicFailureLimit_ = limit ; |
| return SUCCESS ; |
| } |
| |
| |
| |
| void Statement::setOltOpt(NABoolean v) |
| { |
| if (! getRootTdb()) |
| return; |
| |
| if (v && getRootTdb()->doOltQueryOpt()) |
| flags_ |= OLT_OPT; |
| else |
| flags_ &= ~OLT_OPT; |
| } |
| |
| // Must be in CLOSE_ state. |
| // Will cause fixup at open. |
| Lng32 Statement::releaseSpace() |
| { |
| StmtDebug2("[BEGIN releaseSpace] %p, stmt state %s", |
| this, stmtState(getState())); |
| |
| if (stmt_state == INITIAL_) |
| { |
| StmtDebug2("[END releaseSpace] %p, result is %s", |
| this, RetcodeToString(SUCCESS)); |
| return SUCCESS; |
| } |
| |
| // Bugzilla 1662 |
| // A statement being reclaimed might be in the EOF_ state. We will |
| // not attempt to reclaim these statements. Here's one way an EOF_ |
| // statement can be encountered: |
| // * The statement has an autocommit transaction |
| // * End-of-data is fetched |
| // * Inside the CLI fetch, we commit the transaction, put the |
| // statement in the CLOSED_ state, and place the statement on |
| // the closed statement list |
| // * Then we change the statement state to EOF_ so that a subsequent |
| // SQL_EXEC_CloseStmt will succeed |
| // * The statement is now on the closed statement list, is in the |
| // EOF_ state, but is not actually a valid candidate for reclaim |
| // until the CLI caller issues SQL_EXEC_CloseStmt |
| if (stmt_state == EOF_) |
| { |
| StmtDebug2("[END releaseSpace] %p, result is %s", |
| this, RetcodeToString(SUCCESS)); |
| return SUCCESS; |
| } |
| |
| assert(stmt_state == CLOSE_); |
| RETCODE result = releaseTcbs(FALSE); |
| |
| if (result == SUCCESS) |
| { |
| setState(CLOSE_); // The state may have changed by a call within releaseTcbs() |
| space_.freeBlocks(); |
| setFixupState(0); // will fixup at exec() |
| // To recalculate the bulk move info when the tcb is re-generated |
| setComputeBulkMoveInfo(TRUE); |
| if (stmtStats_ != NULL && stmtStats_->getMasterStats() != NULL) |
| stmtStats_->getMasterStats()->incReclaimSpaceCount(); |
| } |
| |
| StmtDebug2("[END releaseSpace] %p, result is %s", |
| this, RetcodeToString(result)); |
| return (Lng32) result; |
| } |
| |
| NABoolean Statement::isReclaimable() |
| { |
| if (root_tdb != NULL && |
| (root_tdb->getQueryType() == ComTdbRoot::SQL_INSERT_RWRS || |
| root_tdb->cantReclaimQuery())) |
| return FALSE; |
| else |
| return TRUE; |
| } |
| |
| NABoolean Statement::returnRecompWarn() |
| { |
| if (stmt_type == STATIC_STMT) |
| return recompWarn(); |
| else if (root_tdb) |
| return root_tdb->recompWarn(); |
| else |
| return FALSE; |
| } |
| |
| // Wait for completion of UDR requests associated with this |
| // statement. If allRequests is FALSE then only wait for transactional |
| // requests. |
| RETCODE Statement::completeUdrRequests(NABoolean allRequests) const |
| { |
| |
| RETCODE result = SUCCESS; |
| while (statementGlobals_ && |
| ((allRequests && statementGlobals_->numUdrMsgsOut() > 0) || |
| (!allRequests && statementGlobals_->numUdrTxMsgsOut() > 0))) |
| { |
| // First attempt to complete requests on scalar udr server |
| IpcConnection *conn = statementGlobals_->getUdrConnection(); |
| ExUdrServer *udrServ = statementGlobals_->getUdrServer(); |
| if(conn != NULL && udrServ != NULL) |
| { |
| StmtDebug1( |
| " About to call ExUdrServer::completeUdrRequests(%p, FALSE)...", |
| conn); |
| |
| udrServ->completeUdrRequests(conn, FALSE); |
| } |
| |
| // Next, attempt to complete requests on dedicated udr servers |
| // if there are any. |
| LIST(ExUdrServer *) udrServList = statementGlobals_->getUdrServersD(); |
| for (CollIndex i = 0; i < udrServList.entries(); i++) |
| { |
| ExUdrServer *udrServ = udrServList[i]; |
| IpcConnection *conn = udrServ->getUdrControlConnection(); |
| |
| StmtDebug1( |
| " About to call ExUdrServer::completeUdrRequests(0x%08x, FALSE)...", |
| conn); |
| udrServ->completeUdrRequests(conn, FALSE); |
| } |
| StmtDebug0(" Done"); |
| } |
| |
| return result; |
| } |
| |
| void Statement::setUniqueStmtId(char * id) |
| { |
| if (uniqueStmtId_) |
| { |
| // get start & finished messages to cancel broker cleaned up |
| // before switching qid. |
| if (root_tcb && root_tcb->anyCbMessages()) |
| releaseTransaction(TRUE); |
| |
| NADELETEBASIC(uniqueStmtId_, &heap_); |
| uniqueStmtId_ = NULL; |
| } |
| |
| if (id) |
| { |
| uniqueStmtId_ = new(&heap_) char[strlen(id)+1]; |
| strcpy(uniqueStmtId_, id); |
| uniqueStmtIdLen_ = strlen(uniqueStmtId_); |
| } |
| else |
| { |
| // generate a unique id and set it. |
| uniqueStmtId_ = new(&heap_) char[ComSqlId::MAX_QUERY_ID_LEN+1]; |
| |
| char tmpLong[ 20 ]; |
| if (statement_id->name_mode == stmt_handle) |
| str_ltoa((ULong)getStmtHandle(), tmpLong); |
| ComSqlId::createSqlQueryId(uniqueStmtId_, |
| ComSqlId::MAX_QUERY_ID_LEN+1, |
| uniqueStmtIdLen_, |
| strlen(context_->getSessionId()), |
| context_->getSessionId(), |
| cliGlobals_->getNextUniqueNumber(), |
| ((statement_id->name_mode == stmt_handle) |
| ? strlen(tmpLong) |
| : strlen(getIdentifier())), |
| (char*) |
| ((statement_id->name_mode == stmt_handle) |
| ? tmpLong |
| : getIdentifier()) |
| ); |
| } |
| } |
| |
| // This method should always be used to assign a new value to the |
| // root_tdb data member. The method will keep the root_tdb pointer for |
| // a given statement in sync with its clones. |
| // |
| // NOTE: we really should have this method keep the root_tdb_size data |
| // member in sync with root_tdb. But currently root_tdb_size is only |
| // used by SHOWPLAN code paths and on other paths is not maintained. A |
| // possible future improvement is to keep root_tdb and root_tdb_size |
| // in sync on all code paths. |
| |
| ex_root_tdb * Statement::assignRootTdb(ex_root_tdb *new_root_tdb) |
| { |
| if (root_tdb == new_root_tdb) |
| return root_tdb; |
| |
| if (root_tdb) |
| { |
| // dealloc() will release the TCB tree for this statement and will |
| // also call dealloc() for any cloned statements |
| dealloc(); |
| |
| if (!isCloned()) |
| { |
| // solution 10-040803-8511: root_tdb is not unpacked for SHOWPLAN |
| // the lateNameInfoList is still the offset, not valid pointer |
| if (!root_tdb->isPacked() && root_tdb->getLateNameInfoList() != NULL) |
| for (Int32 i = 0; |
| i < (Int32) (root_tdb->getLateNameInfoList()->getNumEntries()); |
| i++) |
| root_tdb->getLateNameInfoList()-> |
| getLateNameInfo(i).zeroLastUsedAnsiName(); |
| CollHeap *h = cliGlobals_->getArkcmp()->getHeap(); |
| h->deallocateMemory((void *) root_tdb); |
| StmtDebug2(" Stmt %p deallocated root TDB %p", this, root_tdb); |
| } |
| } |
| |
| root_tdb = new_root_tdb; |
| StmtDebug2(" Stmt %p root TDB is now %p", this, root_tdb); |
| |
| #ifdef _DEBUG |
| Lng32 rs = (Lng32) (root_tdb ? root_tdb->getMaxResultSets() : 0); |
| if (rs > 0) |
| StmtDebug1(" Max result sets: %d", rs); |
| #endif |
| |
| clonedStatements->position(); |
| Statement *clone = (Statement *) clonedStatements->getNext(); |
| for (; clone != NULL; clone = (Statement *) clonedStatements->getNext()) |
| { |
| clone->root_tdb = new_root_tdb; |
| StmtDebug2(" Clone %p root TDB is now %p", clone, new_root_tdb); |
| } |
| |
| return root_tdb; |
| } |
| |
| RETCODE Statement::addDescInfoIntoStaticDesc(Descriptor * desc, |
| Lng32 what_desc, |
| ComDiagsArea &diagsArea) |
| { |
| if(!root_tdb){ |
| return SUCCESS; |
| } |
| |
| InputOutputExpr *expr; |
| enum ex_expr::exp_return_type expRType; |
| |
| Int32 retcode = 0; // be optimistic |
| |
| // A special case for DESCRIBE is when the SQL statement is a CALL |
| // statement and the CLI caller wants to use a "wide" descriptor for |
| // the CALL. |
| if (root_tdb->hasCallStmtExpressions() && desc->isDescTypeWide()) |
| { |
| // First populate the caller's descriptor with information from |
| // the INPUT expression, the overlay information from the OUTPUT |
| // expression into the same descriptor. |
| |
| if(expr = root_tdb->inputExpr()) |
| { |
| expRType = expr->addDescInfoIntoStaticDesc(desc, TRUE /*input*/); |
| if(expRType != ex_expr::EXPR_OK) |
| retcode = -1; |
| } |
| |
| if((retcode == 0) && (expr = root_tdb->outputExpr())) |
| { |
| expRType = expr->addDescInfoIntoStaticDesc(desc, FALSE /*output*/); |
| if(expRType != ex_expr::EXPR_OK) |
| retcode = -1; |
| } |
| } |
| else{ |
| NABoolean isInput; |
| if (what_desc == SQLWHAT_INPUT_DESC){ |
| expr = root_tdb->inputExpr(); // input expr -> desc |
| isInput = TRUE; |
| } |
| else{ |
| expr = root_tdb->outputExpr(); // output expr -> desc |
| isInput = FALSE; |
| } |
| if(expr) |
| { |
| expRType = expr->addDescInfoIntoStaticDesc(desc, isInput); |
| if(expRType != ex_expr::EXPR_OK) |
| retcode = -1; |
| } |
| } |
| |
| if (retcode) |
| { |
| diagsArea << DgSqlCode(- CLI_TDB_DESCRIBE_ERROR); |
| return ERROR; |
| } |
| |
| return SUCCESS; |
| } |
| |
| NABoolean Statement::containsUdrInteractions() const |
| { |
| return (root_tdb && root_tdb->containsUdrInteractions()); |
| } |
| |
| //------------------------------------------------------------------------------ |
| // |
| // For each stoi that is marked as subject table, get trigger status array from |
| // the corresponding rfork. This status array is used to update the status of the |
| // relevant triggers in the call to updateTriggerStatusPerTable() |
| // |
| RETCODE Statement::getTriggersStatus(SqlTableOpenInfoPtr* stoiList, ComDiagsArea &diagsArea) |
| { |
| |
| TriggerStatusWA triggerStatusWA(&heap_, root_tcb); |
| SqlTableOpenInfo* stoi; |
| RETCODE rc = SUCCESS; |
| |
| for (Int32 i=0; i< root_tcb->getTableCount(); i++) |
| { |
| stoi=stoiList[i]; |
| if (stoi->subjectTable()) |
| { |
| |
| // save the current diags before entering CLI again |
| ComDiagsArea* copyOfDiagsArea = diagsArea.copy(); |
| // remove warning 100 from diags. |
| diagsArea.removeFinalCondition100(); |
| |
| // merge diags |
| diagsArea.mergeAfter(*copyOfDiagsArea); |
| copyOfDiagsArea->decrRefCount(); |
| copyOfDiagsArea = NULL; |
| |
| // select the status of the triggers relevant to this statement |
| // and update them in the root_tcb->triggerStatusVector_ |
| triggerStatusWA.updateTriggerStatusPerTable(); |
| triggerStatusWA.deallocateStatusArray(); |
| } |
| } |
| |
| #ifdef _DEBUG |
| if (getenv("SHOW_ENABLE")) |
| { |
| char int64Str[128]; |
| cout << "Trigger Ids in TDB:" << endl; |
| cout << "-------------------" << endl; |
| for (Int32 i=0; i<triggerStatusWA.getTotalTriggersCount(); i++) |
| { |
| convertInt64ToAscii(root_tdb->getTriggersList()[i], int64Str); |
| cout << i << " : " << int64Str << endl; |
| } |
| cout << endl; |
| |
| } |
| #endif //_DEBUG |
| |
| // we cannot check that status was received for all triggers, since |
| // triggers may be dropped/added before similarity check is done... |
| // if (triggerStatusWA.getTotalTriggersCount() == root_tdb->getTriggersCount()) |
| return rc; |
| |
| } |
| |
| // ++ MV - |
| // isIudTargetTable returns TRUE if the table is the target of an |
| // insert/update/delete operation, or if the table does not appear in stoi. |
| NABoolean Statement::isIudTargetTable(char *tableName, |
| SqlTableOpenInfoPtr *stoiList) |
| { |
| NABoolean found = FALSE; |
| for (Int32 i=0; root_tcb && i < root_tcb->getTableCount(); i++) |
| { |
| SqlTableOpenInfo *stoi=stoiList[i]; |
| |
| if (! strcmp(stoi->ansiName(), tableName)) |
| { |
| found = TRUE; |
| if (stoi->getInsertAccess() || |
| stoi->getUpdateAccess() || |
| stoi->getDeleteAccess()) |
| return TRUE; |
| } |
| } |
| |
| if (found) |
| return FALSE; |
| else |
| return TRUE; |
| } |
| |
| RETCODE Statement::mvSimilarityCheck(char *table, |
| ULng32 siMvBitmap, |
| ULng32 rcbMvBitmap, |
| NABoolean &simCheckFailed, |
| ComDiagsArea &diagsArea) |
| { |
| ComMvAttributeBitmap siBitmap; |
| ComMvAttributeBitmap rcbBitmap; |
| NABoolean iud = isIudTargetTable(table, root_tdb->stoiStoiList()); |
| |
| siBitmap.initBitmap((ComSInt32)siMvBitmap); |
| rcbBitmap.initBitmap((ComSInt32)rcbMvBitmap); |
| |
| // if it is INSERT/UPDATE/DELETE statement and table/MV has initialized |
| // ON STATEMENT MV on it - recompile |
| if (iud && |
| rcbBitmap.getInitOnStmtMvOnMe()) |
| { |
| if (returnRecompWarn()) |
| diagsArea << DgSqlCode(EXE_SIM_CHECK_FAILED) |
| << DgString0("Table has on statement MVs on it."); |
| simCheckFailed = TRUE; |
| return SUCCESS; |
| } |
| |
| // if it is INSERT/UPDATE/DELETE statement and table/MV had initialized |
| // ON STATEMENT MV on it - recompile |
| if (iud && |
| siBitmap.getInitOnStmtMvOnMe() != rcbBitmap.getInitOnStmtMvOnMe()) |
| { |
| if (returnRecompWarn()) |
| diagsArea << DgSqlCode(EXE_SIM_CHECK_FAILED) |
| << DgString0("On statement MVs have been removed."); |
| simCheckFailed = TRUE; |
| return SUCCESS; |
| } |
| |
| // if it is INSERT/UPDATE/DELETE statement and table/MV logging required |
| // bit was changed - recompile |
| if (iud && siBitmap.getLoggingRequired() != rcbBitmap.getLoggingRequired()) |
| { |
| if (rcbBitmap.getLoggingRequired()) |
| { |
| if (returnRecompWarn()) |
| diagsArea << DgSqlCode(EXE_SIM_CHECK_FAILED) |
| << DgString0("Logging is required."); |
| } |
| else |
| { |
| if (returnRecompWarn()) |
| diagsArea << DgSqlCode(EXE_SIM_CHECK_FAILED) |
| << DgString0("Logging is not required any more."); |
| } |
| |
| simCheckFailed = TRUE; |
| return SUCCESS; |
| } |
| |
| // if MV enable rewrite bit was change to disable rewrite - recompile |
| if (siBitmap.getIsEnableRewrite() && !rcbBitmap.getIsEnableRewrite()) |
| { |
| if (returnRecompWarn()) |
| diagsArea << DgSqlCode(EXE_SIM_CHECK_FAILED) |
| << DgString0("MV rewrite was disabled."); |
| simCheckFailed = TRUE; |
| return SUCCESS; |
| } |
| |
| // if MV audit bits were changed, but not between the values |
| // AUDIT <--> AUDITONREFRESH - recompile |
| ComMvAuditType siMvAuditType = siBitmap.getMvAuditType(); |
| ComMvAuditType rcbMvAuditType = rcbBitmap.getMvAuditType(); |
| if (siMvAuditType != rcbMvAuditType && |
| !((siMvAuditType == COM_MV_NO_AUDIT_ON_REFRESH && |
| rcbMvAuditType == COM_MV_AUDIT) || |
| (rcbMvAuditType == COM_MV_NO_AUDIT_ON_REFRESH && |
| siMvAuditType == COM_MV_AUDIT))) |
| { |
| if (returnRecompWarn()) |
| diagsArea << DgSqlCode(EXE_SIM_CHECK_FAILED) |
| << DgString0("MV audit status was changed."); |
| simCheckFailed = TRUE; |
| return SUCCESS; |
| } |
| |
| |
| // if table insertlog bit was changed - recompile |
| if (iud && siBitmap.getIsInsertLog() != rcbBitmap.getIsInsertLog()) |
| { |
| if (returnRecompWarn()) |
| diagsArea << DgSqlCode(EXE_SIM_CHECK_FAILED) |
| << DgString0("Table INSERTLOG attribute was changed."); |
| simCheckFailed = TRUE; |
| return SUCCESS; |
| } |
| |
| // if statement is INSERT/UPDATE/DELETE on table and logging is required |
| // and range log bits was changed, but not between the values |
| // NO RANGELOG <--> MIX RANGELOG - recompile |
| ComRangeLogType siRangeLogType = siBitmap.getRangeLogType(); |
| ComRangeLogType rcbRangeLogType = rcbBitmap.getRangeLogType(); |
| if (iud && |
| siBitmap.getLoggingRequired() && |
| siRangeLogType != rcbRangeLogType && |
| !((siRangeLogType == COM_NO_RANGELOG && |
| rcbRangeLogType == COM_MIXED_RANGELOG) || |
| (rcbRangeLogType == COM_NO_RANGELOG && |
| siRangeLogType == COM_MIXED_RANGELOG)) |
| ) |
| { |
| if (returnRecompWarn()) |
| diagsArea << DgSqlCode(EXE_SIM_CHECK_FAILED) |
| << DgString0("Table RANGELOG attribute was changed."); |
| simCheckFailed = TRUE; |
| return SUCCESS; |
| } |
| |
| // MV is unavailable |
| if (rcbBitmap.getMvStatus() == COM_MVSTATUS_UNAVAILABLE) |
| { |
| if (returnRecompWarn()) |
| diagsArea << DgSqlCode(EXE_SIM_CHECK_FAILED) |
| << DgString0("Materialized view is not available."); |
| simCheckFailed = TRUE; |
| return SUCCESS; |
| } |
| |
| simCheckFailed = FALSE; |
| return SUCCESS; |
| } |
| |
| // Discover whether there is already a transaction active |
| // so that we can avoid starting one when we call CatMapGetCatalogVisibilty, |
| // CatMapAnsiNameToGuardianName, or other recursive CLI calls. |
| NABoolean Statement::implicitTransNeeded(void) |
| { |
| const NABoolean noTransInProgress = |
| (context_->getTransaction()->xnInProgress() == FALSE); |
| if (noTransInProgress) |
| anyTransWasStartedByMe_ = TRUE; |
| return noTransInProgress; |
| } |
| |
| // If autocommit is on, disable it, prior to making recursive CLI calls. |
| // Remember if we disabled autoCommit so we can enable before returning |
| // from this method. |
| void Statement::turnOffAutoCommit(void) |
| { |
| if (context_->getTransaction()->autoCommit()) |
| { |
| autoCommitCleared_ = TRUE; |
| context_->getTransaction()->disableAutoCommit(); |
| } |
| } |
| |
| |
| // Turn back on autoCommit if we had turned it off. Maintain the |
| // state variable so the statement can be reexecuted. |
| void Statement::resetAutoCommit(void) |
| { |
| if (autoCommitCleared_) |
| { |
| autoCommitCleared_ = FALSE; |
| context_->getTransaction()->enableAutoCommit(); |
| } |
| } |
| void Statement::saveTmodeValues(void) |
| { |
| ExTransaction *runTrans = context_->getTransaction(); |
| |
| if (runTrans) |
| { |
| savedRoVal_ = runTrans->getROVal(); |
| savedRbVal_ = runTrans->getRBVal(); |
| savedAiVal_ = runTrans->getAIVal(); |
| } |
| } |
| |
| void Statement::resetTmodeValues(void) |
| { |
| ExTransaction * runTrans = context_->getTransaction(); |
| |
| if (runTrans) |
| { |
| |
| runTrans->updateROVal(savedRoVal_); |
| runTrans->updateRBVal(savedRbVal_); |
| runTrans->updateAIVal(savedAiVal_); |
| } |
| } |
| |
| // Commit any transaction that we started. Turn back on autoCommit if we |
| // had turned it off. Maintain the state variables so the statement can be |
| // reexecuted. |
| void Statement::commitImplicitTransAndResetTmodes(void) |
| { |
| if (anyTransWasStartedByMe_) |
| { |
| anyTransWasStartedByMe_ = FALSE; |
| if (context_->getTransaction()->xnInProgress()) |
| { |
| // Don't care about any error -- this was a readonly transaction. |
| context_->getTransaction()->commitTransaction(); |
| } |
| } |
| resetAutoCommit(); |
| resetTmodeValues(); |
| } |
| |
| |
| ExStatisticsArea *Statement::getStatsArea() |
| { |
| if (getGlobals()) |
| return getGlobals()->getStatsArea(); |
| return NULL; |
| } |
| |
| ExStatisticsArea *Statement::getOrigStatsArea() |
| { |
| if (getGlobals()) |
| return getGlobals()->getOrigStatsArea(); |
| return NULL; |
| } |
| |
| ExStatisticsArea *Statement::getCompileStatsArea() |
| { |
| ExStatisticsArea *stats; |
| if ((stats = getStatsArea()) != NULL) |
| return stats; |
| if (compileStatsArea_ != NULL) |
| return compileStatsArea_; |
| if (stmtStats_ == NULL || stmtStats_->getMasterStats() == NULL) |
| return NULL; |
| compileStatsArea_ = new (&heap_) |
| ExStatisticsArea(&heap_, 0, getRootTdb()->getCollectStatsType(), |
| getRootTdb()->getCollectStatsType()); |
| ExMasterStats *masterStats = new(&heap_) ExMasterStats(&heap_); |
| masterStats->copyContents(stmtStats_->getMasterStats()); |
| masterStats->setCollectStatsType(getRootTdb()->getCollectStatsType()); |
| compileStatsArea_->setMasterStats(masterStats); |
| return compileStatsArea_; |
| } |
| |
| void Statement::setStmtStats(NABoolean autoRetry) |
| { |
| StatsGlobals *statsGlobals = NULL; |
| int error; |
| NABoolean stmtStatsRetained = FALSE; |
| StmtStats *stmtStats = NULL; |
| if (stmtStats_ != NULL) |
| { |
| if (getRootTdb()) |
| assignRootTdb (NULL); |
| } |
| statsGlobals = cliGlobals_->getStatsGlobals(); |
| if (statsGlobals != NULL) |
| { |
| error = statsGlobals->getStatsSemaphore(cliGlobals_->getSemId(), |
| cliGlobals_->myPin()); |
| if (autoRetry) |
| { |
| if (getUniqueStmtId() != NULL) |
| { |
| stmtStats = statsGlobals->getMasterStmtStats(getUniqueStmtId(), getUniqueStmtIdLen(), |
| RtsQueryId::ANY_QUERY_); |
| ex_assert(stmtStats, "AQR but missing stmtStats."); |
| stmtStatsRetained = TRUE; |
| } |
| } |
| else |
| { |
| // If the same statement handle is used to prepare the query again |
| // stmtStats_ would have been set, remove the query in that case |
| if (stmtStats_ != NULL) |
| { |
| if (stmtStats_->getMasterStats() != NULL) |
| { |
| stmtStats_->getMasterStats()->setStmtState(Statement::DEALLOCATED_); |
| stmtStats_->getMasterStats()->setEndTimes(! stmtStats_->aqrInProgress()); |
| } |
| // Make a copy of stmtStats_ pointer so that in case it is retained we could use |
| // the same stmtStats- |
| stmtStats = stmtStats_; |
| stmtStatsRetained = statsGlobals->removeQuery(cliGlobals_->myPin(), stmtStats_); |
| } |
| } |
| if (getUniqueStmtId() != NULL) |
| { |
| if (! stmtStatsRetained) |
| { |
| stmtStats_ = statsGlobals->addQuery(cliGlobals_->myPin(), |
| getUniqueStmtId(), |
| getUniqueStmtIdLen(), (void *)this, (Lng32)-1, |
| source_str, source_length, TRUE); |
| ex_assert(stmtStats_, "StmtStats_ is null after addQuery"); |
| } |
| else |
| { |
| ExStatisticsArea *myStats = stmtStats->getStatsArea(); |
| stmtStats->reuse((void *)this); |
| getGlobals()->setStatsArea(NULL); |
| if (myStats != NULL && context_->getStats() == myStats) |
| context_->setStatsArea(NULL, FALSE, FALSE, FALSE); |
| stmtStats_ = stmtStats; |
| } |
| // update the parentQid |
| char *parentQid = getParentQid(); |
| char *parentQidSystem = getParentQidSystem(); |
| ULng32 parentQidSystemLen; |
| if (parentQidSystem != NULL) |
| parentQidSystemLen = str_len(parentQidSystem); |
| else |
| parentQidSystemLen = 0; |
| if (parentQid != NULL) |
| stmtStats_->setParentQid(parentQid, str_len(parentQid), parentQidSystem, |
| parentQidSystemLen, cliGlobals_->myCpu(), (short)cliGlobals_->myNodeNumber()); |
| else |
| stmtStats_->setParentQid(NULL, 0, NULL, 0, cliGlobals_->myCpu(), (short)cliGlobals_->myNodeNumber()); |
| } |
| else |
| stmtStats_ = NULL; |
| statsGlobals->releaseStatsSemaphore(cliGlobals_->getSemId(), cliGlobals_->myPin()); |
| } |
| else |
| { |
| if (stmtStats_ != NULL) |
| { |
| NADELETE(stmtStats_, StmtStats, stmtStats_->getHeap()); |
| stmtStats_ = NULL; |
| } |
| // runtime stats not supported on NT platform or runtime stats |
| // subsystem not up and running. |
| // Allocate ExMasterStats without going thru StatsGlobals. |
| stmtStats_ = StatsGlobals::addStmtStats(stmtHeap(), |
| cliGlobals_->myPin(), |
| getUniqueStmtId(), |
| getUniqueStmtIdLen(), |
| source_str, source_length); |
| } |
| } |
| |
| // VO, Plan Versioning Support. |
| void Statement::issuePlanVersioningWarnings (ComDiagsArea & diagsArea) |
| { |
| if (returnRecompWarn()) |
| { |
| if (fetchErrorCode_ != VERSION_NO_ERROR) |
| { |
| // A plan versioning error was detected at fetch time |
| diagsArea << DgSqlCode (fetchErrorCode_) |
| << DgInt0 (fetchPlanVersion_) |
| << DgString0 (fetchNode_) |
| << DgInt1 (fetchSupportedVersion_); |
| } |
| |
| if (mxcmpErrorCode_ != VERSION_NO_ERROR) |
| { |
| // A plan versioning error was detected at prepare time |
| diagsArea << DgSqlCode (mxcmpErrorCode_) |
| << DgInt0 (mxcmpStartedVersion_) |
| << DgInt1 (COM_VERS_COMPILER_VERSION); |
| } |
| } |
| |
| // Unconditionally clear the saved warning codes, even though we |
| // may not have reported them - we don't want them hanging around |
| // if we recompile for some other reason. |
| fetchErrorCode_ = VERSION_NO_ERROR; |
| mxcmpErrorCode_ = VERSION_NO_ERROR; |
| } |
| const char *Statement::stmtState(State state) |
| { |
| switch (state) |
| { |
| case INITIAL_: return "INITIAL"; |
| case OPEN_: return "OPEN"; |
| case EOF_: return "EOF"; |
| case CLOSE_: return "CLOSE"; |
| case DEALLOCATED_: return "DEALLOCATED"; |
| case FETCH_: return "FETCH"; |
| case CLOSE_TABLES_: return "CLOSE_TABLES"; |
| case PREPARE_: return "PREPARE"; |
| case PROCESS_ENDED_: return "PROCESS_ENDED"; |
| case RELEASE_TRANS_: return "RELEASE_TRANS"; |
| case SUSPENDED_: return "SUSPENDED"; |
| case STMT_EXECUTE_: return "EXECUTE"; |
| case STMT_FIXUP_: return "FIXUP"; |
| default: return ComRtGetUnknownString((Int32) state); |
| } |
| } |
| |
| ExRsInfo *Statement::getResultSetInfo() const |
| { |
| if (statementGlobals_) |
| return statementGlobals_->getResultSetInfo(); |
| return NULL; |
| } |
| |
| ExRsInfo *Statement::getOrCreateResultSetInfo() |
| { |
| ex_assert(statementGlobals_, "No statement globals available"); |
| return statementGlobals_->getResultSetInfo(TRUE); |
| } |
| |
| // For stored procedure result set proxy statements, return TRUE if |
| // the current statement source matches the newSource input string. |
| NABoolean Statement::rsProxyCompare(const char *newSource) const |
| { |
| NABoolean result = FALSE; |
| |
| if (newSource != NULL && source_str != NULL && |
| charset_ == SQLCHARSETCODE_ISO88591 && |
| str_cmp_ne(source_str, newSource) == 0) |
| { |
| result = TRUE; |
| } |
| |
| return result; |
| } |
| |
| // For stored procedure result set proxy statements, see if a |
| // prepare of proxy syntax is required and if so, do the internal |
| // prepare |
| RETCODE Statement::rsProxyPrepare(ExRsInfo &rsInfo, // IN |
| ULng32 rsIndex, // IN |
| ComDiagsArea &diagsArea) // INOUT |
| { |
| StmtDebug3("[BEGIN rsProxyPrepare] %p, rsIndex %u, stmt state %s", |
| this, rsIndex, stmtState(getState())); |
| |
| RETCODE result = SUCCESS; |
| |
| // These variables will be populated below by the call to |
| // getRsInfo() |
| NABoolean preparedFlag = FALSE; |
| Statement *proxyStmt = NULL; |
| NABoolean openFlag = FALSE; |
| NABoolean closedFlag = FALSE; |
| |
| // We have the ability to acquire proxy strings from the |
| // environment. The mechanism is currently disabled. See comments in |
| // the getProxySyntaxFromEnvironment() function (in this file) for |
| // more info. |
| const char *proxySyntax = NULL; |
| |
| if (proxySyntax) |
| { |
| StmtDebug0(" *** WILL USE PROXY SYNTAX FROM AN ENV VAR"); |
| } |
| else |
| { |
| NABoolean found = rsInfo.getRsInfo(rsIndex, proxyStmt, proxySyntax, |
| openFlag, closedFlag, preparedFlag); |
| } |
| |
| StmtDebug2(" rsIndex %u, prepared flag %d", rsIndex, (Int32) preparedFlag); |
| StmtDebug1(" proxySyntax [%s]", (proxySyntax ? proxySyntax : "(NULL)")); |
| |
| if (proxySyntax == NULL) |
| { |
| diagsArea << DgSqlCode(-EXE_UDR_RS_NOT_AVAILABLE) |
| << DgInt0((Int32) rsIndex); |
| result = ERROR; |
| } |
| |
| if (result != ERROR) |
| { |
| if (!preparedFlag) |
| { |
| NABoolean okToReusePlan = FALSE; |
| if (stmt_state != INITIAL_ && root_tdb != NULL) |
| { |
| // This means we currently have a plan. Next step is to decide |
| // if it suitable for reuse. |
| if (rsProxyCompare(proxySyntax)) |
| okToReusePlan = TRUE; |
| } |
| |
| if (!okToReusePlan) |
| { |
| // $$$$ PROXY RECOMPILE: We can consider returning a new |
| // warning condition when a proxy is prepared. This may help |
| // CLI callers avoid doing describes on a proxy after every |
| // CALL execution. |
| result = prepare((char *) proxySyntax, diagsArea, NULL, 0L); |
| } |
| |
| if (result != ERROR) |
| { |
| rsInfo.setPrepared(rsIndex); |
| } |
| else |
| { |
| // Add a condition to the diags area indicating it was an |
| // internal operation that failed unexpectedly |
| diagsArea << DgSqlCode(-EXE_UDR_RS_ALLOC_INTERNAL_ERROR); |
| } |
| } |
| } |
| |
| StmtDebug3("[END rsProxyPrepare] %p, stmt state %s, retcode %s", |
| this, stmtState(getState()), RetcodeToString(result)); |
| |
| return result; |
| } |
| |
| // Helper function to convert a char string to quoted char string. |
| // Also, single quote chars will be replaced with two single quote chars. |
| static |
| char* toQuotedString(char *src, Lng32 srcLen, NAHeap *heap) |
| { |
| // Allocate a buffer to fix the resultant string. |
| char *target = new (heap) char[2 * srcLen + 3]; |
| char *targetPtr = target; |
| |
| *targetPtr++ = '\''; // beginning quote |
| |
| for (Lng32 pos = 0; pos < srcLen; pos++) |
| { |
| *targetPtr++ = src[pos]; |
| |
| if (src[pos] == '\'') |
| *targetPtr++ = '\''; |
| } |
| *targetPtr++ = '\''; // ending quote |
| *targetPtr = '\0'; // NULL terminator |
| |
| return target; |
| } |
| |
| // getProxySyntax generates the text of a SELECT statement that we |
| // call "proxy syntax". The two uses of these SELECTs are stored |
| // procedure result sets and parallel extract consumer queries. |
| // |
| // The result set proxy syntax is of the form: |
| // |
| // SELECT * FROM TABLE ( SP_RESULT_SET ( <col-type-spec>, |
| // <col-type-spec>, ... ) ) |
| // |
| // where <col-type-spec> is in the format |
| // <cat>.<sch>.<table>.<colname> datatype [HEADING '<heading>'] [NOT NULL] |
| // |
| // The extract consumer syntax is of the form: |
| // |
| // SELECT * FROM TABLE ( EXTRACT_SOURCE ( 'ESP', 'esp-process-handle', |
| // 'security-key', |
| // <col-type-spec>, |
| // <col-type-spec>, ... ) ) |
| // |
| // The getProxySyntax function takes a prefix and suffix as input and |
| // generates the string: |
| // |
| // <prefix> <col-type-spec>, ... <suffix> |
| // |
| // In addition to generating proxy syntax, this method also computes |
| // the size of the proxy syntax string. |
| // |
| // This method first assumes that 'proxy' with 'maxlength' size |
| // is sufficient for holding proxy syntax. If it realizes that |
| // 'proxy' does not have enough space then the copying stops but the |
| // function proceeds to compute the required size |
| // |
| // The size of proxy syntax will be returned in spaceRequired. |
| // |
| RETCODE Statement::getProxySyntax(char *proxy, Lng32 maxlength, |
| Lng32 *spaceRequired, |
| const char *prefix, const char *suffix) |
| { |
| // In the code below, once we set hasEnoughSpace to FALSE, |
| // it will never be changed to TRUE again. |
| NABoolean hasEnoughSpace = TRUE; |
| |
| // If proxy is NULL, we only compute the size of proxy syntax |
| // if spaceRequired is provided. |
| if (!proxy) hasEnoughSpace = FALSE; |
| |
| if (!proxy && ! spaceRequired) |
| return SUCCESS; |
| |
| InputOutputExpr *expr = getRootTdb()->outputExpr(); |
| if (expr == NULL) |
| { |
| if (spaceRequired) *spaceRequired = 0; |
| return SUCCESS; |
| } |
| |
| SQLMODULE_ID mod_id; |
| SQLDESC_ID desc_id; |
| |
| mod_id.module_name = 0; |
| desc_id.name_mode = desc_handle; |
| desc_id.identifier = 0; |
| desc_id.handle = 0; |
| desc_id.module = &mod_id; |
| |
| Descriptor *outdesc = new (&heap_) Descriptor(&desc_id, |
| expr->getNumEntries(), |
| context_); |
| outdesc->dealloc(); |
| outdesc->alloc(expr->getNumEntries()); |
| |
| // Describe the OUTPUT expression |
| UInt32 flags = 0; |
| expr->setIsODBC(flags, root_tdb->odbcQuery()); |
| expr->setInternalFormatIO(flags, |
| context_->getSessionDefaults()-> |
| getInternalFormatIO()); |
| expr->setIsDBTR(flags, |
| context_->getSessionDefaults()->getDbtrProcess()); |
| expr->describeOutput(outdesc, flags); |
| |
| // Now generate proxy syntax string |
| char *proxyPtr = proxy; |
| Lng32 spaceLeft = maxlength; |
| Lng32 spaceNeeded = 0; |
| |
| if (prefix == NULL) |
| prefix = ""; |
| if (suffix == NULL) |
| suffix = ""; |
| |
| Lng32 prefixLen = str_len(prefix); |
| Lng32 suffixLen = str_len(suffix); |
| |
| if (spaceLeft < prefixLen) hasEnoughSpace = FALSE; |
| spaceNeeded += prefixLen; |
| if (hasEnoughSpace) |
| { |
| str_cpy_all(proxyPtr, prefix, prefixLen); |
| proxyPtr += prefixLen; |
| spaceLeft -= prefixLen; |
| } |
| |
| for (Int32 entry = 1; entry <= expr->getNumEntries(); entry++) |
| { |
| char *name = NULL; |
| Lng32 nameLen; |
| |
| // get CATALOG attribute |
| outdesc->getDescItemPtr(entry, SQLDESC_CATALOG_NAME, &name, &nameLen); |
| if (name != NULL) |
| { |
| char *catName = ToAnsiIdentifier2(name, nameLen, &heap_); |
| Lng32 catLen = str_len(catName); |
| |
| // Write CATALOG attribute |
| if (spaceLeft < catLen + 1) hasEnoughSpace = FALSE; |
| spaceNeeded += (catLen + 1); |
| if (hasEnoughSpace) |
| { |
| str_cpy_all(proxyPtr, catName, catLen); |
| str_cpy_all(proxyPtr+catLen, ".", 1); |
| proxyPtr += (catLen + 1); |
| spaceLeft -= (catLen + 1); |
| } |
| heap_.deallocateMemory(catName); |
| } |
| |
| // get SCHEMA attribute |
| outdesc->getDescItemPtr(entry, SQLDESC_SCHEMA_NAME, &name, &nameLen); |
| if (name != NULL) |
| { |
| char *schName = ToAnsiIdentifier2(name, nameLen, &heap_); |
| Lng32 schLen = str_len(schName); |
| |
| // Write SCHEMA attribute |
| if (spaceLeft < schLen + 1) hasEnoughSpace = FALSE; |
| spaceNeeded += (schLen + 1); |
| if (hasEnoughSpace) |
| { |
| str_cpy_all(proxyPtr, schName, schLen); |
| str_cpy_all(proxyPtr+schLen, ".", 1); |
| proxyPtr += (schLen + 1); |
| spaceLeft -= (schLen + 1); |
| } |
| heap_.deallocateMemory(schName); |
| } |
| |
| // get TABLE attribute |
| outdesc->getDescItemPtr(entry, SQLDESC_TABLE_NAME, &name, &nameLen); |
| if (name != NULL) |
| { |
| char *tabName = ToAnsiIdentifier2(name, nameLen, &heap_); |
| Lng32 tabLen = str_len(tabName); |
| |
| // Write TABLE attribute |
| if (spaceLeft < tabLen + 1) hasEnoughSpace = FALSE; |
| spaceNeeded += (tabLen + 1); |
| if (hasEnoughSpace) |
| { |
| str_cpy_all(proxyPtr, tabName, tabLen); |
| str_cpy_all(proxyPtr+tabLen, ".", 1); |
| proxyPtr += (tabLen + 1); |
| spaceLeft -= (tabLen + 1); |
| } |
| |
| heap_.deallocateMemory(tabName); |
| } |
| |
| // get COLUMN Name attribute |
| outdesc->getDescItemPtr(entry, SQLDESC_NAME, &name, &nameLen); |
| if (name != NULL) |
| { |
| char *colName = ToAnsiIdentifier2(name, nameLen, &heap_); |
| Lng32 colLen = str_len(colName); |
| |
| // Write COLUMN attribute |
| if (spaceLeft < colLen + 1) hasEnoughSpace = FALSE; |
| spaceNeeded += (colLen + 1); |
| if (hasEnoughSpace) |
| { |
| str_cpy_all(proxyPtr, colName, colLen); |
| str_cpy_all(proxyPtr+colLen, " ", 1); |
| proxyPtr += (colLen + 1); |
| spaceLeft -= (colLen + 1); |
| } |
| |
| heap_.deallocateMemory(colName); |
| } |
| |
| // get Dataype TEXT FORMAT attribute |
| outdesc->getDescItemPtr(entry, SQLDESC_TEXT_FORMAT, &name, &nameLen); |
| if (name != NULL) |
| { |
| // Write Datatype attribute |
| if (spaceLeft < nameLen + 1) hasEnoughSpace = FALSE; |
| spaceNeeded += nameLen + 1; |
| if (hasEnoughSpace) |
| { |
| str_cpy_all(proxyPtr, name, nameLen); |
| str_cpy_all(proxyPtr + nameLen, " ", 1); |
| proxyPtr += (nameLen + 1); |
| spaceLeft -= (nameLen + 1); |
| } |
| } |
| |
| // get HEADING attribute |
| outdesc->getDescItemPtr(entry, SQLDESC_HEADING, &name, &nameLen); |
| if ((name != NULL) && (str_len(name) != 0)) |
| { |
| // quotedHeading will be quoted string format i.e., the string will be |
| // in single quotes and also any quotes in the string are doubled |
| char *quotedHeading = toQuotedString(name, nameLen, &heap_); |
| Lng32 quotedHeadLen = str_len(quotedHeading); |
| |
| // Write HEADING attribute |
| if (spaceLeft < quotedHeadLen + 18) hasEnoughSpace = FALSE; |
| spaceNeeded += (quotedHeadLen + 18); |
| if (hasEnoughSpace) |
| { |
| str_cpy_all(proxyPtr, "HEADING _ISO88591", 17); |
| str_cpy_all(proxyPtr+17, quotedHeading, quotedHeadLen); |
| str_cpy_all(proxyPtr+17+quotedHeadLen, " ", 1); |
| proxyPtr += (quotedHeadLen + 18); |
| spaceLeft -= (quotedHeadLen + 18); |
| } |
| |
| heap_.deallocateMemory(quotedHeading); |
| } |
| |
| // Write "NOT NULL" attribute |
| Lng32 nullable; |
| outdesc->getDescItem(entry, SQLDESC_NULLABLE, &nullable, NULL, 0, NULL, 0); |
| if (nullable == 0) |
| { |
| if (spaceLeft < 8) hasEnoughSpace = FALSE; |
| spaceNeeded += 8; |
| if (hasEnoughSpace) |
| { |
| str_cpy_all(proxyPtr, "NOT NULL", 8); |
| proxyPtr += 8; |
| spaceLeft -= 8; |
| } |
| } |
| |
| if (entry == expr->getNumEntries()) |
| { |
| // This is last entry, so append the suffix |
| if (spaceLeft < suffixLen) |
| hasEnoughSpace = FALSE; |
| spaceNeeded += suffixLen; |
| if (hasEnoughSpace) |
| { |
| str_cpy_all(proxyPtr, suffix, suffixLen); |
| proxyPtr += suffixLen; |
| spaceLeft -= suffixLen; |
| } |
| } |
| else |
| { |
| // Add ',' |
| if (spaceLeft < 2) hasEnoughSpace = FALSE; |
| spaceNeeded += 2; |
| if (hasEnoughSpace) |
| { |
| str_cpy_all(proxyPtr, ", ", 2); |
| proxyPtr += 2; |
| spaceLeft -= 2; |
| } |
| } |
| } |
| |
| // Set the required space size if asked. |
| if (spaceRequired) *spaceRequired = spaceNeeded; |
| |
| // Clean up |
| outdesc->dealloc(); |
| NADELETE(outdesc, Descriptor, &heap_); |
| |
| return SUCCESS; |
| } |
| |
| RETCODE Statement::getRSProxySyntax(char *proxy, Lng32 maxlength, |
| Lng32 *spaceRequired) |
| { |
| return getProxySyntax(proxy, maxlength, spaceRequired, |
| "SELECT * FROM TABLE ( SP_RESULT_SET ( ", " ) )"); |
| } |
| |
| RETCODE Statement::getExtractConsumerSyntax(char *proxy, Lng32 maxlength, |
| Lng32 *spaceRequired) |
| { |
| const char *prefix = |
| "SELECT * FROM TABLE(EXTRACT_SOURCE('ESP', '%s', '%s', "; |
| const char *suffix = ") )"; |
| return getProxySyntax(proxy, maxlength, spaceRequired, prefix, suffix); |
| } |
| |
| #ifdef _DEBUG |
| void Statement::StmtPrintf(const char *formatString, ...) const |
| { |
| if (!stmtDebug_) |
| return; |
| |
| FILE *f = stdout; |
| va_list args; |
| va_start(args, formatString); |
| fprintf(f, "[STMT] "); |
| vfprintf(f, formatString, args); |
| fprintf(f, "\n"); |
| fflush(f); |
| } |
| #endif |
| /****************************************************************************** |
| Method: Statement::doesUninitializedMvExist |
| |
| Description: |
| Any time the statement is executed, check the list to see if |
| any mvs are still uninitialized. If they are initialized, remove |
| them from the list. |
| |
| If one uninitialized mv is found, return immediately with error |
| since this statement cannot be executed. |
| |
| Parameters: |
| char **pMvName (OUTPUT) |
| - this parameter will be set to the name of the uninitialized |
| mv if it is found. |
| |
| ComDiagsArea &diagsArea |
| - diags area for error messages |
| |
| Return: |
| - return TRUE if an uninitialized mv is found |
| |
| ******************************************************************************/ |
| NABoolean |
| Statement::doesUninitializedMvExist( char **pMvName, |
| ComDiagsArea &diagsArea ) |
| { |
| NABoolean bUninitializedMvExists = FALSE; // assume none exist |
| |
| UninitializedMvName *uninitializedMvList = NULL; |
| const short uninitializedMvCount = root_tdb->uninitializedMvCount(); |
| |
| uninitializedMvList = root_tdb->uninitializedMvList(); |
| |
| // if the list is NULL, the return value will show that none exist. |
| if( uninitializedMvList ) |
| { |
| // for each name in the uninitializedMvList |
| for( short i = 0; i < uninitializedMvCount; i++ ) |
| { |
| UninitializedMvName currentMv = uninitializedMvList[ i ]; |
| |
| if( isUninitializedMv( currentMv.getPhysicalName(), |
| currentMv.getAnsiName(), |
| diagsArea ) ) |
| { |
| // point output to name in root_tdb |
| (*pMvName) = uninitializedMvList[ i ].getAnsiName(); |
| bUninitializedMvExists = TRUE; |
| break; // exit loop since at least one is found |
| } |
| |
| } |
| } |
| return bUninitializedMvExists; |
| } |
| /****************************************************************************** |
| Method: Statement::isUninitializedMv |
| |
| Description: |
| Determine whether a table in the statement is an uninitialized mv. |
| This information is gathered from the ExRcb object. |
| |
| Parameters: |
| char * physicalName |
| - table location. used to get rfork info |
| |
| const char *ansiName |
| - table ansi name. used for error reporting |
| |
| ComDiagsArea &diagsArea |
| - diags area for error messages |
| |
| Return: |
| - return TRUE if this mv exists but is uninitialized. |
| |
| ******************************************************************************/ |
| NABoolean |
| Statement::isUninitializedMv( const char * physicalName, |
| const char * ansiName, |
| ComDiagsArea &diagsArea ) |
| { |
| return FALSE; |
| } |
| |
| void Statement::buildConsumerQueryTemplate() |
| { |
| if (extractConsumerQueryTemplate_ == NULL) |
| { |
| Lng32 len = 1000; |
| NABoolean done = FALSE; |
| Lng32 spaceRequired = 0; |
| while (!done) |
| { |
| |
| |
| // add one for NULL terminator |
| extractConsumerQueryTemplate_ = (char *)heap_.allocateMemory(len + 1); |
| |
| // Now we call a function to generate the consumer syntax string. |
| // The function returns a string WITHOUT a null terminator. The |
| // number of bytes required for the full string (not including |
| // the null terminator) is returned in the output parameter |
| // spaceRequired. |
| RETCODE rc = getExtractConsumerSyntax(extractConsumerQueryTemplate_, |
| len, |
| &spaceRequired); |
| ex_assert(rc == SUCCESS, |
| "Unexpected error from getExtractConsumerSyntax"); |
| |
| if (spaceRequired > len) |
| { |
| len = spaceRequired; |
| heap_.deallocateMemory(extractConsumerQueryTemplate_); |
| extractConsumerQueryTemplate_ = NULL; |
| } |
| else |
| { |
| extractConsumerQueryTemplate_[spaceRequired] = 0; |
| done = TRUE; |
| } |
| } |
| } |
| } |
| |
| Lng32 Statement::getConsumerQueryLen(ULng32 index) |
| { |
| // First build the consumer query template |
| buildConsumerQueryTemplate(); |
| |
| // Minimum length will include a null terminator so initialize the |
| // result to 1 |
| Lng32 result = 1; |
| |
| const char *tmpl = extractConsumerQueryTemplate_; |
| const char *phandle = statementGlobals_->getExtractEspPhandleText(index); |
| const char *key = statementGlobals_->getExtractSecurityKey(); |
| |
| if (phandle == NULL) |
| phandle = ""; |
| if (key == NULL) |
| key = ""; |
| |
| Lng32 reqdLen = str_len(tmpl) |
| + str_len(phandle) |
| + str_len(key) |
| - 4 // to account for the "%s" format specifiers |
| + 1; // to account for the null terminator |
| result = reqdLen; |
| |
| return result; |
| } |
| |
| void Statement::getConsumerQuery(ULng32 index, char *buf, Lng32 buflen) |
| { |
| if (buflen < 1) |
| return; |
| |
| // First build the consumer query template |
| buildConsumerQueryTemplate(); |
| |
| buf[0] = 0; |
| |
| const char *tmpl = extractConsumerQueryTemplate_; |
| const char *phandle = statementGlobals_->getExtractEspPhandleText(index); |
| const char *key = statementGlobals_->getExtractSecurityKey(); |
| |
| if (phandle == NULL) |
| phandle = ""; |
| if (key == NULL) |
| key = ""; |
| |
| Lng32 reqdLen = str_len(tmpl) |
| + str_len(phandle) |
| + str_len(key) |
| - 4 // to account for the "%s" format specifiers |
| + 1; // to account for the null terminator |
| ex_assert(buflen >= reqdLen, "Consumer query buffer too small"); |
| str_sprintf(buf, tmpl, phandle, key); |
| } |
| |
| Lng32 Statement::getConsumerCpu(ULng32 index) |
| { |
| Lng32 result = -1; |
| short cpu = statementGlobals_->getExtractEspCpu(index); |
| Lng32 nodeNumber = statementGlobals_->getExtractEspNodeNumber(index); |
| if (cpu >= 0 && nodeNumber >= 0) |
| { |
| result = (nodeNumber << 8); |
| result += (cpu & 0x00ff); |
| } |
| return result; |
| } |
| |
| |
| Lng32 Statement::setParentQid(char *queryId) |
| { |
| Lng32 len = 0; |
| if (queryId != NULL) |
| { |
| len = str_len(queryId); |
| if (len < ComSqlId::MIN_QUERY_ID_LEN) |
| return -CLI_INVALID_ATTR_VALUE; |
| if (len < 4 || (len > 4 && str_cmp((const char *)queryId, COM_SESSION_ID_PREFIX, 4) != 0)) |
| return -CLI_INVALID_ATTR_VALUE; |
| } |
| if (parentQid_) |
| NADELETEBASIC(parentQid_, &heap_); |
| if (queryId != NULL) |
| { |
| parentQid_ = new(&heap_) char[len+1]; |
| str_cpy_all(parentQid_, queryId, len); |
| parentQid_[len] = '\0'; |
| } |
| else |
| { |
| parentQid_ = NULL; |
| len = 0; |
| } |
| if (stmtStats_ && stmtStats_->getMasterStats() != NULL) |
| stmtStats_->getMasterStats()->setParentQid(parentQid_, len); |
| return 0; |
| } |
| |
| void Statement::setParentQidSystem(char *parentQidSystem) |
| { |
| Lng32 len = 0; |
| if (parentQidSystem != NULL) |
| { |
| len = str_len(parentQidSystem); |
| str_cpy_all(parentQidSystem_, parentQidSystem, len); |
| parentQidSystem_[len] = '\0'; |
| } |
| else |
| parentQidSystem_[0] = '\0'; |
| if (stmtStats_ && stmtStats_->getMasterStats() != NULL) |
| stmtStats_->getMasterStats()->setParentQidSystem(parentQidSystem_, len); |
| } |
| |
| char *Statement::getParentQid() |
| { |
| char *parentQid; |
| if (parentQid_ != NULL) |
| parentQid = parentQid_; |
| else |
| { |
| if (context_->getSessionDefaults()) |
| parentQid = context_->getSessionDefaults()->getParentQid(); |
| else |
| parentQid = NULL; |
| } |
| return parentQid; |
| } |
| |
| char *Statement::getParentQidSystem() |
| { |
| char *parentQidSystem; |
| if (parentQidSystem_[0] != '\0') |
| parentQidSystem = parentQidSystem_; |
| else |
| { |
| if (context_->getSessionDefaults()) |
| parentQidSystem = context_->getSessionDefaults()->getParentQidSystem(); |
| else |
| parentQidSystem = NULL; |
| } |
| return parentQidSystem; |
| } |
| |
| Int64 Statement::getExeStartTime() |
| { |
| ExStatisticsArea *statsArea; |
| ExMasterStats *masterStats; |
| statsArea = getStatsArea(); |
| |
| if (statsArea != NULL && (masterStats = statsArea->getMasterStats()) != NULL) |
| return masterStats->getExeStartTime(); |
| else |
| return -1; |
| } |
| |
| void Statement::setExeStartTime(Int64 exeStartTime) |
| { |
| if (exeStartTime != -1) |
| aqrInitialExeStartTime_ = exeStartTime; |
| } |
| |
| Lng32 Statement::initStrTarget(SQLDESC_ID * sql_source, |
| ContextCli &currContext, |
| ComDiagsArea &diags, |
| StrTarget &strTarget) |
| { |
| if (! sql_source) |
| return 0; |
| |
| if (sql_source->name_mode == string_data) |
| { |
| CharInfo::CharSet externalCharset = |
| CharInfo::getCharSetEnum(sql_source->charset); |
| CharInfo::CharSet internalCharset = CharInfo::UTF8; |
| ComDiagsArea *diagsPtr = &diags; |
| |
| |
| strTarget.init((char*)sql_source->identifier, |
| sql_source->identifier_len, |
| CharInfo::getFSTypeFixedChar(externalCharset), |
| externalCharset, |
| internalCharset, |
| currContext.exCollHeap(), |
| diagsPtr); |
| } |
| else |
| { |
| Descriptor * desc = currContext.getDescriptor(sql_source); |
| |
| /* descriptor must exist */ |
| if (!desc) |
| { |
| diags << DgSqlCode(-CLI_DESC_NOT_EXISTS); |
| return -CLI_DESC_NOT_EXISTS; |
| } |
| |
| strTarget.init(desc, 1); |
| } |
| |
| if (!strTarget.getStr()) |
| { |
| diags << DgSqlCode(-CLI_STMT_NOT_PREPARED); |
| return -CLI_STMT_NOT_PREPARED; |
| } |
| copyInSourceStr(strTarget.getStr(), (octetLen(strTarget.getStr(), strTarget.getIntCharSet())), strTarget.getIntCharSet()); |
| return 0; |
| } |
| |
| NABoolean Statement::updateChildQid() |
| { |
| NABoolean parentIsCanceled = FALSE; |
| StatsGlobals *statsGlobals = cliGlobals_->getStatsGlobals(); |
| |
| if (statsGlobals != NULL && uniqueStmtId_ != NULL && parentQid_ != NULL && |
| getStatsArea() != NULL && stmtStats_ != NULL && stmtStats_->updateChildQid()) |
| { |
| int error = statsGlobals->getStatsSemaphore(cliGlobals_->getSemId(), |
| cliGlobals_->myPin()); |
| StmtStats *ss = statsGlobals->getMasterStmtStats(parentQid_, str_len(parentQid_), RtsQueryId::ANY_QUERY_); |
| if (ss != NULL) |
| { |
| ExMasterStats *parentMasterStats = ss->getMasterStats(); |
| if (parentMasterStats) |
| { |
| parentMasterStats->setChildQid(uniqueStmtId_, uniqueStmtIdLen_); |
| if (parentMasterStats->getCanceledTime() != -1) |
| parentIsCanceled = TRUE; |
| } |
| } |
| statsGlobals->releaseStatsSemaphore(cliGlobals_->getSemId(),cliGlobals_->myPin()); |
| } |
| return parentIsCanceled; |
| } |
| /* |
| // make the context's stats point to this statement's stats. |
| // Context points to the most recent executed statement's |
| // stat area so later a getStatistics CLI call without a |
| // statement ID can return it. |
| */ |
| void Statement::updateStatsAreaInContext() |
| { |
| ExStatisticsArea *statsArea; |
| ComTdb::CollectStatsType statsType; |
| ExMasterStats *masterStats; |
| statsArea = getStatsArea(); |
| if (stmtStats_ != NULL && |
| statsArea && |
| ((statsType = statsArea->getCollectStatsType()) != (Int32)SQLCLIDEV_NO_STATS) && |
| stmt_type == Statement::DYNAMIC_STMT) |
| { |
| if (statsType == (Int32)SQLCLIDEV_ALL_STATS) |
| { |
| if ((masterStats = statsArea->getMasterStats()) != NULL) |
| { |
| NADELETE(masterStats, ExMasterStats, masterStats->getHeap()); |
| statsArea->setMasterStats(NULL); |
| } |
| } |
| if (statsArea->getMasterStats() == NULL) |
| { |
| ExMasterStats * ems = new(context_->exHeap()) |
| ExMasterStats(context_->exHeap()); |
| if (stmtStats_->getMasterStats()) |
| ems->copyContents(stmtStats_->getMasterStats()); |
| ems->setCollectStatsType(getRootTdb()->getCollectStatsType()); |
| statsArea->setMasterStats(ems); |
| } |
| context_->setStatsArea(getStatsArea(), FALSE, (cliGlobals_->getStatsGlobals() != NULL)); |
| } |
| } |
| |
| Lng32 Statement::setChildQueryInfo(ComDiagsArea *diagsArea, char * uniqueQueryId, |
| Lng32 uniqueQueryIdLen, |
| SQL_QUERY_COST_INFO *query_cost_info, |
| SQL_QUERY_COMPILER_STATS_INFO *comp_stats_info) |
| { |
| NAHeap *heap = stmtHeap(); |
| |
| if (uniqueQueryId == NULL || query_cost_info == NULL || comp_stats_info == NULL) |
| { |
| if (diagsArea != NULL) |
| *diagsArea << DgSqlCode(-CLI_INVALID_ATTR_VALUE); |
| return ERROR; |
| } |
| if (childQueryId_ != NULL) |
| { |
| NADELETEBASIC(childQueryId_, heap); |
| } |
| childQueryId_ = new (heap) char[uniqueQueryIdLen+1]; |
| str_cpy_all(childQueryId_, uniqueQueryId, uniqueQueryIdLen); |
| childQueryId_[uniqueQueryIdLen] = '\0'; |
| childQueryIdLen_ = uniqueQueryIdLen; |
| if (childQueryCostInfo_ != NULL) |
| { |
| NADELETEBASIC(childQueryCostInfo_, heap); |
| } |
| childQueryCostInfo_ = new (heap) SQL_QUERY_COST_INFO; |
| str_cpy_all((char *)childQueryCostInfo_, (const char *)query_cost_info, sizeof(SQL_QUERY_COST_INFO)); |
| if (childQueryCompStatsInfo_ != NULL) |
| { |
| NADELETEBASIC(childQueryCompStatsInfo_, heap); |
| } |
| childQueryCompStatsInfo_ = new (heap) SQL_QUERY_COMPILER_STATS_INFO; |
| str_cpy_all((char *)childQueryCompStatsInfo_, (const char *)comp_stats_info, sizeof(SQL_QUERY_COMPILER_STATS_INFO)); |
| return SUCCESS; |
| } |
| |
| Lng32 Statement::getChildQueryInfo(ComDiagsArea &diagsArea, char * uniqueQueryId, |
| Lng32 uniqueQueryIdMaxLen, |
| Lng32 * uniqueQueryIdLen, |
| SQL_QUERY_COST_INFO *query_cost_info, |
| SQL_QUERY_COMPILER_STATS_INFO *comp_stats_info) |
| { |
| if (childQueryId_ == NULL) |
| { |
| diagsArea << DgSqlCode(-EXE_RTS_QID_NOT_FOUND); |
| return ERROR; |
| } |
| if (childQueryIdLen_ > uniqueQueryIdMaxLen || uniqueQueryId == NULL || |
| uniqueQueryIdLen == NULL) |
| { |
| diagsArea << DgSqlCode(-CLI_BUFFER_TOO_SMALL); |
| return ERROR; |
| } |
| str_cpy_all(uniqueQueryId, (const char *)childQueryId_, childQueryIdLen_); |
| *uniqueQueryIdLen = childQueryIdLen_; |
| if (childQueryCostInfo_ != NULL && query_cost_info != NULL) |
| str_cpy_all((char *)query_cost_info, (const char *)childQueryCostInfo_, |
| sizeof(SQL_QUERY_COST_INFO)); |
| if (childQueryCompStatsInfo_ != NULL && comp_stats_info != NULL) |
| str_cpy_all((char *)childQueryCompStatsInfo_, (const char *)comp_stats_info, |
| sizeof(SQL_QUERY_COMPILER_STATS_INFO)); |
| return SUCCESS; |
| } |
| |