blob: dbefed0c8ab34ff4cee798f66f95d1a43b1e24b0 [file] [log] [blame]
/********************************************************************
//
// @@@ START COPYRIGHT @@@
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
// @@@ END COPYRIGHT @@@
********************************************************************/
/* -*-C++-*-
*****************************************************************************
*
* File: hs_cli.C
* Description: Classes for accessing tables using CLI calls.
* Created: 03/25/96
* Language: C++
*
*
*
*
*****************************************************************************
*/
#define SQLPARSERGLOBALS_FLAGS // must precede all #include's
#include <string.h>
#include <netdb.h>
#define HS_FILE "hs_cli"
#include "Platform.h"
#include "str.h"
#include "NumericType.h"
#include "CharType.h"
#include "DatetimeType.h"
#include "IntervalType.h"
#include "MiscType.h"
#include "Int64.h"
#include "cli_stdh.h"
#include "sql_id.h"
#include "ComMPLoc.h"
#include "CmpContext.h"
#include "CompException.h"
#include "hs_globals.h"
#include "hs_cli.h"
#include "hs_auto.h"
#include "hs_la.h"
#include "SqlParserGlobals.h"
#include "ComCextdecs.h"
#include "SchemaDB.h"
#include "exp_clause_derived.h" // Definition of convDoIt.
#include "NAString.h"
#include "NLSConversion.h"
#include "SqlciError.h"
#include "ExpErrorEnums.h"
#include "CmpSeabaseDDL.h" // call to createHistogramTables
#include "ComMisc.h" // to get ComTrafReservedColName
// -----------------------------------------------------------------------
// Class to deallocate statement and descriptor.
// -----------------------------------------------------------------------
class HSRefDesc {
public:
HSRefDesc( SQLSTMT_ID *pstmt
, SQLDESC_ID *pdesc
)
: ps_(pstmt), pd_(pdesc), allocatedS_(FALSE), allocatedD_(FALSE)
{}
~HSRefDesc()
{
Lng32 error;
if (allocatedD_)
{
error = SQL_EXEC_DeallocDesc(pd_);
if (error)
HSLogError(error);
}
if (allocatedS_)
{
error = SQL_EXEC_DeallocStmt(ps_);
if (error)
HSLogError(error);
}
}
Int32 allocatedS_, allocatedD_;
private:
SQLSTMT_ID *ps_;
SQLDESC_ID *pd_;
};
// -----------------------------------------------------------------------
// DESCRIPTION: Call SQL CLI to execute a SQL statement. The caller is
// responsible for all error checking and recovery.
// INPUTS: stmt = statement descriptor for the SQL query
// srcDesc = source descriptor pointing to the
// text string of SQL query.
// doPrintPlan = if true, the plan for the query will be
// printed after the statement is prepared but before
// execution; if false we do a single CLI ExecDirect call
// checkForMdam = if true, do a query on the Explain virtual table
// to see if MDAM was used in the query being executed, and
// display the result in the ulog.
// -----------------------------------------------------------------------
Lng32 HSExecDirect( SQLSTMT_ID * stmt
, SQLDESC_ID * srcDesc
, NABoolean doPrintPlan
, NABoolean checkForMdam
)
{
Lng32 retcode = 0;
HSLogMan *LM = HSLogMan::Instance();
if ((doPrintPlan || checkForMdam) && LM->LogNeeded())
{
retcode = SQL_EXEC_Prepare(stmt, srcDesc);
if (retcode >= 0) // ignore warnings
{
if (doPrintPlan)
printPlan(stmt);
if (checkForMdam)
checkMdam(stmt);
retcode = SQL_EXEC_ExecFetch(stmt,0,0);
}
}
else
{
retcode = SQL_EXEC_ExecDirect(stmt, srcDesc, 0, 0);
}
return retcode;
}
// -----------------------------------------------------------------------
// DESCRIPTION: Execute a standalone dml/ddl statement.
// INPUTS: dml = text string of SQL query.
// sqlcode = the error to issue upon failure, or HS_WARNING if
// errors should be suppressed.
// rowsAffected, srcTabRowCount = pointers (NULL by default) to
// variables that rowcount info for query will be stored in.
// errorToken = text string indicating major operation being
// executed (within ustats, ...). Used for err processing.
// tabDef = pointer to HSTableDef of table affected (only used
// when rowsAffected, srcTabRowCount are non NULL.
// errorToIgnore = sqlcode of an inconsequential expected error
// that should not disrupt execution, such as "schema already
// exists" when executing a Create Schema statement. 0 indicates
// there is no such expected error.
// checkMdam = if TRUE, determine whether the query uses MDAM, and
// include this information in the ulog.
// inactivateErrorCatcher = TRUE if the caller already has an
// HSErrorCatcher object active (that is, the caller wants
// to capture diagnostics itself).
// -----------------------------------------------------------------------
Lng32 HSFuncExecQuery( const char *dml
, short sqlcode
, Int64 *rowsAffected
, const char *errorToken
, Int64 *srcTabRowCount
, const HSTableDef *tabDef
, short errorToIgnore
, NABoolean checkMdam
, NABoolean inactivateErrorCatcher
)
{
Lng32 retcode;
// The HSErrorCatcher captures any diagnostics when it goes out-of-scope,
// unless it is inactivated (in which case it does nothing).
HSErrorCatcher errorCatcher(retcode, sqlcode, errorToken, TRUE,
inactivateErrorCatcher);
retcode = HSFuncExecQueryBody(dml,sqlcode,rowsAffected,errorToken,
srcTabRowCount,tabDef,errorToIgnore,checkMdam);
HSHandleError(retcode);
return retcode;
}
// -----------------------------------------------------------------------
// DESCRIPTION: This is the body of HSFuncExecQuery. It is pulled out
// as a separate function so it can also be used by
// HSFuncExecTransactionalQueryWithRetry. Each of the callers has its
// own HSErrorCatcher object to capture diagnostics.
// -----------------------------------------------------------------------
Lng32 HSFuncExecQueryBody( const char *dml
, short sqlcode
, Int64 *rowsAffected
, const char *errorToken
, Int64 *srcTabRowCount
, const HSTableDef *tabDef
, short errorToIgnore
, NABoolean checkMdam
)
{
HSLogMan *LM = HSLogMan::Instance();
HSGlobalsClass *hs_globals = GetHSContext();
if (!tabDef && hs_globals) tabDef = hs_globals->objDef;
if (dml == NULL)
{
return 0;
}
LM->Log(dml);
Lng32 retcode;
SQLMODULE_ID module;
SQLSTMT_ID stmt;
SQLDESC_ID srcDesc;
init_SQLMODULE_ID(&module);
init_SQLCLI_OBJ_ID(&stmt);
init_SQLCLI_OBJ_ID(&srcDesc);
HSRefDesc tmp(&stmt, &srcDesc);
module.module_name = 0;
module.module_name_len = 0;
// Allocate a SQL statement
if (LM->LogNeeded() && (rowsAffected != NULL)) {
init_SQLCLI_OBJ_ID(&stmt, SQLCLI_CURRENT_VERSION,
stmt_name, &module, HS_FUNC_EXEC_QUERY_STMTID, 0,
SQLCHARSETSTRING_ISO88591, (Lng32)strlen(HS_FUNC_EXEC_QUERY_STMTID), 0);
}
else {
stmt.name_mode = stmt_handle;
stmt.module = &module;
stmt.identifier = 0;
stmt.handle = 0;
}
retcode = SQL_EXEC_ClearDiagnostics(&stmt);
HSHandleError(retcode);
retcode = SQL_EXEC_AllocStmt(&stmt, 0);
HSHandleError(retcode);
tmp.allocatedS_ = TRUE;
// Allocate a descriptor which will hold the SQL statement source
srcDesc.name_mode = desc_handle;
srcDesc.module = &module;
module.module_name = 0;
module.module_name_len = 0;
srcDesc.identifier = 0;
srcDesc.handle = 0;
retcode = SQL_EXEC_AllocDesc(&srcDesc, 1);
HSHandleError(retcode);
tmp.allocatedD_ = TRUE;
retcode = SQL_EXEC_SetDescItem(&srcDesc, 1, SQLDESC_TYPE_FS,
REC_BYTE_V_ANSI, 0);
HSHandleError(retcode);
retcode = SQL_EXEC_SetDescItem(&srcDesc, 1, SQLDESC_VAR_PTR,
(Long)dml, 0);
HSHandleError(retcode);
retcode = SQL_EXEC_SetDescItem(&srcDesc, 1, SQLDESC_LENGTH,
strlen(dml) + 1, 0);
HSHandleError(retcode);
// SQLDESC_CHAR_SET must be the last descriptor item set, otherwise
// it may get reset by other calls to SQL_EXEC_SetDescItem().
NAString charSet = ActiveSchemaDB()->getDefaults().getValue(ISO_MAPPING);
NAString defCS = ActiveSchemaDB()->getDefaults().getValue(DEFAULT_CHARSET);
retcode = SQL_EXEC_SetDescItem(&srcDesc, 1, SQLDESC_CHAR_SET,
SQLCHARSETCODE_UTF8
, 0);
HSHandleError(retcode);
retcode = SQL_EXEC_SetStmtAttr(&stmt, SQL_ATTR_PARENT_QID, 0,
(char *)CmpCommon::context()->sqlSession()->getParentQid());
HSHandleError(retcode);
// execute immediate this statement
retcode = HSExecDirect(&stmt, &srcDesc, srcTabRowCount != 0, checkMdam);
// If retcode is > 0 or sqlcode is HS_WARNING, then set to 0 (no error/ignore).
if (retcode >= 0) retcode = 0;
// If sqlcode is HS_WARNING, then this means failures should be returned as
// warnings. So, don't call HSHandleError, but rather return 0. Also return
// 0 if we get an expected and inconsequential error.
if ((sqlcode == HS_WARNING && retcode < 0) || retcode == errorToIgnore)
retcode = 0;
else
HSHandleError(retcode);
if (rowsAffected != NULL && tabDef != NULL)
{
// set up a descriptor to get the # of rows affected from
// the SQL Diagnostics area
Lng32 sql_item = SQLDIAG_ROW_COUNT;
SQLDESC_ID rc_desc;
SQLMODULE_ID mod_id;
rc_desc.version = SQLCLI_CURRENT_VERSION;
rc_desc.name_mode = desc_handle;
rc_desc.module = &mod_id;
mod_id.module_name = 0;
rc_desc.identifier = 0;
rc_desc.handle = 0;
SQL_EXEC_AllocDesc(&rc_desc, 1);
SQL_EXEC_SetDescItem(&rc_desc, 1, SQLDESC_TYPE_FS,
REC_BIN64_SIGNED, 0);
SQL_EXEC_SetDescItem(&rc_desc, 1, SQLDESC_VAR_PTR,
(Long) rowsAffected, 0);
// SQLDESC_CHAR_SET must be the last descriptor item set, otherwise
// it may get reset by other calls to SQL_EXEC_SetDescItem().
NAString charSet = ActiveSchemaDB()->getDefaults().getValue(ISO_MAPPING);
NAString defCS = ActiveSchemaDB()->getDefaults().getValue(DEFAULT_CHARSET);
SQL_EXEC_SetDescItem(&rc_desc, 1, SQLDESC_CHAR_SET,
SQLCHARSETCODE_UTF8
, 0);
SQL_EXEC_GetDiagnosticsStmtInfo(&sql_item, &rc_desc);
SQL_EXEC_DeallocDesc(&rc_desc);
if (srcTabRowCount)
{
getRowCountFromStats(srcTabRowCount, tabDef) ;
}
}
return retcode;
}
// -----------------------------------------------------------------------
// DESCRIPTION: Execute a standalone dml/ddl statement within a
// locally-started and committed transaction, with retry.
// Many Trafodion errors abort the transaction; in order
// to retry statements we need to manage the transaction
// here as well.
// INPUTS: dml = text string of SQL query.
// sqlcode = the error to issue upon failure, or HS_WARNING if
// errors should be suppressed.
// rowsAffected, srcTabRowCount = pointers (NULL by default) to
// variables that rowcount info for query will be stored in.
// errorToken = text string indicating major operation being
// executed (within ustats, ...). Used for err processing.
// tabDef = pointer to HSTableDef of table affected (only used
// when rowsAffected, srcTabRowCount are non NULL.
// errorToIgnore = sqlcode of an inconsequential expected error
// that should not disrupt execution, such as "schema already
// exists" when executing a Create Schema statement. 0 indicates
// there is no such expected error.
// checkMdam = if TRUE, determine whether the query uses MDAM, and
// include this information in the ulog.
// -----------------------------------------------------------------------
Lng32 HSFuncExecTransactionalQueryWithRetry( const char *dml
, short sqlcode
, Int64 *rowsAffected
, const char *errorToken
, Int64 *srcTabRowCount
, const HSTableDef *tabDef
, short errorToIgnore
, NABoolean checkMdam
)
{
HSLogMan *LM = HSLogMan::Instance();
HSTranMan *TM = HSTranMan::Instance();
HSGlobalsClass *hs_globals = GetHSContext();
Lng32 retcode = 0;
if (TM->InTransaction())
{
// a transaction is already in progress; can't do retry
// as we don't know if there is other work already in the
// transaction
*CmpCommon::diags() << DgSqlCode(-UERR_GENERIC_ERROR)
<< DgString0("HSFuncExecTransactionalQueryWithRetry")
<< DgString1("-9215")
<< DgString2("Unexpected transaction in progress");
retcode = -UERR_GENERIC_ERROR;
return retcode;
}
// Note: We don't use the HSErrorCatcher object to capture diagnostic
// information here. The reason is, HSErrorCatcher works by lexical scope,
// but the place where diagnostics are available to us does not nicely
// match lexical scope. Instead, we call HSFuncMergeDiags directly.
// On very busy system, some "update statistics" implementation steps like
// "COLLECT FILE STATISTICS" step in HSTableDef::collectFileStatistics()
// may experience transient failures that may succeed if retried enough
// times. Note that AQR may do some retries for us under the covers.
Int32 centiSecs = getDefaultAsLong(USTAT_RETRY_DELAY);
Int32 limit = getDefaultAsLong(USTAT_RETRY_LIMIT);
for (Int32 retry = 0; retry <= limit; retry++)
{
// start a transaction
retcode = TM->Begin("Transaction for retryable Statement",TRUE);
if (retcode == 0)
{
// execute the statement
retcode = HSFuncExecQueryBody(dml, sqlcode, rowsAffected, errorToken,
srcTabRowCount, tabDef, errorToIgnore, checkMdam);
// Figure out if we want to ignore certain conditions
// filter retcode for HSHandleError
HSFilterWarning(retcode);
// If retcode is > 0 then set to 0 (no error/ignore).
if (retcode > 0)
retcode = 0;
// If sqlcode is HS_WARNING, then failures should be ignored. Also check
// for specific error code to be ignored.
if ((sqlcode == HS_WARNING && retcode < 0) || retcode == errorToIgnore)
retcode = 0;
// if successful, commit the transaction
if (retcode == 0)
retcode = TM->Commit(TRUE);
// analyze any error from the statement or the commit to see
// if we should retry
if (retcode == 0)
retry = limit+1; // exit retry loop on success
if (retcode == -SQLCI_SYNTAX_ERROR)
retry = limit+1; // don't retry statements with syntax errors
else if (retcode)
{
ComDiagsArea diags(STMTHEAP);
SQL_EXEC_MergeDiagnostics_Internal(diags); // copy CLI diags area
if (diags.contains(-EXE_CANCELED))
retry = limit+1; // don't retry canceled query
}
}
// If we are on our last retry, capture any diagnostics from
// the begin or statement failure. (If we don't capture them
// now, they will be cleared out of the CLI when we do the
// ROLLBACK below.)
if (retcode && (retry >= limit))
{
HSFuncMergeDiags(sqlcode, errorToken, NULL, TRUE /* get CLI diags */);
}
// If we had an error (on the begin, the statement or the commit),
// try rolling back the transaction. It might have already been
// rolled back, in which case the Rollback method just ignores
// the error.
if (retcode)
{
TM->Rollback(TRUE);
if (retry < limit) // if there are more retries
DELAY_CSEC(centiSecs); // wait before retrying
}
}
if (sqlcode != HS_WARNING || retcode < 0)
HSHandleError(retcode);
return retcode;
}
Lng32 HSFuncExecDDL( const char *dml
, short sqlcode
, Int64 *rowsAffected
, const char *errorToken
, const HSTableDef *tabDef
)
{
Lng32 retcode = 0;
HSGlobalsClass *hs_globals = GetHSContext();
if (!tabDef && hs_globals) tabDef = hs_globals->objDef;
if (!tabDef) return -1;
retcode = HSFuncExecTransactionalQueryWithRetry(dml, sqlcode,
rowsAffected, errorToken, NULL, tabDef);
HSHandleError(retcode);
return retcode;
}
Lng32 HSClearCLIDiagnostics()
{
Lng32 retcode;
retcode = SQL_EXEC_ClearDiagnostics(NULL);
return retcode;
}
// Obtain any JNI diagnostic text stored in the CLI
const char * HSFuncGetJniErrorStr()
{
return GetCliGlobals()->getJniErrorStr();
}
// -----------------------------------------------------------------------
// Create histogram tables if they don't exist.
// -----------------------------------------------------------------------
Lng32 CreateHistTables (const HSGlobalsClass* hsGlobal)
{
Lng32 retcode = 0;
HSLogMan *LM = HSLogMan::Instance();
if (LM->LogNeeded())
{
snprintf(LM->msg, sizeof(LM->msg), "Creating histogram tables for schema %s on demand.",
hsGlobal->catSch->data());
LM->Log(LM->msg);
}
// do NOT check volatile tables
if (hsGlobal->objDef->isVolatile()) return retcode;
LM->StartTimer("Create histogram tables");
NAString tableNotCreated;
// Call createHistogramTables to create any table that does not yet exist.
NAString histogramsLocation = getHistogramsTableLocation(hsGlobal->catSch->data(), FALSE);
HSTranController TC("Create histogram tables.",&retcode);
retcode = (CmpSeabaseDDL::createHistogramTables(NULL, histogramsLocation, TRUE, tableNotCreated));
if (retcode < 0 && LM->LogNeeded())
{
snprintf(LM->msg, sizeof(LM->msg), "Create failed for table %s.",
tableNotCreated.data());
LM->Log(LM->msg);
}
LM->StopTimer();
HSFilterWarning(retcode);
HSHandleError(retcode);
hsGlobal->diagsArea.clear();
return retcode;
}
Lng32 CreateSeabasePersSamples(const HSGlobalsClass* hsGlobal)
{
HSLogMan *LM = HSLogMan::Instance();
Lng32 retcode = 0;
ComObjectName tableName(hsGlobal->hsperssamp_table->data());
HSSqTableDef sampleDef(tableName, ANSI_TABLE);
if (!sampleDef.objExists()) //DROP existing sample table
{
if (LM->LogNeeded())
{
snprintf(LM->msg, sizeof(LM->msg), "Creating %s table for schema %s on demand.",
HBASE_PERS_SAMP_NAME, hsGlobal->catSch->data());
LM->Log(LM->msg);
}
retcode = CreateHistTables(hsGlobal);
}
return retcode;
}
/***********************************************/
/* METHOD: HSSample create() member function */
/* PURPOSE: Physically create sample table. */
/* This is a helper function to */
/* HSSample::make() */
/* RETCODE: 0 - successful */
/* non-zero otherwise */
/***********************************************/
Lng32 HSSample::create(NABoolean unpartitioned, NABoolean isPersSample)
{
makeTableName(isPersSample); // assigns 'sampleTable' name for MX/MP.
Lng32 retcode = 0;
retcode = create(sampleTable, unpartitioned, isPersSample);
return retcode;
}
Lng32 HSSample::create(NAString& tblName, NABoolean unpartitioned, NABoolean isPersSample)
{
Lng32 retcode = 0;
NAString ddl;
NAString dropstmt;
NAString catName, schName, objName;
hs_table_type tableType;
NAString tempTabName = tblName;
NAString userTabName = objDef->getObjectFullName();
HSGlobalsClass *hs_globals = GetHSContext();
NABoolean isNativeTable =
HSGlobalsClass::isNativeCat(objDef->getCatName(HSTableDef::EXTERNAL_FORMAT));
NAString userLocation;
ComObjectName *sampleName;
NAString tableOptions = " WITHOUT LOB COLUMNS";
HSTranMan *TM = HSTranMan::Instance();
if (objDef->getObjectFormat() == SQLMX)
{
// Do not emit the WITH PARTITIONS clause for native table.
// Rather, the SALT USING clause will be used.
if ( !isNativeTable )
tableOptions += " WITH PARTITIONS";
if (hs_globals->hasOversizedColumns)
{
// We will be truncating some columns when populating the sample table,
// trading off accuracy in UEC against performance. Add a clause to
// the table options limiting column lengths to the desired maximum.
tableOptions += " LIMIT COLUMN LENGTH TO ";
char temp[20]; // long enough for 32-bit integer
sprintf(temp,"%d",hs_globals->maxCharColumnLengthInBytes);
tableOptions += temp;
}
ddl = "CREATE TABLE ";
ddl += tempTabName;
ddl += " LIKE ";
// is this an MV LOG table?
if (objDef->getNameSpace() == COM_IUD_LOG_TABLE_NAME)
{
ddl += "TABLE (IUD_LOG_TABLE ";
ddl += userTabName;
ddl += ") ";
}
else
{
ddl += userTabName;
}
ddl += tableOptions;
tableType = ANSI_TABLE;
sampleName = new(STMTHEAP) ComObjectName(tempTabName,
COM_UNKNOWN_NAME,
FALSE,
STMTHEAP);
}
else
{
ComMPLoc tempObj(tblName, ComMPLoc::FILE);
tempTabName = tempObj.getMPName();
ComMPLoc userObj(objDef->getObjectFullName(), ComMPLoc::FILE);
userTabName = userObj.getMPName();
sampleName = new(STMTHEAP) ComObjectName(tempObj.getSysDotVol(),
tempObj.getSubvolName(),
tempObj.getFileName(),
COM_UNKNOWN_NAME,
ComAnsiNamePart::INTERNAL_FORMAT,
STMTHEAP);
tableType = GUARDIAN_TABLE;
// Do not remove the NOPURGEUNTIL clause in the following CREATE LIKE
// query. This clause is needed to override any existing NOPURGEUNTIL
// clause in the SOURCE table. Without this clause, a future
// nopurge date in the SOURCE table (nopurgeuntil is automatically
// inherited via CREATE LIKE) would prevent the TEMP table from being
// created or purged later.
// NOPURGEUNTIL is based on the current local time.
time_t t;
char purgeDate[12];
time(&t);
strftime(purgeDate, 12, "%b %d %Y", localtime(&t));
//The temporary table location and size is dependent on the base table's
//primary partition. If the PP0(primary partition) is FORMAT1, then the
//temporary table will be FORMAT1. If the PP0 is FORMAT2, then the temporary
//table will be FORMAT2. In addition, the temporary table will share the same
//volume as the primary partition.
//FORMAT1: has a limit of 2GB
//FORMAT2: has no limit, other that hardware constraints.
// If a transaction is running, the table needs to be created as audited.
// Otherwise, create table as non-audited.
if (TM->InTransaction())
tableOptions = " AUDIT ";
else
tableOptions = " NO AUDIT ";
ddl = "CREATE TABLE ";
ddl += tempTabName;
ddl += " LIKE ";
ddl += userTabName;
ddl += " NOPURGEUNTIL ";
ddl += purgeDate;
ddl += " CATALOG ";
ddl += objDef->getCatalogLoc();
ddl += tableOptions;
//There is no storage limit on format2, key-sequence tables, therefore we
//we want to allocate as big as possible. All others, including format 2,
//entry-sequence tables, have a 2G limit
ddl += HS_EXTENT_SIZE_MP_FMT1;
}
HSSqTableDef sampleDef(*sampleName, tableType);
if (sampleDef.objExists()) //DROP existing sample table
{
// make the sample table droppable
NAString droppableStmt;
droppableStmt = "ALTER TABLE ";
droppableStmt += tempTabName;
droppableStmt += " DROPPABLE " ;
retcode = HSFuncExecDDL(droppableStmt,
- UERR_GENERIC_ERROR,
NULL,
tempTabName, objDef);
HSHandleError(retcode);
dropstmt = "DROP TABLE ";
dropstmt += tempTabName;
retcode = HSFuncExecDDL(dropstmt,
- UERR_UNABLE_TO_DROP_OBJECT,
NULL,
tempTabName);
HSHandleError(retcode);
}
if (hs_globals && hs_globals->diagsArea.getNumber(DgSqlCode::ERROR_))
hs_globals->diagsArea.deleteError(0);
HSLogMan *LM = HSLogMan::Instance();
if (LM->LogNeeded()) {
snprintf(LM->msg, sizeof(LM->msg), "CREATE SAMPLE TABLE ddl text=%s", ddl.data());
LM->Log(LM->msg);
}
retcode = HSFuncExecDDL(ddl,
- UERR_UNABLE_TO_CREATE_OBJECT,
NULL,
tempTabName, objDef);
HSHandleError(retcode);
//***********************************************************
//The Persistent sample tables are in PUBLIC_ACCESS schema.
//The objects in this schema have grant to everyone all DML and
//DDL privileges. This means that any user can drop the persistent
//sample tables, alter the data in them or so forth. The mechanism
//to "hide" these tables from all users except update statistics code
// is to make the table not droppable and offline.
//By making the table not droppable, no users can drop the tables
//except Update stats code. When the tables are offline, users cannot
//select or delete from it. A user has to set the internal CQD to allow
//DML access on offline tables.
//A user cannot alter the table to droppable and drop it because
//its been marked offline.
//***********************************************************
// If the table is not volatile, make the sample table 'not droppable' and
// offline (no dml allowed). Skip this if makeAccessible_ is set; this happens
// when the sample table name is specified by cqd USTAT_SAMPLE_TABLE_NAME_CREATE
// so we can manipulate the sample table in tests.
// Skip this Alter stmt for Trafodion, it is not supported (the table was left
// as droppable in this case).
if (objDef->getTblOrigin() == HSTableDef::SQ_TBL &&
!objDef->isVolatile() && !makeAccessible_)
{
NAString notDroppableStmt;
notDroppableStmt = "ALTER TABLE ";
notDroppableStmt += tempTabName;
notDroppableStmt += " NOT DROPPABLE " ;
retcode = HSFuncExecDDL(notDroppableStmt,
-UERR_GENERIC_ERROR,
NULL,
tempTabName, objDef);
HSHandleError(retcode);
// make the sample table offline
HSFuncExecQuery("CONTROL QUERY DEFAULT IS_DB_TRANSPORTER 'ON'");
const Int32 hsALLOW_SPECIALTABLETYPE = 0x1;
SQL_EXEC_SetParserFlagsForExSqlComp_Internal(hsALLOW_SPECIALTABLETYPE);
NAString offlineStmt;
offlineStmt = "ALTER TABLE ";
offlineStmt += tempTabName ;
offlineStmt += " OFFLINE ";
retcode = HSFuncExecDDL(offlineStmt,
-UERR_GENERIC_ERROR,
NULL,
tempTabName, objDef);
HSFuncExecQuery("CONTROL QUERY DEFAULT IS_DB_TRANSPORTER RESET");
SQL_EXEC_ResetParserFlagsForExSqlComp_Internal(hsALLOW_SPECIALTABLETYPE);
}
HSHandleError(retcode);
return retcode;
}
Lng32 CreateHistView (const HSGlobalsClass* hsGlobal)
{
Lng32 retcode = 0;
NAString ddl;
NAString viewName;
if (hsGlobal->tableFormat == SQLMX)
{
NAString schVer(hsGlobal->objDef->getCatName(HSTableDef::EXTERNAL_FORMAT));
schVer += ".HP_DEFINITION_SCHEMA";
viewName = hsGlobal->catSch->data();
viewName += ".HISTVIEW";
ddl = "CREATE VIEW ";
ddl += viewName;
ddl += " (TABLE_NAME, TABLE_UID, COLUMN_NAME, HIST_ID, INTERVAL_COUNT, ROWCOUNT, TOTAL_UEC, LOW, HI) AS SELECT";
ddl += " SUBSTRING(O.OBJECT_NAME,1,20), TABLE_UID, SUBSTRING(C.COLUMN_NAME,1,20), CAST(H.HISTOGRAM_ID AS INTEGER), CAST(H.INTERVAL_COUNT AS INTEGER), CAST(H.ROWCOUNT AS INTEGER), CAST(H.TOTAL_UEC AS INTEGER), SUBSTRING(LOW_VALUE,1,50), SUBSTRING(HIGH_VALUE,1,50) FROM ";
ddl += schVer;
ddl += ".OBJECTS O, ";
ddl += schVer;
ddl += ".COLS C, ";
ddl += hsGlobal->hstogram_table->data();
ddl += " H WHERE O.OBJECT_UID = H.TABLE_UID AND O.OBJECT_UID = C.OBJECT_UID AND C.COLUMN_NUMBER = H.COLUMN_NUMBER";
}
else
{
viewName = hsGlobal->objDef->getCatalogLoc();
viewName += ".HISTVIEW";
ComMPLoc viewObj(viewName, ComMPLoc::FILE);
viewName = viewObj.getMPName();
NAString locName = viewObj.getSysDotVol();
locName += ".";
locName += viewObj.getSubvolName();
ddl = "CREATE VIEW ";
ddl += viewName;
ddl += " (TABLE_NAME, TABLE_UID, COLUMN_NAME, HIST_ID, INTERVAL_COUNT, ROWCOUNT, TOTAL_UEC, LOW, HI) AS SELECT";
ddl += " T.TABLENAME, H.TABLE_UID, SUBSTRING(C.COLNAME FROM 1 FOR 20), H.HISTOGRAM_ID, H.INTERVAL_COUNT, H.ROWCOUNT, H.TOTAL_UEC, SUBSTRING(H.LOW_VALUE FROM 1 FOR 50), SUBSTRING(H.HIGH_VALUE FROM 1 FOR 50) FROM ";
ddl += locName;
ddl += ".TABLES T, ";
ddl += locName;
ddl += ".COLUMNS C, ";
ddl += locName;
ddl += ".HISTOGRM H WHERE T.CREATETIME = H.TABLE_UID AND T.TABLENAME = C.TABLENAME AND H.COLUMN_NUMBER = C.COLNUMBER SECURE \"UUUN\" CATALOG ";
ddl += locName;
}
retcode = HSFuncExecDDL(ddl,
- UERR_UNABLE_TO_CREATE_OBJECT,
NULL,
viewName);
HSHandleError(retcode);
return retcode;
}
/*****************************************************/
/* METHOD: dropSample() static member function. */
/* */
/* PURPOSE: Drop sample table sampTblName if it */
/* exists. */
/* */
/* RETURN: 0 if successful, */
/* non-zero if failure. */
/*****************************************************/
Lng32 HSSample::dropSample(NAString& sampTblName, HSTableDef *sourceTblDef)
{
Lng32 retcode = 0;
NAString dropstmt, tempTabName;
HSLogMan *LM = HSLogMan::Instance();
if (sampTblName.length())
{
LM->Log("\tDROP SAMPLE TABLE");
tempTabName = sampTblName;
// If the table is not volatile, make the sample table 'droppable', except
// in the case of Trafodion, which does not support this Alter statement.
if (sourceTblDef->getTblOrigin() == HSTableDef::SQ_TBL &&
!sourceTblDef->isVolatile())
{
// make the sample table droppable
NAString droppableStmt;
droppableStmt = "ALTER TABLE ";
droppableStmt += tempTabName;
droppableStmt += " DROPPABLE " ;
retcode = HSFuncExecDDL(droppableStmt,
- UERR_GENERIC_ERROR,
NULL,
tempTabName, sourceTblDef);
HSHandleError(retcode);
}
dropstmt = "DROP TABLE ";
dropstmt += tempTabName;
retcode = HSFuncExecDDL(dropstmt,
- UERR_UNABLE_TO_DROP_OBJECT,
NULL,
tempTabName);
HSHandleError(retcode);
}
HSGlobalsClass *hs_globals = GetHSContext();
if (hs_globals)
{
hs_globals->sampleTableUsed = FALSE;
hs_globals->samplingUsed = FALSE;
}
return retcode;
}
/******************************************************/
/* METHOD: HSPrologEpilog constructor */
/* PURPOSE: Record transaction state when instance of */
/* HSPrologEpilog is created. This info is */
/* used by the destructor to check for an */
/* unterminated transaction */
/******************************************************/
HSPrologEpilog::HSPrologEpilog(NAString scopeName)
: scopeName_(scopeName),
tranMan_(HSTranMan::Instance()),
logMan_(HSLogMan::Instance())
{
enteredWithTranInProgress_ = tranMan_->InTransaction();
}
/******************************************************/
/* METHOD: HSPrologEpilog destructor */
/* PURPOSE: Compare current transaction state with */
/* the state when the object was created. */
/* The expectation is that they will be the */
/* same. If a transaction is in progress now */
/* but was not when the object was created, */
/* a rollback is performed under the */
/* assumption that its termination was */
/* bypassed due to a thrown exception. An */
/* external transaction that was terminated */
/* during the life of this object causes a */
/* message to be logged. */
/******************************************************/
HSPrologEpilog::~HSPrologEpilog()
{
NABoolean inTranNow = tranMan_->InTransaction();
if (!enteredWithTranInProgress_ && inTranNow)
{
if (logMan_->LogNeeded())
{
snprintf(logMan_->msg, sizeof(logMan_->msg),
"Rolling back transaction started but not terminated during %s",
scopeName_.data());
logMan_->Log(logMan_->msg);
}
tranMan_->Rollback();
}
else if (enteredWithTranInProgress_ && !inTranNow)
{
if (logMan_->LogNeeded())
{
snprintf(logMan_->msg, sizeof(logMan_->msg),
"External transaction terminated within dynamic scope of %s",
scopeName_.data());
logMan_->Log(logMan_->msg);
}
}
}
/*****************************************************************************/
/* CLASS: HSTranMan */
/* PURPOSE: Transaction manager for all of Update Statistics needs. */
/* DEPENDENCY: Pre-compiled statements in SQLHIST.mdf */
/* NOTES: This is a singleton class, which means that there could only be */
/* one instance of this class. */
/*****************************************************************************/
THREAD_P HSTranMan* HSTranMan::instance_ = 0;
HSTranMan::HSTranMan()
: transStarted_(FALSE), extTrans_(FALSE), retcode_(0)
{}
/***********************************************/
/* METHOD: Instance() */
/* PURPOSE: Returns the instance of the class. */
/* NOTES: We need to instantiate using the */
/* contextHeap because we need this */
/* class to stay around through */
/* multiple statements. */
/***********************************************/
HSTranMan* HSTranMan::Instance()
{
if (instance_ == 0)
instance_ = new (GetCliGlobals()->exCollHeap()) HSTranMan;
return instance_;
}
/***********************************************/
/* METHOD: Begin() */
/* PURPOSE: Starts a transaction */
/* RETCODE: 0 - successful */
/* 4 - transaction is running */
/* SQLCODE - severe error */
/***********************************************/
Lng32 HSTranMan::Begin(const char *title,NABoolean inactivateErrorCatcher)
{
HSLogMan *LM = HSLogMan::Instance();
// if an error occurred on a previous HSTransMan call, try to clean it up here
if ((retcode_ < 0) && (!InTransaction()))
retcode_ = 0; // not in a transaction, so OK to try starting one
if (retcode_ < 0) /*== ERROR HAD OCCURRED ==*/
{
if (LM->LogNeeded())
{
snprintf(LM->msg, sizeof(LM->msg), "BEGINWORK(%s) ***ERROR(%d)", title, retcode_);
LM->Log(LM->msg);
}
}
else
{
/*== BEGIN TRANSACTION ==*/
if (NOT transStarted_ &&
NOT (extTrans_ = ((SQL_EXEC_Xact(SQLTRANS_STATUS, 0) == 0) ? TRUE : FALSE)))
{
NAString stmtText = "BEGIN WORK";
retcode_ = HSFuncExecQuery(stmtText.data(), - UERR_INTERNAL_ERROR, NULL,
HS_QUERY_ERROR, NULL, NULL, 0, FALSE, inactivateErrorCatcher);
if (retcode_ >= 0)
{
transStarted_ = TRUE;
retcode_ = 0;
if (LM->LogNeeded())
{
snprintf(LM->msg, sizeof(LM->msg), "BEGINWORK(%s)", title);
LM->Log(LM->msg);
}
}
}
else
{ /*== TRANSACTION RUNNING==*/
retcode_ = HS_WARNING;
if (extTrans_ && LM->LogNeeded())
{
snprintf(LM->msg, sizeof(LM->msg), "BEGINWORK(%s) ***transaction running", title);
LM->Log(LM->msg);
}
}
}
return retcode_;
}
/***********************************************/
/* METHOD: Commit() */
/* PURPOSE: Commits any changes made during a */
/* transaction and ends the */
/* transaction. */
/* RETCODE: 0 - successful */
/* 4 - no transaction running */
/* or cannot commit user */
/* transaction */
/* SQLCODE - severe error */
/***********************************************/
Lng32 HSTranMan::Commit(NABoolean inactivateErrorCatcher)
{
HSLogMan *LM = HSLogMan::Instance();
if (retcode_ < 0) /*== ERROR HAD OCCURRED ==*/
{
if (LM->LogNeeded())
{
snprintf(LM->msg, sizeof(LM->msg), "COMMITWORK(unstable state: %d)\n", retcode_);
LM->Log(LM->msg);
}
}
else
{
if (transStarted_) /*== COMMIT TRANSACTION ==*/
{
NAString stmtText = "COMMIT WORK";
retcode_ = HSFuncExecQuery(stmtText.data(), - UERR_INTERNAL_ERROR, NULL,
HS_QUERY_ERROR, NULL, NULL, 0, FALSE, inactivateErrorCatcher);
// transaction has ended
transStarted_ = FALSE;
if (retcode_ >= 0) {
retcode_ = 0;
LM->Log("COMMITWORK()\n");
} else {
LM->Log("COMMIT WORK failed\n");
}
}
else
{ /*==NO TRANSACTION RUNNING==*/
/*== OR ==*/
retcode_ = HS_WARNING; /*== EXTERNAL TRANSACTION ==*/
if (extTrans_)
LM->Log("COMMITWORK(external transaction being used)\n");
else
LM->Log("COMMITWORK(no running transaction)\n");
}
}
return retcode_;
}
void HSTranMan::logXactCode(const char* title)
{
HSLogMan *LM = HSLogMan::Instance();
if (LM->LogNeeded()) {
LM->Log(title);
Lng32 transCode = SQL_EXEC_Xact(SQLTRANS_STATUS, 0);
char buf[80];
snprintf(buf, sizeof(buf), "SQL_EXEC_Xact() code=%d\n", transCode);
LM->Log(buf);
snprintf(buf, sizeof(buf), "transStarted_ = %s, extTrans_ = %s, retcode_ = %d\n",
transStarted_ ? "TRUE" : "FALSE", extTrans_ ? "TRUE" : "FALSE", retcode_);
LM->Log(buf);
}
}
/***********************************************/
/* METHOD: Rollback() */
/* PURPOSE: Undoes any changes made during a */
/* transaction and ends the */
/* transaction. */
/* RETCODE: 0 - successful */
/* 4 - no transaction running */
/* or cannot rollback user */
/* transaction */
/* SQLCODE - severe error */
/***********************************************/
Lng32 HSTranMan::Rollback(NABoolean inactivateErrorCatcher)
{
HSLogMan *LM = HSLogMan::Instance();
// if an error occurred on a previous HSTransMan call, try to clean it up here
if ((retcode_ < 0) && (!InTransaction()))
{
retcode_ = 0; // not in a transaction, and therefore no need to roll back
if (LM->LogNeeded())
{
snprintf(LM->msg, sizeof(LM->msg), "ROBACKWORK(cleaned up; not in transaction)");
LM->Log(LM->msg);
}
return retcode_;
}
if (retcode_ < 0) /*== ERROR HAD OCCURRED ==*/
{
if (LM->LogNeeded())
{
snprintf(LM->msg, sizeof(LM->msg), "ROBACKWORK(unstable state: %d)", retcode_);
LM->Log(LM->msg);
}
}
else
{
if (transStarted_) /*==ROLLBACK TRANSACTION==*/
{
NAString stmtText = "ROLLBACK WORK";
retcode_ = HSFuncExecQuery(stmtText.data(), - UERR_INTERNAL_ERROR, NULL,
HS_QUERY_ERROR, NULL, NULL, 0, FALSE, inactivateErrorCatcher);
// transaction has ended
transStarted_ = FALSE;
if (retcode_ < 0)
{
// The rollback may have failed because the Executor already
// aborted the transaction.
if (!InTransaction())
retcode_ = 0; // just ignore the error
else
{
snprintf(LM->msg, sizeof(LM->msg), "ROBACKWORK failed, retcode_: %d)", retcode_);
LM->Log(LM->msg);
}
}
if (retcode_ >= 0)
{
retcode_ = 0;
LM->Log("ROLLBACK()");
}
}
else
{ /*==NO TRANSACTION RUNNING==*/
/*== OR ==*/
retcode_ = HS_WARNING; /*== EXTERNAL TRANSACTION ==*/
if (extTrans_)
LM->Log("ROBACKWORK(external transaction being used)");
else
LM->Log("ROBACKWORK(no running transaction)");
}
}
return retcode_;
}
/***********************************************/
/* METHOD: InTransaction() */
/* PURPOSE: Tells you if there is a transaction*/
/* currently running. It does not */
/* consider if the transaction was */
/* started by user or instance. */
/* RETCODE: TRUE - transaction is running */
/* FALSE - no transaction running */
/***********************************************/
NABoolean HSTranMan::InTransaction()
{
return (SQL_EXEC_Xact(SQLTRANS_STATUS, 0) == 0);
}
/***********************************************/
/* METHOD: HSTranController constructor */
/* PURPOSE: Instantiates an HSTranController */
/* object, which also begins a */
/* transaction. If returnCodePtr is */
/* not NULL, then the value at that */
/* address when the destructor is */
/* called determines whether the */
/* transaction is committed or rolled */
/* back. returnCodePtr is also used */
/* to indicate the status of the */
/* begintran done in the ctor. This */
/* value should be checked upon ctor */
/* return. */
/***********************************************/
HSTranController::HSTranController(const char* title,
Lng32* returnCodePtr)
: tranMan_(HSTranMan::Instance()),
logMan_(HSLogMan::Instance()),
title_(title),
returnCodePtr_(returnCodePtr)
{
Lng32 retcode = tranMan_->Begin(title);
if (retcode == 0)
{
startedTrans_ = TRUE;
if (logMan_->LogNeeded())
logMan_->LogTimestamp("Transaction started");
} else
if (logMan_->LogNeeded())
logMan_->LogTimestamp("HSTranController: Transaction fail to start");
if (returnCodePtr)
*returnCodePtr = retcode;
}
/**************************************************/
/* METHOD: HSTranController end */
/* PURPOSE: When an HSTranController is destroyed,*/
/* its associated transaction is ended. */
/**************************************************/
HSTranController::~HSTranController()
{ endTrans(); }
/**************************************************/
/* METHOD: HSTranController end */
/* PURPOSE: If the return code address given to */
/* the ctor points to a negative value, */
/* the transaction is rolled back, else */
/* it is committed. */
/**************************************************/
void HSTranController::endTrans()
{
if (startedTrans_)
{
if (returnCodePtr_ && *returnCodePtr_ < 0)
{
tranMan_->Rollback();
if (logMan_->LogNeeded())
{
snprintf(logMan_->msg, sizeof(logMan_->msg), "Transaction \"%s\" rolled back due to error %d",
title_.data(), *returnCodePtr_);
logMan_->Log(logMan_->msg);
}
}
else
{
tranMan_->Commit();
if (logMan_->LogNeeded())
logMan_->LogTimestamp("Transaction committed");
}
}
}
/***********************************************/
/* METHOD: HSTranController stop/start */
/* PURPOSE: Stops previous transaction and */
/* starts a new one. The new one can */
/* ended with the destructor or */
/* another call to stopStart(). */
/***********************************************/
void HSTranController::stopStart(const char* title)
{
endTrans();
Lng32 retcode = tranMan_->Begin(title);
if (logMan_->LogNeeded() && retcode >= 0)
logMan_->LogTimestamp("Transaction started");
}
/*****************************************************************************/
/* CLASS: HSPersSamples */
/* FUNCTION: Handles persistent samples created for statistics needs. */
/* NOTES: This is a singleton class, which means that there could only be */
/* one instance of this class. */
/*****************************************************************************/
THREAD_P HSPersSamples* HSPersSamples::instance_ = 0;
HSPersSamples::HSPersSamples(const NAString & catalog, const NAString & schema)
: catalog_(new (CTXTHEAP) NAString(catalog)),
schema_(new (CTXTHEAP) NAString(schema)),
triedCreatingSBPersistentSamples_(false)
{}
HSPersSamples::~HSPersSamples()
{
delete catalog_;
delete schema_;
}
void HSPersSamples::setCatalogSchema(const NAString &catalog,
const NAString &schema)
{
if ((schema != *schema_) || (catalog != *catalog_))
{
*catalog_ = catalog;
*schema_ = schema;
triedCreatingSBPersistentSamples_ = false; // will try again on a new schema
}
}
//
// METHOD: Instance()
// PURPOSE: Returns the instance of the class */
// and searches for or creates */
// SB_PERSISTENT_SAMPLES table (list). */
// For every catalog for which a */
// SB_PERSISTENT_SAMPLES table is known */
// to exist, the catalog name is added*/
// to the 'persSampleTablesList'. */
// RETCODE: 0 if no errors. */
// non-zero otherwise. */
// INPUT: 'catalog', the catalog of the table*/
// for which a sample is created. */
// 'createTable', if TRUE, create the */
// SB_PERSISTENT_SAMPLES table for this */
// catalog if it doesn't exist. */
// NOTES: We need to instantiate using the */
// contextHeap because we need this */
// class to stay around through */
// multiple statements.
//
HSPersSamples* HSPersSamples::Instance(const NAString &catalog,
const NAString &schema)
{
Lng32 retcode = 0;
HSGlobalsClass *hs_globals = GetHSContext();
if (instance_ == 0)
instance_ = new (CTXTHEAP) HSPersSamples(catalog,schema);
else
{
instance_->setCatalogSchema(catalog,schema);
}
if (HSGlobalsClass::isHiveCat(catalog))
{
NAString hiveStatsCatalog(HIVE_STATS_CATALOG);
NAString hiveStatsSchema(HIVE_STATS_SCHEMA_NO_QUOTES);
instance_->setCatalogSchema(hiveStatsCatalog,hiveStatsSchema);
}
else if (HSGlobalsClass::isNativeHbaseCat(catalog))
{
NAString hbaseStatsCatalog(HBASE_STATS_CATALOG);
NAString hbaseStatsSchema(HBASE_STATS_SCHEMA_NO_QUOTES);
instance_->setCatalogSchema(hbaseStatsCatalog,hbaseStatsSchema);
}
// Create the SB_PERSISTENT_SAMPLES table if it does not already exist
// Do this at most once
if ((instance_) && (!instance_->triedCreatingSBPersistentSamples_))
{
retcode = CreateSeabasePersSamples(hs_globals);
instance_->triedCreatingSBPersistentSamples_ = true;
}
if (retcode) return 0;
else return instance_;
}
/***********************************************/
/* METHOD: find() */
/* PURPOSE: finds a */
/* persistent sample table for UID */
/* and sample size. If a persistent */
/* sample is found, the name will be */
/* returned in 'table', or blank if */
/* not. */
/* INPUT: uid - source table UID. */
/* sampleRows - the numner of rows a */
/* sample table should be close to */
/* in size. */
/* allowedDiff - the fraction amount */
/* that a matching table may differ */
/* from 'sampleRows'. */
/* OUTPUT: table - the name of a sample table */
/* found or "" if none found. */
/* RETCODE: 0 if no error during search. */
/* non-zero if error */
/***********************************************/
Lng32 HSPersSamples::find(HSTableDef *objDef, Int64 &actualRows, NABoolean isEstimate,
Int64 &sampleRows, double allowedDiff, NAString &table)
{
// Note: Earlier versions of this code supported multiple sample tables
// (with different sample ratios) for the same base table, which seems
// like overkill. So, in making this code work for Trafodion, we've made
// a simplifying assumption: Assume just one sample table per base table.
// (Indeed, we defined SB_PERSISTENT_SAMPLES to be keyed solely on the
// base table UID.)
//
// So, this method now ignores the actualRows, isEstimate, sampleRows and
// allowedDiff parameters. We can take them out later if this design
// decision becomes permanent.
Lng32 retcode = 0;
NABoolean removedObsolete=FALSE;
table=""; // in case no sample table is found
NAWchar tempTabNameUCS2[408]; // first double byte is varchar length + a few extras NAWchars
ComObjectName persSampTblObjName(*catalog_,
*schema_,
"SB_PERSISTENT_SAMPLES",
COM_TABLE_NAME,
ComAnsiNamePart::INTERNAL_FORMAT,
STMTHEAP);
NAString fromTable = persSampTblObjName.getExternalName();
Int64 uid = objDef->getObjectUID();
char uidStr[30];
convertInt64ToAscii(uid,uidStr);
HSCursor findSampleTableCursor1(STMTHEAP,"findSampleTable1");
NAString query = "SELECT SAMPLE_NAME FROM ";
query += fromTable;
query += " WHERE TABLE_UID = ";
query += uidStr;
retcode = findSampleTableCursor1.prepareQuery(query, 0, 4); // no input parms, 1 output col
HSLogError(retcode);
retcode = findSampleTableCursor1.open();
HSLogError(retcode);
// expect at most one row will match the condition because of one
// IUS sample table only assumption.
// sel_stmt.fetch() will set 'tempTabName' to a persistent table name,
// if one is found.
retcode = findSampleTableCursor1.fetch (1, (void *)&tempTabNameUCS2[0]);
// Handle errors from fetch. If retcode is 100 (meaning no more data),
// then set retcode = 0.
if (retcode)
{
if (retcode != HS_EOF)
{
HSHandleError(retcode);
}
else
{
retcode = 0;
}
}
else
{ // fetch() successful.
char tempTabNameUTF8[2*sizeof(tempTabNameUCS2)];
char *dummyFirstUntranslatedChar;
unsigned int outputDataLen;
Int32 rc = UTF16ToLocale(cnv_version1,
(char *) &tempTabNameUCS2[1],
tempTabNameUCS2[0], // varchar length
tempTabNameUTF8,
sizeof(tempTabNameUTF8),
cnv_UTF8,
dummyFirstUntranslatedChar,
&outputDataLen);
// conversion UCS2 to UTF8 should always be successful
HS_ASSERT(rc == 0 && outputDataLen >=0 && outputDataLen < sizeof(tempTabNameUTF8));
tempTabNameUTF8[outputDataLen] = 0;
table = tempTabNameUTF8;
}
// Don't overwrite the return code if an error has occurred, but attempt to
// close the cursor anyway (in case it was successfully opened).
if (retcode < 0)
findSampleTableCursor1.close();
else
retcode = findSampleTableCursor1.close();
HSTranMan * TM = HSTranMan::Instance();
if (TM->InTransaction())
TM->Commit();
return retcode;
}
/***********************************************/
/* METHOD: find() */
/* PURPOSE: finds a persistent sample table for*/
/* specified reason code.If found, */
/* the name will be returned in */
/* 'table', or blank if not. */
/* INPUT: uid - source table UID. */
/* reason - (ignored for now) */
/* OUTPUT: table - the name of a sample table */
/* found or "" if none found. */
/* requestedRows: requested rows */
/* sampleRows: sampled rows */
/* sampleRate: sample rate */
/* RETCODE: 0 if no error during search. */
/* non-zero if error */
/***********************************************/
Lng32 HSPersSamples::find(HSTableDef *objDef, char reason,
NAString &table, Int64 &requestedRows,
Int64 &sampleRows, double &sampleRate)
{
Lng32 retcode = 0;
NABoolean removedObsolete=FALSE;
table="";
NAWchar tempTabNameUCS2[408]; // first double byte is varchar length + a few extras NAWchars
ComObjectName persSampTblObjName(*catalog_,
*schema_,
"SB_PERSISTENT_SAMPLES",
COM_TABLE_NAME,
ComAnsiNamePart::INTERNAL_FORMAT,
STMTHEAP);
NAString fromTable = persSampTblObjName.getExternalName();
Int64 uid = objDef->getObjectUID();
char uidStr[30];
convertInt64ToAscii(uid,uidStr);
// Create query to search for a matching persistent sample in
// SB_PERSISTENT_SAMPLES table.
HSCursor findSampleTableCursor(STMTHEAP,"findSampleTable");
NAString query = "SELECT SAMPLE_NAME, REQUESTED_SAMPLE_ROWS, ACTUAL_SAMPLE_ROWS, SAMPLING_RATIO FROM ";
query += fromTable;
query += " WHERE TABLE_UID = ";
query += uidStr;
retcode = findSampleTableCursor.prepareQuery(query, 0, 4); // no input parms, 2 output cols
HSLogError(retcode);
retcode = findSampleTableCursor.open();
HSLogError(retcode);
// expect at most one row will match the condition because of one
// IUS sample table only assumption.
// sel_stmt.fetch() will set 'tempTabName' to a persistent table name,
// requestedRows to requested rows, sampleRows to sampled rows, and
// sampleRate to percent, if one is found. Otherwise 'table'
// is set to "" and requestdRows, sampleRows and sampleRate
// are not set.
retcode = findSampleTableCursor.fetch (4, (void *)&tempTabNameUCS2[0],
(void *)&requestedRows,
(void *)&sampleRows,
(void *)&sampleRate
);
// Handle errors from fetch. If retcode is 100 (meaning no more data),
// then set retcode = 0.
if (retcode)
{
if (retcode != HS_EOF)
{
HSHandleError(retcode);
}
else
{
retcode = 0;
}
}
else { // fetch() successful.
char tempTabNameUTF8[2*sizeof(tempTabNameUCS2)];
char *dummyFirstUntranslatedChar;
unsigned int outputDataLen;
Int32 rc = UTF16ToLocale(cnv_version1,
(char *) &tempTabNameUCS2[1],
tempTabNameUCS2[0], // varchar length
tempTabNameUTF8,
sizeof(tempTabNameUTF8),
cnv_UTF8,
dummyFirstUntranslatedChar,
&outputDataLen);
// conversion UCS2 to UTF8 should always be successful
HS_ASSERT(rc == 0 && outputDataLen >=0 && outputDataLen < sizeof(tempTabNameUTF8));
tempTabNameUTF8[outputDataLen] = 0;
table = tempTabNameUTF8;
}
// Don't overwrite the return code if an error has occurred, but attempt to
// close the cursor anyway (in case it was successfully opened).
if (retcode < 0)
findSampleTableCursor.close();
else
retcode = findSampleTableCursor.close();
return retcode;
}
Lng32 HSPersSamples::removeSample(HSTableDef* tabDef, NAString& sampTblName,
char reason, const char* txnLabel)
{
Lng32 retcode = 0;
HSTranMan *TM = HSTranMan::Instance();
if (sampTblName.length() > 0)
{
// Delete row in persistent samples table regardless of whether sample
// could be dropped.
ComObjectName persSampTblObjName(*catalog_,
*schema_,
"SB_PERSISTENT_SAMPLES",
COM_TABLE_NAME,
ComAnsiNamePart::INTERNAL_FORMAT,
STMTHEAP);
NAString fromTable = persSampTblObjName.getExternalName();
Int64 objUID = tabDef->getObjectUID();
NAString dml((size_t)500/*allocate 500 bytes*/, STMTHEAP);
dml.append("DELETE FROM ");
dml += fromTable; // in UTF8
dml += " WHERE TABLE_UID = ";
dml += Int64ToNAString(objUID);
// for now, the reason is ignored
retcode = HSFuncExecTransactionalQueryWithRetry(dml, - UERR_INTERNAL_ERROR,
NULL, txnLabel, NULL, NULL);
HSSample::dropSample(sampTblName, tabDef);
HSHandleError(retcode);
}
return retcode;
}
/****************************************************************/
/* METHOD: createAndInsert() */
/* PURPOSE: create a persistent sample table and insert an entry*/
/* for it into list. Note that actualRows will modified*/
/* if isEstimate = TRUE and the rows of original table */
/* can be determined from statistics. */
/* INPUTS: tabDef - a pointer to table struct. */
/* isEstimate - TRUE if 'actualRows' is est. on input. */
/* reason - sets REASON in SB_PERSISTENT_SAMPLES. */
/* IN/OUTS: sampleRows - the desired size of sample on input, */
/* actual sample size on output. */
/* actualRows - table size (possibly an est.) on input */
/* actual table size on output if modified. */
/* OUTPUTS: sampleName - the name of persistent sample table. */
/* RETCODE: 0 if success */
/* non-zero otherwise */
/****************************************************************/
Lng32 HSPersSamples::createAndInsert(HSTableDef *tabDef, NAString &sampleName,
Int64 &sampleRows, Int64 &actualRows,
NABoolean isEstimate, char reason,
NABoolean createDandI,
Int64 minRowCtPerPartition
)
{
HSTranMan *TM = HSTranMan::Instance();
HSCliStatement::statementIndex stmt;
Int64 reqSampleRows = 0, objUID = 0;
double percent = 0;
NAString ddl, into_table;
Lng32 retcode = 0;
NABoolean isPersistent = TRUE;
if (!tabDef || tabDef->setHasSyskeyFlag() != 0) return -1;
// Sets flag in tabDef indicating primary key is SYSKEY.
// If a persistent sample table already exists, raise an error telling
// the user about it.
Int64 dummy1, dummy2;
double dummy3;
NAString oldSampTblName;
retcode = find(tabDef, reason, oldSampTblName,
dummy1, dummy2, dummy3);
if ((retcode == 0) && (oldSampTblName.length() > 0))
{
retcode = -UERR_DROP_PERSISTANT_SAMPLE_FIRST;
HSFuncMergeDiags(retcode);
HSHandleError(retcode);
}
// Save original requested number of sample rows for inserting to SB_PERSISTENT_SAMPLES.
reqSampleRows = sampleRows;
Lng32 st = (CmpCommon::getDefault(USTAT_IUS_USE_PERIODIC_SAMPLING) == DF_ON) ?
SAMPLE_PERIODIC : SAMPLE_RAND_1;
HSSample sample(tabDef, st, (double)sampleRows/(double)actualRows*100,
isPersistent, reason == 'I');
// The last parameter in the make method indicates that it is a persistent sample
// table being created for fetch count.
// Create a partitioned persistent sample table for incremental update stats
// (reason code is 'I').
retcode = sample.make(isEstimate, sampleName, actualRows, sampleRows, TRUE,
TRUE, /* partitioned */
minRowCtPerPartition
);
// sampleName output & actualRows will get modified if necessary
// (based on isEstimate).
if (!retcode)
{
// Sample table successfully created, insert entry into
// SB_PERSISTENT_SAMPLES table. If unable to insert entry,
// drop sample table.
stmt = HSCliStatement::INSERT_PST;
percent = ((float)sampleRows/(float)actualRows)*100;
ComObjectName persSampTblObjName(*catalog_,
*schema_,
"SB_PERSISTENT_SAMPLES",
COM_TABLE_NAME,
ComAnsiNamePart::INTERNAL_FORMAT,
STMTHEAP);
into_table = persSampTblObjName.getExternalName();
objUID = tabDef->getObjectUID();
char timeStr[HS_TIMESTAMP_SIZE];
hs_formatTimestamp(timeStr);
NAString dml((size_t)500/*allocate 500 bytes*/, STMTHEAP);
dml.append("INSERT INTO ");
dml += into_table; // in UTF8
dml += " VALUES(";
dml += Int64ToNAString(objUID);
dml += ",";
dml += Int64ToNAString(reqSampleRows);
dml += ",";
dml += Int64ToNAString(sampleRows);
dml += ",";
dml += RealToNAString(percent);
dml += ",TIMESTAMP'";
dml += timeStr;
dml += "',_ISO88591'";
dml += reason;
dml += "',_UCS2";
NAString quotedSampleName(STMTHEAP);
ToQuotedString ( quotedSampleName // out - NAString &quotedStr
, sampleName // in - const NAString &internalStr
, TRUE // in - NABoolean encloseInQuotes = TRUE
);
dml += quotedSampleName; // in UTF8
HSGlobalsClass *hs_globals = GetHSContext();
ToQuotedString ( hs_globals->getWherePredicateForIUS() // out - NAString &quotedStr
, "" // in - const NAString &internalStr
, TRUE // in - NABoolean encloseInQuotes = TRUE
);
dml += ",_UCS2''"; // IUS_SEARCH_CONDITION (added after IUS stmt)
dml += ",TIMESTAMP'1970-01-01 00:00:00'"; // UPDATE_START_TIME TIMESTAMP
dml += ",''"; // UPDATER_INFO
dml += ",_UCS2''"; // V1
dml += ",_UCS2''"; // V2
dml += ");";
retcode = HSFuncExecTransactionalQueryWithRetry(dml, - UERR_INTERNAL_ERROR,
NULL, HS_QUERY_ERROR, NULL, NULL);
HSFilterWarning(retcode); // can't do HSHandleError here since we want to do the drop
if (retcode)
sample.drop();
HSHandleError(retcode);
}
return retcode;
}
/***********************************************/
/* METHOD: remove() */
/* PURPOSE: Remove all manual (i.e., not IUS) */
/* persistent sample tables for */
/* the table with object uid 'uid' */
/* and within 'allowedDiff' fraction */
/* of 'sampleRows' in size. */
/* INPUT: uid - source table UID. */
/* sampleRows - the numner of rows a */
/* sample table should be close to */
/* in size. */
/* allowedDiff - the fraction amount */
/* that a matching table may differ */
/* from 'sampleRows' that will be */
/* removed. */
/* RETCODE: 0 if a table was found and removed */
/* non-zero otherwise */
/* NOTE: This function implements a non- */
/* published command of update stats. */
/* Because of the transaction scope, */
/* it should not be used for functions*/
/* that will run on a production */
/* system. */
/***********************************************/
Lng32 HSPersSamples::removeMatchingSamples(HSTableDef *tabDef,
Int64 sampleRows,
double allowedDiff)
{
Lng32 retcode = 0;
NAString ddl, table, fromTable;
NABoolean nothingToDrop = TRUE;
// Loop until all persistent samples matching criteria have been removed.
Int64 actualRows = -1; // Obsolete samples will not be removed by find().
NABoolean isEstimate = TRUE;
retcode = find(tabDef, actualRows, isEstimate, sampleRows, allowedDiff, table);
ComObjectName persSampTblObjName(*catalog_,
*schema_,
"SB_PERSISTENT_SAMPLES",
COM_TABLE_NAME,
ComAnsiNamePart::INTERNAL_FORMAT,
STMTHEAP);
fromTable = persSampTblObjName.getExternalName();
while (retcode == 0 && table != "")
{
// Drop persistent sample table and remove from list.
nothingToDrop = FALSE;
retcode = removeSample(tabDef, table, 'M', "");
if (!retcode)
retcode = find(tabDef, actualRows, isEstimate, sampleRows, allowedDiff, table);
}
if (nothingToDrop)
{
// we didn't drop anything; warn the user
*CmpCommon::diags() << DgSqlCode(UERR_WARNING_NO_SAMPLE_TABLE);
}
return retcode;
}
/*********************************************************************/
/* METHOD: readIUSUpdateInfo() */
/* PURPOSE: Retrieve the UPDATE_START_TIME and UPDATER_INFO columns */
/* of the persistent samples table for the row identified */
/* by the UID of the passed HSTableDef. */
/* INPUT: tblDef - Ptr to HSTableDef for table the sample is on. */
/* updHistory - Buffer to store update history value. */
/* updTimestamp - Receives update timestamp value. */
/* RETCODE: Status code from the update operation. */
/*********************************************************************/
Lng32 HSPersSamples::readIUSUpdateInfo(HSTableDef* tblDef,
char* updHistory,
Int64* updTimestamp)
{
Lng32 retcode = 0;
HSErrorCatcher errorCatcher(retcode, - UERR_INTERNAL_ERROR, "readIUSUpdateInfo", TRUE);
ComObjectName persSampTblObjName(*catalog_,
*schema_,
"SB_PERSISTENT_SAMPLES",
COM_TABLE_NAME,
ComAnsiNamePart::INTERNAL_FORMAT,
STMTHEAP);
NAString fromTable = persSampTblObjName.getExternalName();
Int64 uid = tblDef->getObjectUID();
char uidStr[30];
convertInt64ToAscii(uid,uidStr);
HSCursor readIUSInfoCursor(STMTHEAP,"readIUSUpdateInfo");
NAString query = "SELECT UPDATER_INFO, "
"CAST(UPDATE_START_TIME - TIMESTAMP '1970-01-01 00:00:00' AS LARGEINT) FROM ";
query += fromTable;
query += " WHERE TABLE_UID = ";
query += uidStr;
retcode = readIUSInfoCursor.prepareQuery(query, 0, 2); // no input parms, 2 output cols
HSLogError(retcode);
retcode = readIUSInfoCursor.open();
HSLogError(retcode);
if (retcode == 0)
{
struct
{
short len;
char data[129]; // to fetch a varchar; UPDATER_INFO is VARCHAR(128)
} buffer;
retcode = readIUSInfoCursor.fetch(2, (void *)&buffer, (void *)updTimestamp);
HSLogError(retcode);
if (retcode == 0)
{
memmove(updHistory,buffer.data,buffer.len);
updHistory[buffer.len] = '\0'; // to insure a null terminator
}
}
// Don't overwrite a nonzero return code (error, warning, or HS_EOF) from
// the fetch, but attempt to close the cursor anyway.
if (retcode != 0)
readIUSInfoCursor.close();
else
retcode = readIUSInfoCursor.close();
return retcode;
}
// helper function that doubles any single quotes in text, so that
// we can write SQL text into a SQL table
void doubleUpSingleQuotes(const char *text, NAString & result)
{
const char * next = text;
char buf[2];
char doubledSingleQuote[3] = "''";
buf[1] = '\0';
while (*next)
{
if (*next == '\'')
result += doubledSingleQuote; // double up any single quote
else
{
// NAString += with a char doesn't work; have to use a char[]
buf[0] = *next;
result += buf;
}
next++;
}
}
/*********************************************************************/
/* METHOD: updIUSUpdateInfo() */
/* PURPOSE: Update the UPDATE_START_TIME and UPDATER_INFO columns of */
/* the persistent samples table for the row identified by */
/* the UID of the passed HSTableDef. */
/* INPUT: tblDef - Ptr to HSTableDef for table the sample is on. */
/* updHistory - Buffer containing updater info. */
/* updTimestamp - Char representation of update timestamp. */
/* updWhereCondition - if not null, the where predicate of */
/* the last completed IUS operation. (If null, we don't */
/* update this column.) */
/* requestedSampleRows - if non-null, points to expected # */
/* of rows in sample table (based on sampling rate). */
/* actualSampleRows - if non-null, actual # of rows. */
/* RETCODE: Status code from the update operation. */
/*********************************************************************/
Lng32 HSPersSamples::updIUSUpdateInfo(HSTableDef* tblDef,
const char* updHistory,
const char* updTimestampStr,
const char* updWhereCondition,
const Int64* requestedSampleRows,
const Int64* actualSampleRows)
{
Lng32 retcode = 0;
HSErrorCatcher errorCatcher(retcode, - UERR_INTERNAL_ERROR, "updIUSUpdateInfo", TRUE);
HSTranMan *TM = HSTranMan::Instance();
Int64 tblUID = tblDef->getObjectUID();
TM->Begin("UPDATE THE UPDATE DATE AND HISTORY FOR PERSISTENT SAMPLE TABLE");
// Update the update date and history in the row of the persistent samples
// table for the passed table UID.
ComObjectName persSampTblObjName(*catalog_,
*schema_,
"SB_PERSISTENT_SAMPLES",
COM_TABLE_NAME,
ComAnsiNamePart::INTERNAL_FORMAT,
STMTHEAP);
NAString updTable = persSampTblObjName.getExternalName();
Int64 uid = tblDef->getObjectUID();
char buf[30];
HSCursor writeIUSInfoCursor(STMTHEAP,"writeIUSUpdateInfo");
// The caller should have checked to see that the row existed first,
// but even so it is possible (though unlikely) that someone could
// have deleted the row in the meantime.
NAString query = "UPDATE ";
query += updTable;
query += " SET UPDATE_START_TIME = TIMESTAMP '";
query += updTimestampStr;
query += "', UPDATER_INFO = '";
query += updHistory;
query += "'";
if (updWhereCondition)
{
NAString doubledUp;
doubleUpSingleQuotes(updWhereCondition,doubledUp /* out*/);
query += ", LAST_WHERE_PREDICATE = ";
if (strlen(updWhereCondition) > 250)
{
// let SQL deal with character truncation issues
query += "SUBSTRING('";
query += doubledUp;
query += "' FOR 250)";
}
else
{
query += "'";
query += doubledUp;
query += "'";
}
}
if (requestedSampleRows)
{
convertInt64ToAscii(*requestedSampleRows, buf);
query += ", REQUESTED_SAMPLE_ROWS = ";
query += buf;
}
if (actualSampleRows)
{
convertInt64ToAscii(*actualSampleRows, buf);
query += ", ACTUAL_SAMPLE_ROWS = ";
query += buf;
}
query += " WHERE TABLE_UID = ";
convertInt64ToAscii(uid,buf);
query += buf;
retcode = writeIUSInfoCursor.prepareQuery(query, 0, 0);
HSLogError(retcode);
retcode = writeIUSInfoCursor.open();
HSLogError(retcode);
if (retcode == 0)
{
retcode = writeIUSInfoCursor.fetch(0,0);
if (retcode == 100) // a successful UPDATE returns 100 on fetch
retcode = 0;
HSLogError(retcode);
}
// Don't overwrite a nonzero return code (error, warning) from
// the fetch, but attempt to close the cursor anyway.
if (retcode != 0)
writeIUSInfoCursor.close();
else
{
retcode = writeIUSInfoCursor.close();
if (retcode == 100) // a successful UPDATE returns 100 on close too
retcode = 0;
}
if (retcode)
TM->Rollback();
else
TM->Commit();
return retcode;
}
/*****************************************************************************/
/* CLASS: HSPersData - This class is modeled after HSPersSamples */
/* FUNCTION: Stores data about CBFs created for statistics needs. */
/* NOTES: This is a singleton class, which means that there could only be */
/* one instance of this class. */
/*****************************************************************************/
THREAD_P HSPersData* HSPersData::instance_ = 0;
THREAD_P NAList<NAString>* HSPersData::persDataList_ = NULL;
THREAD_P NAString* HSPersData::catalog_ = NULL;
THREAD_P NAString* HSPersData::schema_ = NULL;
HSPersData::HSPersData()
{}
HSPersData* HSPersData::Instance(const NAString &catalog)
{
Lng32 retcode = 0;
if (instance_ == 0)
instance_ = new (CTXTHEAP) HSPersData;
if (persDataList_ == 0)
persDataList_ = new (CTXTHEAP) NAList<NAString>(CTXTHEAP);
if (catalog_ == 0)
catalog_ = new (CTXTHEAP) NAString("");
if (schema_ == 0)
schema_ = new (CTXTHEAP) NAString("");
// Search for an instance of catalog in memory resident list.
if (HSGlobalsClass::isHiveCat(catalog))
{
*catalog_ = HIVE_STATS_CATALOG;
*schema_ = HIVE_STATS_SCHEMA;
}
else
{
*catalog_ = catalog;
*schema_ = "PUBLIC_ACCESS_SCHEMA";
}
if (!(*persDataList_).contains(*catalog_))
{
*(CmpCommon::diags()) << DgSqlCode(-4222)
<< DgString0("Persistent Sample Table");
return 0;
}
if (retcode) return 0;
else return instance_;
}
Lng32 HSPersData::insert(NAString &tableName, ULng32 objectSubId, ULng32 seqNum, NAString &data)
{
Lng32 retcode = 0;
HSTranMan *TM = HSTranMan::Instance();
HSCliStatement::statementIndex stmt = HSCliStatement::INSERT_PDT;
ComObjectName persDataTblObjName(*catalog_,
*schema_,
"PERSISTENT_DATA",
COM_TABLE_NAME,
ComAnsiNamePart::INTERNAL_FORMAT,
STMTHEAP);
NAString into_table = persDataTblObjName.getExternalName();
NAWString *pTableNameInUTF16 =
charToUnicode ( (Lng32) CharInfo::UTF8 // char set of src str
, tableName/*in_utf8*/.data() // src str
, (Int32)tableName/*in_utf8*/.length() // src str len in bytes
, STMTHEAP // heap for allocated target str
);
NAWString tableNameInUTF16(STMTHEAP);
if (pTableNameInUTF16 != NULL && pTableNameInUTF16->length() > 0)
tableNameInUTF16.append(*pTableNameInUTF16);
delete pTableNameInUTF16; pTableNameInUTF16 = NULL;
if (!tableName.isNull() && tableNameInUTF16.length() <= 0)
{
NAString str0((size_t)600/*allocate 600 bytes*/, STMTHEAP);
str0.append("INSERT ");
str0.append(tableName);
*CmpCommon::diags() << DgSqlCode(-UERR_GENERIC_ERROR)
<< DgString0(str0.data())
<< DgString1("-2109")
<< DgString2("Unable to translate sample name from UTF8 to UCS2");
// HSHandleError(-UERR_GENERIC_ERROR);
return (-UERR_GENERIC_ERROR);
}
TM->Begin("INSERT INTO PERSISTENT_DATA TABLE.");
HSCliStatement insertPDT(stmt,
(char *)into_table.data(),
(char *)tableNameInUTF16.data(),
(char *)&objectSubId,
(char *)&seqNum,
(char *)data.data());
retcode = insertPDT.execFetch("INSERT_PDT " + into_table );
HSHandleError(retcode);
if (!retcode)
TM->Commit();
else
TM->Rollback();
return retcode;
}
Lng32 HSPersData::remove(NAString &tableName, ULng32 objectSubId, ULng32 seqNum)
{
Lng32 retcode = 0;
NAString fromTable;
HSTranMan *TM = HSTranMan::Instance();
HSCliStatement::statementIndex stmt = HSCliStatement::DELETE_PDT;
ComObjectName persDataTblObjName(*catalog_,
*schema_,
"PERSISTENT_DATA",
COM_TABLE_NAME,
ComAnsiNamePart::INTERNAL_FORMAT,
STMTHEAP);
fromTable = persDataTblObjName.getExternalName();
NAWString *pTableNameInUTF16 =
charToUnicode ( (Lng32) CharInfo::UTF8 // char set of src str
, tableName/*in_utf8*/.data() // src str
, (Int32)tableName/*in_utf8*/.length() // src str len in bytes
, STMTHEAP // heap for allocated target str
);
NAWString tableNameInUTF16(STMTHEAP);
if (pTableNameInUTF16 != NULL && pTableNameInUTF16->length() > 0)
tableNameInUTF16.append(*pTableNameInUTF16);
delete pTableNameInUTF16; pTableNameInUTF16 = NULL;
if (!tableName.isNull() && tableNameInUTF16.length() <= 0)
{
NAString str0((size_t)600/*allocate 600 bytes*/, STMTHEAP);
str0.append("INSERT ");
str0.append(tableName);
*CmpCommon::diags() << DgSqlCode(-UERR_GENERIC_ERROR)
<< DgString0(str0.data())
<< DgString1("-2109")
<< DgString2("Unable to translate sample name from UTF8 to UCS2");
// HSHandleError(-UERR_GENERIC_ERROR);
return (-UERR_GENERIC_ERROR);
}
TM->Begin("DELETE ROW FROM PERSISTENT_DATA TABLE.");
HSCliStatement deletePDT(stmt,
(char *)fromTable.data(),
(char *)tableNameInUTF16.data(),
(char *)&objectSubId,
(char *)&seqNum);
retcode = deletePDT.execFetch("DELETE_PDT " + fromTable );
if (!retcode)
TM->Commit();
else
TM->Rollback();
return retcode;
}
Lng32 HSPersData::fetch(NAString &tableName, ULng32 objectSubId, ULng32 seqNum, NAString &data)
{
Lng32 retcode = 0;
NAString fromTable;
HSCliStatement::statementIndex stmt = HSCliStatement::CURSOR_PDT;;
ComObjectName persDataTblObjName(*catalog_,
*schema_,
"PERSISTENT_DATA",
COM_TABLE_NAME,
ComAnsiNamePart::INTERNAL_FORMAT,
STMTHEAP);
fromTable = persDataTblObjName.getExternalName();
NAWString *pTableNameInUTF16 =
charToUnicode ( (Lng32) CharInfo::UTF8 // char set of src str
, tableName/*in_utf8*/.data() // src str
, (Int32)tableName/*in_utf8*/.length() // src str len in bytes
, STMTHEAP // heap for allocated target str
);
NAWString tableNameInUTF16(STMTHEAP);
if (pTableNameInUTF16 != NULL && pTableNameInUTF16->length() > 0)
tableNameInUTF16.append(*pTableNameInUTF16);
delete pTableNameInUTF16; pTableNameInUTF16 = NULL;
if (!tableName.isNull() && tableNameInUTF16.length() <= 0)
{
NAString str0((size_t)600/*allocate 600 bytes*/, STMTHEAP);
str0.append("INSERT ");
str0.append(tableName);
*CmpCommon::diags() << DgSqlCode(-UERR_GENERIC_ERROR)
<< DgString0(str0.data())
<< DgString1("-2109")
<< DgString2("Unable to translate sample name from UTF8 to UCS2");
// HSHandleError(-UERR_GENERIC_ERROR);
return (-UERR_GENERIC_ERROR);
}
HSCliStatement cursorPDT(stmt,
(char *)fromTable.data(),
(char *)tableNameInUTF16.data(),
(char *)&objectSubId,
(char *)&seqNum);
retcode = cursorPDT.open();
HSHandleError(retcode);
char *outputData;
outputData = new STMTHEAP char[32008];
retcode = cursorPDT.fetch (1, (void *)outputData);
// Handle errors from fetch. If retcode is 100 (meaning no more data),
// then set retcode = 0.
if (retcode)
{
if (retcode != HS_EOF)
HSHandleError(retcode);
}
else
{
short length = 0;
char *vclenPtr = NULL;
vclenPtr = (char *)&length;
str_cpy_all(vclenPtr,outputData,sizeof(short));
data = NAString(&outputData[sizeof(short)],length);
}
// Don't overwrite the return code if an error has occurred, but attempt to
// close the cursor anyway (in case it was successfully opened).
if (retcode < 0 || retcode == HS_EOF)
cursorPDT.close();
else
retcode = cursorPDT.close();
return retcode;
}
// -----------------------------------------------------------------------
// Some global constants for HSCliStatement.
// -----------------------------------------------------------------------
static const char *const StatementNames[] = {
"BEGINWORK"
, "COMMITWORK"
, "ROBACKWORK"
, "PRINTPLAN"
, "INSERT101_MP" , "INSERT101_MX", "INSERT101_MX_2300"
, "INSERT201_MP" , "INSERT201_MX", "INSERT201_MX_2300"
, "DELETE101_MP" , "DELETE101_MX", "DELETE101_MX_2300"
, "DELETE201_MP" , "DELETE201_MX", "DELETE201_MX_2300"
, "DELETE102_MP" , "DELETE102_MX", "DELETE102_MX_2300"
, "DELETE202_MP" , "DELETE202_MX", "DELETE202_MX_2300"
, "SECURITY101_MP" , "SECURITY101_MX", "SECURITY101_MX_2300"
, "SECURITY201_MP" , "SECURITY201_MX", "SECURITY201_MX_2300"
, "CURSOR101_MP" , "CURSOR101_MX", "CURSOR101_MX_2300"
, "CURSOR101_NOMC_MP" , "CURSOR101_NOMC_MX", "CURSOR101_NOMC_MX_2300"
, "CURSOR102_MP" , "CURSOR102_MX", "CURSOR102_MX_2300"
, "CURSOR103_MP" , "CURSOR103_MX", "CURSOR103_MX_2300"
, "CURSOR201_MP" , "CURSOR201_MX", "CURSOR201_MX_2300"
, "SHOWHIST_MP" , "SHOWHIST_MX", "SHOWHIST_MX_2300"
, "SHOWINT_MP" , "SHOWINT_MX", "SHOWINT_MX_2300"
, "ROWCOUNT_FROM_STATS" , "CURSOR104_MX_2300", "INSERT104_MX_2300"
, "CURSOR105_MX_2300"
, "DELETE_PST" , "INSERT_PST", "CURSOR_PST"
, "CURSOR_PST_REASON_CODE"
, "CURSOR107_MX_2300"
, "DELETE_PDT" , "INSERT_PDT", "CURSOR_PDT"
, "CURSOR_PST_UPDATE_INFO", "UPDATE_PST_UPDATE_INFO"
, "CURSOR103_MX_2300_X"
};
// -----------------------------------------------------------------------
// Constructor and destructor for HSCliStatement which runs static SQL.
// -----------------------------------------------------------------------
HSCliStatement::HSCliStatement( statementIndex ix,
char * in01, char * in02, char * in03,
char * in04, char * in05, char * in06,
char * in07, char * in08, char * in09,
char * in10, char * in11, char * in12,
char * in13, char * in14, char * in15,
char * in16, char * in17, char * in18,
char * in19, char * in20, char * in21,
char * in22, char * in23, char * in24,
char * in25
)
: pInputDesc_(NULL), validStmt_(FALSE),
in01_(in01), in02_(in02), in03_(in03),
in04_(in04), in05_(in05), in06_(in06),
in07_(in07), in08_(in08), in09_(in09),
in10_(in10), in11_(in11), in12_(in12),
in13_(in13), in14_(in14), in15_(in15),
in16_(in16), in17_(in17), in18_(in18),
in19_(in19), in20_(in20), in21_(in21),
in22_(in22), in23_(in23), in24_(in24),
in25_(in25),
retcode_(0)
{
HSLogMan *LM = HSLogMan::Instance();
if (LM->LogNeeded())
{
snprintf(LM->msg, sizeof(LM->msg), "!!>> %s", StatementNames[ix & 0xFFFF]);
LM->Log(LM->msg);
}
static SQLMODULE_ID module;
static char moduleName[HS_MODULE_LENGTH];
static NABoolean moduleSet = FALSE;
if (!moduleSet)
{
init_SQLMODULE_ID(&module);
strncpy(moduleName, HS_MODULE, HS_MODULE_LENGTH);
module.module_name = (char *)moduleName;
module.module_name_len = strlen((char*)moduleName);
module.creation_timestamp = 1234567890;
moduleSet = TRUE;
}
init_SQLCLI_OBJ_ID(&stmt_);
stmt_.name_mode = stmt_name;
stmt_.module = &module;
strncpy(stmtID_, StatementNames[ix & 0xFFFF], HS_STMTID_LENGTH);
stmt_.identifier = (char *)stmtID_;
stmt_.identifier_len = strlen((char *)stmtID_);
stmt_.handle = 0;
numVars_ = ((ix & 0x7FFF0000) >> 16);
const Int32 PRESET_VARS_NUM = 18; // 1 + max number of return values (in sqlhist.mdf) + 1
if (numVars_ < PRESET_VARS_NUM)
{
validStmt_ = TRUE;
return;
}
HS_ASSERT(numVars_ <= HSCliStatement::MAX_NUM_HOST_VARIABLES);
init_SQLCLI_OBJ_ID(&desc_);
pInputDesc_ = &desc_;
desc_.name_mode = desc_name;
desc_.module = &module;
strncpy(descID_, stmtID_, HS_STMTID_LENGTH);
strncat(descID_, "_IVAR", HS_STMTID_LENGTH);
desc_.identifier = (char *)descID_;
desc_.identifier_len = strlen((char *)descID_);
stmt_.handle = 0;
retcode_ = SQL_EXEC_SetDescPointers(pInputDesc_, PRESET_VARS_NUM, (numVars_ - PRESET_VARS_NUM + 1),
in18_, NULL, // starting parameter for PRESET_VARS_NUM
in19_, NULL,
in20_, NULL,
in21_, NULL,
in22_, NULL,
in23_, NULL,
in24_, NULL,
in25_, NULL);
if (retcode_)
{
HSFuncMergeDiags(-UERR_INTERNAL_ERROR, "HSCliStatement constructor", NULL, TRUE);
return;
}
validStmt_ = TRUE;
}
HSCliStatement::~HSCliStatement()
{}
Lng32 HSCliStatement::open()
{
Lng32 retcode = 0;
HSErrorCatcher errorCatcher(retcode, -UERR_INTERNAL_ERROR, "HSCliStatement destructor", TRUE);
retcode = SQL_EXEC_ClearDiagnostics(&stmt_);
HSHandleError(retcode);
retcode = SQL_EXEC_Exec(&stmt_, NULL, numVars_, /* OPEN CURSOR */
in01_, NULL, in02_, NULL, in03_, NULL,
in04_, NULL, in05_, NULL, in06_, NULL,
in07_, NULL, in08_, NULL, in09_, NULL,
in10_, NULL, in11_, NULL, in12_, NULL,
in13_, NULL, in14_, NULL, in15_, NULL,
in16_, NULL, in17_, NULL, in18_, NULL,
in19_, NULL, in20_, NULL, in21_, NULL,
in22_, NULL, in23_, NULL, in24_, NULL,
in25_, NULL);
HSHandleError(retcode);
return retcode;
}
Lng32 HSCliStatement::fetch(Lng32 numParam,
void * out01, void * out02, void * out03,
void * out04, void * out05, void * out06,
void * out07, void * out08, void * out09,
void * out10, void * out11, void * out12,
void * out13, void * out14, void * out15,
void * out16, void * out17, void * out18,
void * out19, void * out20, void * out21,
void * out22, void * out23, void * out24,
void * out25
)
{
Lng32 retcode = 0;
HS_ASSERT(numParam > 0 && out01 != NULL);
retcode = SQL_EXEC_Fetch(&stmt_, NULL, numParam,
out01, NULL, out02, NULL, out03, NULL,
out04, NULL, out05, NULL, out06, NULL,
out07, NULL, out08, NULL, out09, NULL,
out10, NULL, out11, NULL, out12, NULL,
out13, NULL, out14, NULL, out15, NULL,
out16, NULL, out17, NULL, out18, NULL,
out19, NULL, out20, NULL, out21, NULL,
out22, NULL, out23, NULL, out24, NULL,
out25, NULL
);
// Avoid overhead of HSErrorCatcher object for merging diagnostics from CLI,
// since fetch() will be called many times.
HSFilterWarning(retcode);
if (retcode)
{
HSFuncLogError(retcode, (char*)HS_FILE, (Lng32)__LINE__);
HSFilterError(retcode);
if (retcode < 0)
HSFuncMergeDiags(-UERR_INTERNAL_ERROR, "HSCliStatement::fetch()", NULL, TRUE);
}
return retcode;
}
Lng32 HSCliStatement::close()
{
Lng32 retcode = 0;
HSErrorCatcher errorCatcher(retcode, -UERR_INTERNAL_ERROR, "HSCliStatement::close()", TRUE);
retcode = SQL_EXEC_ClearDiagnostics(&stmt_);
HSHandleError(retcode);
retcode = SQL_EXEC_CloseStmt(&stmt_);
HSHandleError(retcode);
return retcode;
}
// -----------------------------------------------------------------------
// Run a static sql statement without output host variables.
// -----------------------------------------------------------------------
Lng32 HSCliStatement::execFetch(const char *dml, NABoolean hideError)
{
HSErrorCatcher errorCatcher(retcode_, hideError ? 0 : (- UERR_INTERNAL_ERROR),
dml, TRUE);
if (!validStmt_)
return -1;
statementNum++;
// Clear the sqlcode from any previous errors, so that this statement
// will execute...
Lng32 error = SQL_EXEC_ClearDiagnostics(&stmt_);
HSLogMan *LM = HSLogMan::Instance();
if (LM->LogNeeded())
{
snprintf(LM->msg, sizeof(LM->msg), "SQL_EXEC_ExecFetch for %s", dml);
LM->StartTimer(LM->msg);
}
retcode_ = SQL_EXEC_ExecFetch(&stmt_, pInputDesc_, numVars_,
in01_, NULL, in02_, NULL, in03_, NULL,
in04_, NULL, in05_, NULL, in06_, NULL,
in07_, NULL, in08_, NULL, in09_, NULL,
in10_, NULL, in11_, NULL, in12_, NULL,
in13_, NULL, in14_, NULL, in15_, NULL,
in16_, NULL, in17_, NULL, in18_, NULL,
in19_, NULL, in20_, NULL, in21_, NULL,
in22_, NULL, in23_, NULL, in24_, NULL,
in25_, NULL);
if (LM->LogNeeded())
LM->StopTimer();
retcode_ = (retcode_ >= 0 ? 0 : retcode_);
if (retcode_)
validStmt_ = FALSE;
// Optionally, we could try dynamic sql if static sql fails, i.e.,
// retcode_ = HSFuncExecQuery(dml);
Lng32 retcode = ((retcode_ < 0) ? -1 : retcode_);
return retcode;
}
// -----------------------------------------------------------------------
// Constructor and destructor for HSCursor.
// -----------------------------------------------------------------------
HSCursor::HSCursor(NAHeap *heap, const char* stmtName)
: stmtAllocated_(FALSE),
srcDescAllocated_(FALSE),
outputDescAllocated_(FALSE),
inputDescAllocated_(FALSE),
cursorName_(NULL),
rowsetSize_(0),
colDesc_(NULL), numEntries_(0),
ptrNAType_(NULL), dataBuf_(NULL), outputDataLen_(0),
group_(NULL), boundaryRowSet_(NULL), rowset_fields_(NULL),
closeStmtNeeded_(FALSE), retcode_(0), heap_(heap),
lastFetchReturned100_(FALSE)
{
static SQLMODULE_ID module;
static NABoolean moduleSet = FALSE;
stmt_ = new(heap_) SQLSTMT_ID;
srcDesc_ = new(heap_) SQLDESC_ID;
outputDesc_ = new(heap_) SQLDESC_ID;
inputDesc_ = new(heap_) SQLDESC_ID;
if (!moduleSet)
{
init_SQLMODULE_ID(&module);
moduleSet = TRUE;
}
init_SQLCLI_OBJ_ID(stmt_, SQLCLI_CURRENT_VERSION,
stmt_name, &module, stmtName, 0,
SQLCHARSETSTRING_ISO88591, (Lng32)strlen(stmtName), 0);
init_SQLCLI_OBJ_ID(srcDesc_);
init_SQLCLI_OBJ_ID(outputDesc_);
init_SQLCLI_OBJ_ID(inputDesc_);
srcDesc_->module = &module;
outputDesc_->module = &module;
inputDesc_->module = &module;
// ---------------------------------------------------------------------
// Initialize a descriptor which will hold the SQL statement source
// ---------------------------------------------------------------------
srcDesc_->name_mode = desc_handle;
srcDesc_->identifier = 0;
srcDesc_->identifier_len = 0;
srcDesc_->handle = 0;
// ---------------------------------------------------------------------
// Initialize an output descriptor to retrieve values
// ---------------------------------------------------------------------
outputDesc_->name_mode = desc_handle;
outputDesc_->identifier = 0;
outputDesc_->identifier_len = 0;
outputDesc_->handle = 0;
// ---------------------------------------------------------------------
// Initialize an input descriptor to deliver parameters.
// This is for the dynamic interface, used by TOPL.
// ---------------------------------------------------------------------
inputDesc_->name_mode = desc_handle;
inputDesc_->identifier = 0;
inputDesc_->identifier_len = 0;
inputDesc_->handle = 0;
}
HSCursor::~HSCursor()
{
// For errors occurring in CLI calls here, just log the error rather than
// causing the statement to fail.
Lng32 retcode;
if (closeStmtNeeded_)
{
retcode = SQL_EXEC_CloseStmt(stmt_);
HSLogError(retcode);
retcode = SQL_EXEC_ClearDiagnostics(stmt_);
HSLogError(retcode);
}
if (inputDescAllocated_)
SQL_EXEC_DeallocDesc(inputDesc_);
if (outputDescAllocated_)
{
retcode = SQL_EXEC_DeallocDesc(outputDesc_);
HSLogError(retcode);
}
if (srcDescAllocated_)
{
retcode = SQL_EXEC_DeallocDesc(srcDesc_);
HSLogError(retcode);
}
if (stmtAllocated_)
{
retcode = SQL_EXEC_DeallocStmt(stmt_);
HSLogError(retcode);
}
NADELETEBASIC(stmt_, heap_);
stmt_ = NULL;
NADELETEBASIC(srcDesc_,heap_);
srcDesc_ = NULL;
NADELETEBASIC(outputDesc_,heap_);
outputDesc_ = NULL;
NADELETEBASIC(inputDesc_,heap_);
inputDesc_ = NULL;
NADELETEBASIC(dataBuf_, heap_);
dataBuf_ = NULL;
delete cursorName_;
NADELETEBASIC(boundaryRowSet_, heap_);
boundaryRowSet_ = NULL;
// NADELETEBASIC(colDesc_, heap_);
// delete colDesc_;
// NADELETEARRAY(colDesc_, this->numEntries_, HSColDesc, heap_);
// colDesc_ = NULL;
// NADELETEBASIC(ptrNAType_, heap_);
// ptrNAType_ = NULL;
// NADELETEBASIC(group_, heap_);
// group_ = NULL;
}
/***********************************************/
/* METHOD: prepareRowset() */
/* PURPOSE: Prepare the query and store results*/
/* in ROWSET. */
/* RETCODE: 0 - successful */
/* -1 - failure */
/* PARAMS: cliStr(input) - single-column query*/
/* orderAndGroup(input) - true if no */
/* internal sort */
/* group - list of single columns */
/* maxRows - limit on number of rows */
/* for a single fetch */
/* ASSUMPTIONS: query is in the form: */
/* SELECT column, COUNT(*) and */
/* data retrieved in ascending order */
/***********************************************/
Lng32 HSCursor::prepareRowset(const char *cliStr, NABoolean orderAndGroup,
HSColGroupStruct *group, Lng32 maxRows)
{
// on very busy system, some "update statistics" implementation steps like
// read_cols_into_mem, fetch_boundary_rowset, etc may experience failures
// resulting in a flurry of callcatcher error 9200 events that show up in
// genesis solutions like 10-110215-6139, 10-100730-2173, 10-100530-0718,
// bugzilla 2457, etc. we suspect some of these errors may be transient
// failures that may succeed if retried enough times.
// 2 cqds allow user control of these retries.
Int32 centiSecs = getDefaultAsLong(USTAT_RETRY_DELAY);
Int32 limit = getDefaultAsLong(USTAT_RETRY_LIMIT);
Lng32 retcode = 0;
Int32 retry;
for (retry = 0; retry <= limit; retry++) {
// this must be a retry-able call
retcode = prepareRowsetInternal(cliStr, orderAndGroup, group, maxRows);
HSFilterWarning(retcode);
if (retcode >= 0) break;
if (limit && retry < limit) DELAY_CSEC(centiSecs);
}
return retcode;
}
Lng32 HSCursor::prepareRowsetInternal
(const char *cliStr, NABoolean orderAndGroup,
HSColGroupStruct *group, Lng32 maxRows)
{
// Not needed, as there is an error catcher in all of the caller's
// code paths. (And having two of them results in double reporting
// of the errors.)
//HSErrorCatcher errorCatcher(retcode_, -UERR_INTERNAL_ERROR,
// "HSCursor::prepareRowsetInternal()", TRUE);
HSLogMan *LM = HSLogMan::Instance();
HSColGroupStruct *col = group;
Lng32 numResults = 0;
Lng32 rowset_status[1]; //Has no functionality currently.
//However it is part of RowsetSetDesc API
if (orderAndGroup)
{
if (group->computeAvgVarCharSize())
{
numResults = 3; //FMT(value), UEC(value), AVG(OCTET_LENGTH(value))
}
else
numResults = 2; //FMT(value), UEC(value)
}
else if (col)
{
do
{
if (col->state == PENDING)
numResults++;
}
while (col = col->next);
}
else
{
LM->Log("***[ERROR] prepareRowset: GROUP PTR IS NULL\n");
return -1;
}
LM->Log(cliStr);
// Cannot reuse a cursor. This causes memory leak.
if (stmtAllocated_)
{
LM->Log("***[ERROR] REUSING ALLOCATED CURSOR\n");
return -1;
}
retcode_ = SQL_EXEC_ClearDiagnostics(stmt_);
HSHandleError(retcode_);
retcode_ = SQL_EXEC_AllocStmt(stmt_, 0);
HSHandleError(retcode_);
stmtAllocated_ = TRUE;
retcode_ = SQL_EXEC_AllocDesc(srcDesc_, 1);
HSHandleError(retcode_);
srcDescAllocated_ = TRUE;
retcode_ = SQL_EXEC_AllocDesc(outputDesc_, numResults);
HSHandleError(retcode_);
outputDescAllocated_ = TRUE;
retcode_ = SQL_EXEC_SetDescItem(srcDesc_, 1, SQLDESC_TYPE_FS,
REC_BYTE_V_ANSI, 0);
HSHandleError(retcode_);
retcode_ = SQL_EXEC_SetDescItem(srcDesc_, 1, SQLDESC_VAR_PTR,
(Long)cliStr, 0);
HSHandleError(retcode_);
retcode_ = SQL_EXEC_SetDescItem(srcDesc_, 1, SQLDESC_LENGTH,
strlen(cliStr) + 1, 0);
HSHandleError(retcode_);
// SQLDESC_CHAR_SET must be the last descriptor item set, otherwise
// it may get reset by other calls to SQL_EXEC_SetDescItem().
NAString charSet = ActiveSchemaDB()->getDefaults().getValue(ISO_MAPPING);
NAString defCS = ActiveSchemaDB()->getDefaults().getValue(DEFAULT_CHARSET);
retcode_ = SQL_EXEC_SetDescItem(srcDesc_, 1, SQLDESC_CHAR_SET,
SQLCHARSETCODE_UTF8
, 0);
HSHandleError(retcode_);
retcode_ = SQL_EXEC_SetStmtAttr(stmt_, SQL_ATTR_PARENT_QID, 0,
(char *)CmpCommon::context()->sqlSession()->getParentQid());
HSHandleError(retcode_);
/*==============================*/
/* PREPARE STATEMENT */
/*==============================*/
SQL_QUERY_COST_INFO query_cost_info;
SQL_QUERY_COMPILER_STATS_INFO comp_stats_info;
retcode_ = SQL_EXEC_Prepare2(stmt_, srcDesc_,NULL,0,NULL,&query_cost_info, &comp_stats_info,NULL,0,0);
HSHandleError(retcode_);
if (LM->LogNeeded())
{
printPlan(stmt_);
SQL_EXEC_ClearDiagnostics(stmt_);
}
/*==============================*/
/* SET OUTPUT DESCRIPTOR */
/* USING ROWSETS */
/*==============================*/
retcode_ = SQL_EXEC_DescribeStmt(stmt_, 0, outputDesc_);
HSHandleError(retcode_);
rowset_fields_ = new(heap_) SQLCLI_QUAD_FIELDS[numResults];
if (orderAndGroup)
{
boundaryRowSet_ = new (heap_) boundarySet<myVarChar>;
rowset_fields_[0].var_layout = HS_MAX_BOUNDARY_LEN;
rowset_fields_[0].var_ptr = (void *)&boundaryRowSet_->data[0];
rowset_fields_[0].ind_layout = sizeof(short);
rowset_fields_[0].ind_ptr = (void *)&boundaryRowSet_->nullInd[0];
rowset_fields_[1].var_layout = sizeof(Int64);
rowset_fields_[1].var_ptr = (void *)&boundaryRowSet_->dataSum[0];
rowset_fields_[1].ind_layout = 0;
rowset_fields_[1].ind_ptr = 0;
if (numResults == 3)
{
rowset_fields_[2].var_layout = sizeof(Int64);
rowset_fields_[2].var_ptr = (void *)&boundaryRowSet_->avgVarCharSize[0];
rowset_fields_[2].ind_layout = sizeof(short);
rowset_fields_[2].ind_ptr = (void *)&boundaryRowSet_->avgVarCharNullInd[0];
}
retcode_ = SQL_EXEC_SETROWSETDESCPOINTERS(outputDesc_,
MAX_ROWSET,
rowset_status,
1,
numResults,
rowset_fields_);
HSHandleError(retcode_);
}
else
retcode_ = setRowsetPointers(group, maxRows);
/*==============================*/
/* OPEN CURSOR */
/*==============================*/
retcode_ = SQL_EXEC_ClearDiagnostics(stmt_);
HSHandleError(retcode_);
LM->StartTimer("open cursor (execute)");
retcode_ = SQL_EXEC_Exec(stmt_, 0, 0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
LM->StopTimer();
if (retcode_ < 0)
{
LM->Log("***[FAILED] Unable to open rowset cursor.\n");
HSHandleError(retcode_);
}
closeStmtNeeded_ = TRUE;
return 0;
}
/********************************************************/
/* METHOD: setRowsetPointers() */
/* PURPOSE: Bind data and indicator addresses for */
/* columns retrieved for use by internal sort. */
/* PARAMS: group - list of single columns histograms */
/* are being created for. */
/* maxRows - limit on number of rows to */
/* retrieve in a single fetch. */
/* RETCODE: 0 - successful */
/********************************************************/
Lng32 HSCursor::setRowsetPointers(HSColGroupStruct *group, Lng32 maxRows)
{
HSErrorCatcher errorCatcher(retcode_, -UERR_INTERNAL_ERROR, "HSCursor::setRowsetPointers()", TRUE);
Int32 j=0;
Lng32 rowset_status[1]; //Has no functionality currently.
//However it is part of RowsetSetDesc API
do
{
if (group->state != PENDING)
continue;
if (group->nullIndics)
{
rowset_fields_[j].ind_layout = sizeof(short);
rowset_fields_[j].ind_ptr = (void *)group->nullIndics;
}
else
{
rowset_fields_[j].ind_layout = 0;
rowset_fields_[j].ind_ptr = 0;
}
rowset_fields_[j].var_layout = group->ISlength;
// Set the first offset that a value of this column will be written to.
// Character data is written into a different buffer, and the data buffer
// will consist of pointers to the char values.
if (DFS2REC::isAnyCharacter(group->ISdatatype))
{
if (DFS2REC::isSQLVarChar(group->ISdatatype) && group->isCompacted())
rowset_fields_[j].var_ptr = (void *)group->varcharFetchBuffer;
else
rowset_fields_[j].var_ptr = (void *)group->strNextData;
}
else
rowset_fields_[j].var_ptr = (void *)group->nextData;
j++;
}
while (group = group->next);
retcode_ = SQL_EXEC_SETROWSETDESCPOINTERS(outputDesc_,
maxRows,
rowset_status,
1,
j,
rowset_fields_);
HSHandleError(retcode_);
return retcode_;
}
/***********************************************/
/* METHOD: prepare() */
/* PURPOSE: Prepare the query and use single */
/* fetch cursor. */
/* RETCODE: 0 - successful */
/* -1 - failure */
/* PARAMS: cliStr(input) - single-column query*/
/* ASSUMPTIONS: query is in the form: */
/* SELECT column, COUNT(*) and */
/* data retrieved in ascending order */
/***********************************************/
Lng32 HSCursor::prepare( const char *clistr
, const Lng32 outDescEntries
)
{
HSErrorCatcher errorCatcher(retcode_, -UERR_INTERNAL_ERROR, "HSCursor::prepare()", TRUE);
Lng32 entry;
HSLogMan *LM = HSLogMan::Instance();
LM->Log(clistr);
// Cannot reuse a cursor. This causes memory leak.
if (stmtAllocated_)
{
LM->Log("***[ERROR] REUSING ALLOCATED CURSOR\n");
return -1;
}
retcode_ = SQL_EXEC_ClearDiagnostics(stmt_);
HSHandleError(retcode_);
retcode_ = SQL_EXEC_AllocStmt(stmt_, 0);
HSHandleError(retcode_);
stmtAllocated_ = TRUE;
retcode_ = SQL_EXEC_AllocDesc(srcDesc_, 1);
HSHandleError(retcode_);
srcDescAllocated_ = TRUE;
retcode_ = SQL_EXEC_AllocDesc(outputDesc_, outDescEntries);
HSHandleError(retcode_);
outputDescAllocated_ = TRUE;
retcode_ = SQL_EXEC_SetDescItem(srcDesc_, 1, SQLDESC_TYPE_FS,
REC_BYTE_V_ANSI, 0);
HSHandleError(retcode_);
retcode_ = SQL_EXEC_SetDescItem(srcDesc_, 1, SQLDESC_VAR_PTR,
(Long)clistr, 0);
HSHandleError(retcode_);
retcode_ = SQL_EXEC_SetDescItem(srcDesc_, 1, SQLDESC_LENGTH,
strlen(clistr) + 1, 0);
HSHandleError(retcode_);
// SQLDESC_CHAR_SET must be the last descriptor item set, otherwise
// it may get reset by other calls to SQL_EXEC_SetDescItem().
NAString charSet = ActiveSchemaDB()->getDefaults().getValue(ISO_MAPPING);
NAString defCS = ActiveSchemaDB()->getDefaults().getValue(DEFAULT_CHARSET);
retcode_ = SQL_EXEC_SetDescItem(srcDesc_, 1, SQLDESC_CHAR_SET,
SQLCHARSETCODE_UTF8
, 0);
HSHandleError(retcode_);
retcode_ = SQL_EXEC_SetStmtAttr(stmt_, SQL_ATTR_PARENT_QID, 0,
(char *)CmpCommon::context()->sqlSession()->getParentQid());
HSHandleError(retcode_);
/*==============================*/
/* PREPARE STATEMENT */
/*==============================*/
LM->StartTimer("SQL_EXEC_Prepare");
retcode_ = SQL_EXEC_Prepare(stmt_, srcDesc_);
LM->StopTimer();
HSHandleError(retcode_);
if (LM->LogNeeded())
{
printPlan(stmt_);
SQL_EXEC_ClearDiagnostics(stmt_);
}
/*==============================*/
/* SET OUTPUT DESCRIPTOR */
/* SINGLE FETCH CURSOR */
/*==============================*/
LM->StartTimer("Set up output descriptor");
retcode_ = SQL_EXEC_DescribeStmt(stmt_, 0, outputDesc_);
HSHandleError(retcode_);
retcode_ = SQL_EXEC_GetDescEntryCount(outputDesc_, &numEntries_);
HSHandleError(retcode_);
delete [] colDesc_;
colDesc_ = new(heap_) HSColDesc[numEntries_];
outputDataLen_ = 0;
Int32 i = 0;
for (; i < numEntries_; i++)
{
entry = i + 1;
retcode_ = SQL_EXEC_GetDescItem(outputDesc_, entry,
SQLDESC_TYPE_FS,
&colDesc_[i].datatype,
0, 0, 0, 0);
HSHandleError(retcode_);
retcode_ = SQL_EXEC_GetDescItem(outputDesc_, entry,
SQLDESC_OCTET_LENGTH,
&colDesc_[i].length,
0, 0, 0, 0);
HSHandleError(retcode_);
if (colDesc_[i].datatype == REC_DATETIME)
{
retcode_ = SQL_EXEC_GetDescItem(outputDesc_, entry,
SQLDESC_DATETIME_CODE,
&colDesc_[i].precision, 0, 0, 0, 0);
HSHandleError(retcode_);
retcode_ = SQL_EXEC_GetDescItem(outputDesc_, entry,
SQLDESC_PRECISION,
&colDesc_[i].scale, 0, 0, 0, 0);
HSHandleError(retcode_);
}
else if ((colDesc_[i].datatype >= REC_MIN_INTERVAL) &&
(colDesc_[i].datatype <= REC_MAX_INTERVAL))
{
retcode_ = SQL_EXEC_GetDescItem(outputDesc_, entry,
SQLDESC_INT_LEAD_PREC,
&colDesc_[i].precision, 0, 0, 0, 0);
HSHandleError(retcode_);
retcode_ = SQL_EXEC_GetDescItem(outputDesc_, entry,
SQLDESC_PRECISION,
&colDesc_[i].scale, 0, 0, 0, 0);
HSHandleError(retcode_);
}
else
{
retcode_ = SQL_EXEC_GetDescItem(outputDesc_, entry,
SQLDESC_PRECISION,
&colDesc_[i].precision,
0, 0, 0, 0);
HSHandleError(retcode_);
retcode_ = SQL_EXEC_GetDescItem(outputDesc_, entry,
SQLDESC_SCALE,
&colDesc_[i].scale,
0, 0, 0, 0);
HSHandleError(retcode_);
}
retcode_ = SQL_EXEC_GetDescItem(outputDesc_, entry,
SQLDESC_NULLABLE,
&colDesc_[i].nullflag,
0, 0, 0, 0);
HSHandleError(retcode_);
colDesc_[i].groupNum = 0;
colDesc_[i].indDataOffset = outputDataLen_;
if (colDesc_[i].nullflag)
outputDataLen_ += SQL_NULL_HDR_SIZE;
colDesc_[i].dataOffset = outputDataLen_;
//We need to adjust the buffer length to incorporate the length field for
//VARCHAR datatypes. Currently, the length field for VARCHAR is 2bytes. If
//a new datatype is created and requires a length field larger than 2bytes
//we will need to re-write this code. Possibly use
//SQLTYPECODE_VARCHAR_WITH_LENGTH
if (DFS2REC::isAnyVarChar(colDesc_[i].datatype))
outputDataLen_ += SQL_VARCHAR_HDR_SIZE;
outputDataLen_ += colDesc_[i].length;
} // for loop
// aligned on 4-byte boundary
outputDataLen_ = roundup4(outputDataLen_);
// Make sure dataBuf_ starts at a 4-byte boundary.
dataBuf_ = (char *)(new(heap_) Lng32[(outputDataLen_ >> 2) + 1]);
Long addr;
for (i = 0; i < numEntries_; i++)
{
entry = i + 1;
colDesc_[i].data = &dataBuf_[colDesc_[i].dataOffset];
colDesc_[i].indData = &dataBuf_[colDesc_[i].indDataOffset];
if (colDesc_[i].nullflag)
{
addr = (Long) colDesc_[i].indData;
retcode_ = SQL_EXEC_SetDescItem(outputDesc_, entry,
SQLDESC_IND_PTR,
addr, 0);
HSHandleError(retcode_);
}
addr = (Long) colDesc_[i].data;
retcode_ = SQL_EXEC_SetDescItem(outputDesc_,
entry,
SQLDESC_VAR_PTR,
addr, 0);
HSHandleError(retcode_);
}
LM->StopTimer();
/*==============================*/
/* OPEN CURSOR */
/*==============================*/
retcode_ = SQL_EXEC_ClearDiagnostics(stmt_);
HSHandleError(retcode_);
LM->StartTimer("SQL_EXEC_Exec (open cursor)");
retcode_ = SQL_EXEC_Exec(stmt_, 0, 0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
LM->StopTimer();
if (retcode_)
{
if (LM->LogNeeded())
{
snprintf(LM->msg, sizeof(LM->msg), "***[FAILED] OPEN CURSOR, retcode=%d", retcode_);
LM->Log(LM->msg);
}
HSHandleError(retcode_);
}
closeStmtNeeded_ = TRUE;
return 0;
}
// -----------------------------------------------------------------------
// Classes specialized for fast encoding.
// -----------------------------------------------------------------------
#define ALIGN2(addr) ((addr & 0x1) == 0)
#define ALIGN4(addr) ((addr & 0x3) == 0)
#define ALIGN8(addr) ((addr & 0x7) == 0)
template <class T> class HSBin : public SQLInt {
public:
HSBin(Lng32 id)
: SQLInt(NULL)
, id_(id) {}
~HSBin() {}
double encode(void *data) const
{ return *((T *)data); }
private:
Lng32 id_;
};
// -----------------------------------------------------------------------
class HSLargeint : public SQLInt {
public:
HSLargeint(Lng32 id)
: SQLInt(NULL)
, id_(id) {}
~HSLargeint() {}
double encode(void *data) const
{ return convertInt64ToDouble(*((Int64 *)data)); }
private:
Lng32 id_;
};
// -----------------------------------------------------------------------
class HSDate : public SQLDate {
public:
HSDate() : SQLDate(NULL, FALSE /*nullflag*/) {}
double encode(void *data) const
{
ULng32 w[4];
datetimeToLong((char *)data, w);
double val = (w[0] - 1) * 365 + (w[1] - 1) * 30 + w[2];
return val;
}
};
// -----------------------------------------------------------------------
NAType* ConstructNumericType( Long addr
, Lng32 id
, Lng32 length
, Lng32 precision
, Lng32 scale
, NABoolean allowNeg
, NABoolean nullflag
, NAHeap *currHeap
)
{
NAType *type;
switch(length) {
case 1:
type = new(currHeap) SQLTiny(currHeap, allowNeg, nullflag);
break;
case 2:
if (!ALIGN2(addr))
{
type = new(currHeap) SQLSmall(currHeap, allowNeg, nullflag);
break;
}
if (allowNeg) // 2-byte aligned
type = new(currHeap) HSBin<short>(id);
else
type = new(currHeap) HSBin<unsigned short>(id);
break;
case 4:
if (!ALIGN4(addr))
{
type = new(currHeap) SQLInt(currHeap, allowNeg, nullflag);
break;
}
if (allowNeg) // 4-byte aligned
type = new(currHeap) HSBin<Lng32>(id);
else
type = new(currHeap) HSBin<ULng32>(id);
break;
case 8:
if (!ALIGN8(addr))
{
type = new(currHeap) SQLLargeInt(currHeap, allowNeg, nullflag);
break;
}
if (allowNeg) // 8-byte aligned
type = new(currHeap) HSLargeint(id);
else
type = new(currHeap) SQLLargeInt(currHeap, allowNeg, nullflag);
break;
default:
type = new(currHeap) SQLNumeric(currHeap, length, precision, scale, allowNeg, nullflag);
break;
}
return type;
}
// -----------------------------------------------------------------------
// Construct a NA type from an SQL type so that NAtype.encode can be
// called later.
// -----------------------------------------------------------------------
Lng32 HSCursor::buildNAType()
{
#define REC_INTERVAL REC_MIN_INTERVAL
NAType *type;
Lng32 datatype, intervalType, length, precision, scaleword, nullword;
Int32 nullflag = 0;
Lng32 scale = 0;
Long addr;
if (ptrNAType_ != NULL)
delete [] ptrNAType_;
ptrNAType_ = new(heap_) HSPtrObj<NAType>[numEntries_];
for (Lng32 i = 0; i < numEntries_; i++)
{
datatype = colDesc_[i].datatype;
length = colDesc_[i].length;
precision = colDesc_[i].precision;
scaleword = colDesc_[i].scale;
nullword = colDesc_[i].nullflag;
type = NULL;
addr = (Long) colDesc_[i].data;
if (REC_MIN_INTERVAL <= datatype && datatype <= REC_MAX_INTERVAL)
{
intervalType = datatype;
datatype = REC_INTERVAL;
}
switch(datatype)
{
case REC_BIN8_SIGNED:
if (precision <= 0)
length = 1;
type = ConstructNumericType(addr, i, length, precision, scale,
TRUE, nullflag, heap_);
break;
case REC_BIN8_UNSIGNED:
if (precision <= 0)
length = 1;
type = ConstructNumericType(addr, i, length, precision, scale,
FALSE, nullflag, heap_);
break;
case REC_BIN16_SIGNED:
if (precision <= 0)
length = 2;
type = ConstructNumericType(addr, i, length, precision, scale,
TRUE, nullflag, heap_);
break;
case REC_BPINT_UNSIGNED:
case REC_BIN16_UNSIGNED:
if (precision <= 0)
length = 2;
type = ConstructNumericType(addr, i, length, precision, scale,
FALSE, nullflag, heap_);
break;
//
//
case REC_BIN32_SIGNED:
if (precision <= 0)
length = 4;
type = ConstructNumericType(addr, i, length, precision, scale,
TRUE, nullflag, heap_);
break;
case REC_BIN32_UNSIGNED:
if (precision <= 0)
length = 4;
type = ConstructNumericType(addr, i, length, precision, scale,
FALSE, nullflag, heap_);
break;
//
//
case REC_BIN64_SIGNED:
if (precision <= 0)
length = 8;
type = ConstructNumericType(addr, i, length, precision, scale,
TRUE, nullflag, heap_);
break;
case REC_BIN64_UNSIGNED:
if (precision <= 0)
length = 8;
type = ConstructNumericType(addr, i, length, precision, scale,
FALSE, nullflag, heap_);
break;
//
//----------------------------------------------------------------
case REC_FLOAT32:
case REC_FLOAT64:
//datatype = ((precision <= SQL_REAL_PRECISION) ?
// REC_FLOAT32 : REC_FLOAT64);
if (datatype == REC_FLOAT32)
type = new(heap_) SQLReal(heap_, nullflag, precision);
else if (datatype == REC_FLOAT64)
type = new(heap_) SQLDoublePrecision(heap_, nullflag, precision);
break;
//
//
case REC_DECIMAL_UNSIGNED:
type = new(heap_) SQLDecimal(heap_, length, scale, FALSE, nullflag);
break;
case REC_DECIMAL_LSE:
type = new(heap_) SQLDecimal(heap_, length, scale, TRUE, nullflag);
break;
case REC_NUM_BIG_UNSIGNED:
type = new(heap_) SQLBigNum(heap_, precision, scale, FALSE, FALSE, nullflag);
break;
case REC_NUM_BIG_SIGNED:
type = new(heap_) SQLBigNum(heap_, precision, scale, FALSE, TRUE, nullflag);
break;
//
//
case REC_BYTE_F_ASCII:
case REC_NCHAR_F_UNICODE:
type = new(heap_) SQLChar(heap_, length
, nullflag
#ifdef FULL_CHARSET_SUPPORT //##NCHAR: to be done!
, colDesc_[i].upshifted
, FALSE/*varLenFlag*/
, colDesc_[i].charset
, colDesc_[i].collation
, colDesc_[i].coercibility
#endif
);
break;
case REC_BYTE_V_ASCII:
case REC_NCHAR_V_UNICODE:
type = new(heap_) SQLVarChar(heap_, length
, nullflag
#ifdef FULL_CHARSET_SUPPORT //##NCHAR: to be done!
, colDesc_[i].upshifted
, colDesc_[i].charset
, colDesc_[i].collation
, colDesc_[i].coercibility
#endif
);
break;
// datetime and interval datatype values are fetched in external
// string format. Create a 'SQLChar' type for them so they could
// be encoded correctly.
case REC_DATETIME:
case REC_INTERVAL:
type = new(heap_) SQLChar(heap_, length
, nullflag
);
break;
case REC_BOOLEAN:
type = new(heap_) SQLBooleanNative(heap_, nullflag);
break;
default:
HSFuncMergeDiags(- UERR_UNSUPPORTED_DATATYPE);
retcode_ = -1;
HSHandleError(retcode_);
}
ptrNAType_[i].pt_ = type;
}
return 0;
}
/***********************************************/
/* METHOD: fetchRowset() */
/* PURPOSE: Fetch results from query and store */
/* in rowset. */
/* RETCODE: 0 - successful */
/* -1 - failure */
/* PARAMS: none */
/* ASSUMPTIONS: cursor has been prepared */
/***********************************************/
Lng32 HSCursor::fetchRowset()
{
HSErrorCatcher errorCatcher(retcode_, -UERR_INTERNAL_ERROR, "HSCursor::fetchRowset()", TRUE);
retcode_ = SQL_EXEC_Fetch(stmt_,outputDesc_, 0);
HSHandleError(retcode_);
if (retcode_ == 0 || retcode_ == 100)
{
retcode_ = SQL_EXEC_GetDescItem(outputDesc_, 1,
SQLDESC_ROWSET_NUM_PROCESSED,
&rowsetSize_,
0, 0, 0, 0);
HSHandleError(retcode_);
if (boundaryRowSet_)
boundaryRowSet_->size = rowsetSize_;
}
return retcode_;
}
/***************************************************************************/
/* METHOD: mergeAverage() */
/* PURPOSE: Merges the average gap magnitude for a rowset into the average */
/* for all gaps seen so far. */
/* PARAMS: firstRowset(in) -- TRUE if 1st rowset for this column. */
/* rowsetGapAvg(in) -- Average gap magnitude for this */
/* rowset. */
/* rowsetGapCount(in) -- Number of gaps (distinct values */
/* minus 1) in the rowset. */
/* overallGapAvg(in/out) -- Overall gap magnitude so far. */
/* overallGapCount(in/out) -- Overall number of gaps so far. */
/***************************************************************************/
void mergeAverage(NABoolean firstRowset,
double rowsetGapAvg, Int64 rowsetGapCount,
double &overallGapAvg, Int64 &overallGapCount)
{
HSLogMan *LM = HSLogMan::Instance();
// If this is the first rowset, just use the values for that rowset.
if (overallGapCount == 0)
{
overallGapAvg = rowsetGapAvg;
overallGapCount = rowsetGapCount;
return;
}
Int64 newOverallGapCount = overallGapCount + rowsetGapCount;
overallGapAvg = (overallGapAvg * overallGapCount) / newOverallGapCount
+(rowsetGapAvg * rowsetGapCount) / newOverallGapCount;
overallGapCount = newOverallGapCount;
if (LM->LogNeeded())
{
static Int32 numRowsets = 0;
if (firstRowset)
numRowsets = 0;
snprintf(LM->msg, sizeof(LM->msg), "Gap average for this rowset/first %d rowsets = %.2f/%.2f",
++numRowsets, rowsetGapAvg, overallGapAvg);
LM->Log(LM->msg);
}
}
/***********************************************/
/* METHOD: fetchBoundaries() */
/* PURPOSE: Using ROWSETS - Fetch data from */
/* table and build histogram */
/* intervals. */
/* RETCODE: 0 - successful */
/* -1 - failure */
/* PARAMS: group (input) - */
/* single-column group */
/* rowCount (input/output) */
/* #rows expected to read */
/* intCount (input) */
/* #intervals to generate */
/* ASSUMPTIONS: query is in the form: */
/* SELECT column, COUNT(*) and */
/* retrieved in ascending order */
/* NOTES: The last interval is reserved for */
/* NULL values only. */
/* bndry: )[----](----]...(----] */
/* int# 0 1 2 ... n */
/***********************************************/
Lng32 HSCursor::fetchBoundaries(HSColGroupStruct *group,
Int64 &rowCount,
Lng32 intCount,
NABoolean sampleUsed)
{
Int32 i;
Lng32 adjIntCount = intCount;
Int64 xTotalRowsRead = 0;
Int64 rowsInSet = 0;
HSGlobalsClass *hs_globals = GetHSContext();
NABoolean singleIntervalPerUec = FALSE;
Lng32 gapIntCount; // # intervals to use for gaps
Lng32 highFreqIntCount; // # intervals added for possible high frequency values
HSErrorCatcher errorCatcher(retcode_, - UERR_INTERNAL_ERROR, "FETCH_BOUNDARY_ROWSET", TRUE);
/*==============================*/
/* PREPARE ROWSET */
/*==============================*/
// prepareRowset may do retries
retcode_ = prepareRowset(group->clistr->data(), TRUE, group, 0);
HSHandleError(retcode_);
/*==============================*/
/* FETCH DATA INTO ROWSET */
/*==============================*/
// this is unlikely to succeed on a retry, so we don't.
retcode_ = fetchRowset();
if (rowsetSize() == 0 && retcode_ >= 0) /* no need to proces empty */
{ /* table, set rowcount=0 */
rowCount = 0;
return SUCCESS;
}
HSHandleError(retcode_);
/*==============================*/
/* ADJUST #INTERVALS */
/*==============================*/
// Don't include group for null in rowset count used for interval adjustment,
// or singleIntervalPerUec won't be set rowset count exactly equals the
// user-specified interval count.
Lng32 nonNullRowsets = boundaryRowSet_->nullInd[rowsetSize()-1] == -1
? rowsetSize() - 1
: rowsetSize();
adjIntCount = hs_globals->getAdjustedIntervalCount(group, intCount,
rowCount, nonNullRowsets,
singleIntervalPerUec,
gapIntCount,
highFreqIntCount);
group->groupHist = new(heap_) HSHistogram(adjIntCount, rowCount,
gapIntCount, highFreqIntCount,
sampleUsed, singleIntervalPerUec);
// If gaps are not to be processed, set all the gap sizes to 0 so subsequent
// processing won't have to take it into account. Don't need to do this again
// for subsequent rowsets of the same group; the values will remain 0.
// Gap processing is not done if using a single uec per interval, the group
// is a multi-column group, or the single column is not numeric.
if (gapIntCount == 0)
{
for (i=0; i<MAX_ROWSET; i++)
{
boundaryRowSet_->gapMagnitude[i] = 0;
}
}
NABoolean firstRowset = TRUE; // for use by profileGaps
// Maintain running average of magnitude of gaps and total # gaps. A gap is
// the numeric difference between 2 consecutive values, so the integers 5 and
// 6 are considered to have a gap of 1.
double overallGapAvg = 0;
Int64 overallGapCount = 0;
while (retcode_ == 0)
{
if (gapIntCount > 0)
profileGaps(group, boundaryRowSet_, overallGapAvg, overallGapCount,
firstRowset);
group->groupHist->processIntervalValues(
boundaryRowSet_,
group,
rowsInSet,
overallGapAvg);
xTotalRowsRead += rowsInSet;
/*==============================*/
/* FETCH DATA INTO ROWSET */
/*==============================*/
retcode_ = fetchRowset(); // not retry-able
firstRowset = FALSE;
if (retcode_ == HS_EOF)
{
//update the actual number of rows processed
rowCount = xTotalRowsRead;
break;
}
HSHandleError(retcode_);
}
hs_globals->checkTime("after fetching data into rowsets for RUS");
// Now that all distinct values and their frequencies have been seen, we
// know the actual gap average. Revisit the gap intervals we created,
// and keep the gapIntCount ones with the greatest gap magnitude. The rest
// are merged into adjacent intervals, unless that would create an interval
// of excessive height.
if (gapIntCount > 0)
group->groupHist->removeLesserGapIntervals(overallGapAvg);
return SUCCESS;
}
// -----------------------------------------------------------------------
// Fetch a numerical type column.
// -----------------------------------------------------------------------
Lng32 HSCursor::fetchNumColumn( const char *clistr
, Lng32 *pSmallValue
, Int64 *pLargeValue
)
{
HSErrorCatcher errorCatcher(retcode_, - UERR_INTERNAL_ERROR, "fetchNumColumn", TRUE);
HSLogMan *LM = HSLogMan::Instance();
LM->StartTimer("prepare() from HSCursor::fetchNumColumn()");
retcode_ = prepare(clistr, 1);
LM->StopTimer();
HSHandleError(retcode_);
LM->StartTimer("getRow() from HSCursor::fetchNumColumn()");
retcode_ = getRow();
LM->StopTimer();
HSHandleError(retcode_);
switch(colDesc_[0].length)
{
case sizeof(Int32):
{
Int32 tmp;
memcpy((char *) &tmp,
colDesc_[0].data,
sizeof(Int32));
if (pSmallValue != NULL)
*pSmallValue = tmp;
if (pLargeValue != NULL)
*pLargeValue = (Int64)tmp;
break;
}
case sizeof(Int64):
{
Int64 tmp;
memcpy((char *) &tmp,
colDesc_[0].data,
sizeof(Int64));
if (pSmallValue != NULL)
*pSmallValue = int64ToInt32(tmp);
if (pLargeValue != NULL)
*pLargeValue = tmp;
break;
}
return -1;
// Following assert is being commented to let the query compile with optimizer using
// histograms to estimate cardinalities instead of relying on fetchCount. Also created
// a Sol: 10-100318-8861 to track the issue as to why the colDesc_[0].length is not being
// set correctly.
// default: HS_ASSERT(FALSE);
}
return 0;
}
// -----------------------------------------------------------------------
// Fetch char type and largeint type columns from a table.
// -----------------------------------------------------------------------
Lng32 HSCursor::fetchCharNumColumn(const char *clistr, NAString &value1, Int64 &value2, double &value3)
{
HSErrorCatcher errorCatcher(retcode_, - UERR_INTERNAL_ERROR, "fetchCharColumn", TRUE);
retcode_ = prepare(clistr, 1);
HSHandleError(retcode_);
retcode_ = getRow();
if (retcode_ && retcode_ != HS_EOF) HSHandleError(retcode_);
if ((colDesc_[0].datatype != REC_BYTE_V_ASCII && colDesc_[0].datatype != REC_BYTE_V_DOUBLE) ||
colDesc_[1].datatype != REC_BIN64_SIGNED ||
(colDesc_[2].datatype != REC_IEEE_FLOAT64))
return -1; // Result should be VARCHAR, LARGEINT, and FLOAT64 datatypes.
if (retcode_ != HS_EOF)
{
// Copy varchar output to 'value'. First byte is length of varchar.
// Varchar data starts at byte 3.
#if defined(NA_LITTLE_ENDIAN)
char length = colDesc_[0].data[0];
#elif defined(NA_BIG_ENDIAN)
char length = colDesc_[0].data[1];
#else
"Neither NA_LITTLE_ENDIAN or NA_BIG_ENDIAN is defined."
#endif
char *tmp1 = new (heap_) char[length + 1];
memcpy(tmp1, colDesc_[0].data+2, length);
tmp1[length] = '\0';
value1 = tmp1;
NADELETEBASIC(tmp1, heap_);
Int64 tmp2;
memcpy((char *) &tmp2, colDesc_[1].data, sizeof(Int64));
value2 = tmp2;
// Assign value3 based on whether it is Tandem or IEEE float.
double tmp3;
if (colDesc_[2].datatype == REC_IEEE_FLOAT64)
memcpy((char *) &tmp3, colDesc_[2].data, colDesc_[2].length);
value3 = tmp3;
}
else value1="";
return 0;
}
// Constructor used for static interface.
HSinsertHist::HSinsertHist(const char *stmtID,
const char *histTable)
: tableName_(histTable),
numRows_(0),
retcode_(0),
stmtText_(""),
stmtAllocated_(FALSE),
srcDescAllocated_(FALSE),
inputDescAllocated_(FALSE)
{
static SQLMODULE_ID module;
static char moduleName[HS_MODULE_LENGTH];
static NABoolean moduleSet = FALSE;
if (!moduleSet)
{
init_SQLMODULE_ID(&module);
strncpy(moduleName, HS_MODULE, HS_MODULE_LENGTH);
module.module_name = (char *)moduleName;
module.module_name_len = strlen((char*)moduleName);
module.creation_timestamp = 1234567890;
moduleSet = TRUE;
}
init_SQLCLI_OBJ_ID(&stmt_);
stmt_.name_mode = stmt_name;
stmt_.module = &module;
strncpy(stmtID_, stmtID, HS_STMTID_LENGTH);
stmt_.identifier = (char *)stmtID_;
stmt_.identifier_len = strlen((char *)stmtID_);
stmt_.handle = 0;
init_SQLCLI_OBJ_ID(&desc_);
desc_.name_mode = desc_name;
desc_.module = &module;
strncpy(descID_, stmtID_, HS_STMTID_LENGTH);
strncat(descID_, "_IVAR", HS_STMTID_LENGTH);
desc_.identifier = (char *)descID_;
desc_.identifier_len = strlen((char *)descID_);
stmt_.handle = 0;
}
// Constructor used for dynamic interface.
HSinsertHist::HSinsertHist(const char *histTable)
: tableName_(histTable),
numRows_(0),
retcode_(0),
stmtText_(""),
stmtAllocated_(FALSE),
srcDescAllocated_(FALSE),
inputDescAllocated_(FALSE)
{
static SQLMODULE_ID module;
static NABoolean moduleSet = FALSE;
static const char stmtName[] = "INSERT_HIST_STMT";
if (!moduleSet)
{
init_SQLMODULE_ID(&module);
moduleSet = TRUE;
}
init_SQLCLI_OBJ_ID(&stmt_, SQLCLI_CURRENT_VERSION, stmt_handle, &module);
init_SQLCLI_OBJ_ID(&srcDesc_);
init_SQLCLI_OBJ_ID(&desc_);
srcDesc_.module = &module;
desc_.module = &module;
// ---------------------------------------------------------------------
// Initialize a descriptor which will hold the SQL statement source
// ---------------------------------------------------------------------
srcDesc_.name_mode = desc_handle;
srcDesc_.identifier = 0;
srcDesc_.identifier_len = 0;
srcDesc_.handle = 0;
// ---------------------------------------------------------------------
// Initialize an input descriptor to deliver parameters
// ---------------------------------------------------------------------
desc_.name_mode = desc_handle;
desc_.identifier = 0;
desc_.identifier_len = 0;
desc_.handle = 0;
}
HSinsertHist::~HSinsertHist()
{
if (inputDescAllocated_)
SQL_EXEC_DeallocDesc(&desc_);
if (srcDescAllocated_)
SQL_EXEC_DeallocDesc(&srcDesc_);
if (stmtAllocated_)
SQL_EXEC_DeallocStmt(&stmt_);
}
void HSinsertHist::setText()
{
char rowsetSizeStr[10];
snprintf(rowsetSizeStr, sizeof(rowsetSizeStr), "%d", MAX_ROWSET);
stmtText_ = "ROWSET FOR INPUT SIZE ? "
"INSERT INTO ";
stmtText_.append(tableName_)
.append(" VALUES (");
for (Int32 i=2; i<25; i++)
stmtText_.append("?[").append(rowsetSizeStr).append("], ");
stmtText_.append("?[").append(rowsetSizeStr).append("])");
}
Lng32 HSinsertHist::prepareDynamic()
{
HSErrorCatcher errorCatcher(retcode_, -UERR_INTERNAL_ERROR,
"HSinsertHist::prepareDynamic()", TRUE);
HSLogMan *LM = HSLogMan::Instance();
retcode_ = SQL_EXEC_ClearDiagnostics(&stmt_);
HSHandleError(retcode_);
retcode_ = SQL_EXEC_AllocStmt(&stmt_, 0);
HSHandleError(retcode_);
stmtAllocated_ = TRUE;
retcode_ = SQL_EXEC_AllocDesc(&srcDesc_, 1);
HSHandleError(retcode_);
srcDescAllocated_ = TRUE;
retcode_ = SQL_EXEC_AllocDesc(&desc_, 25);
HSHandleError(retcode_);
inputDescAllocated_ = TRUE;
retcode_ = SQL_EXEC_SetDescItem(&srcDesc_, 1, SQLDESC_TYPE_FS,
REC_BYTE_V_ANSI, 0);
HSHandleError(retcode_);
retcode_ = SQL_EXEC_SetDescItem(&srcDesc_, 1, SQLDESC_VAR_PTR,
(Long)stmtText_.data(), 0);
HSHandleError(retcode_);
retcode_ = SQL_EXEC_SetDescItem(&srcDesc_, 1, SQLDESC_LENGTH,
stmtText_.length() + 1, 0);
HSHandleError(retcode_);
// SQLDESC_CHAR_SET must be the last descriptor item set, otherwise
// it may get reset by other calls to SQL_EXEC_SetDescItem().
NAString charSet = ActiveSchemaDB()->getDefaults().getValue(ISO_MAPPING);
NAString defCS = ActiveSchemaDB()->getDefaults().getValue(DEFAULT_CHARSET);
retcode_ = SQL_EXEC_SetDescItem(&srcDesc_, 1, SQLDESC_CHAR_SET,
SQLCHARSETCODE_UTF8, 0);
HSHandleError(retcode_);
retcode_ = SQL_EXEC_SetStmtAttr(&stmt_, SQL_ATTR_PARENT_QID, 0,
(char *)CmpCommon::context()->sqlSession()->getParentQid());
HSHandleError(retcode_);
SQL_QUERY_COST_INFO query_cost_info;
SQL_QUERY_COMPILER_STATS_INFO comp_stats_info;
LM->StartTimer("SQL_EXEC_Prepare2() from HSinsertHist::prepareDynamic()");
retcode_ = SQL_EXEC_Prepare2(&stmt_, &srcDesc_, NULL,0,NULL,&query_cost_info, &comp_stats_info,NULL,0,0);
LM->StopTimer();
if (retcode_ < 0 && LM->LogNeeded())
{
LM->Log("Failure in preparing the following statement:");
LM->Log(stmtText_.data());
}
HSHandleError(retcode_);
retcode_ = SQL_EXEC_DescribeStmt(&stmt_, &desc_, NULL);
HSHandleError(retcode_);
return retcode_;
}
Lng32 HSinsertHist::initialize()
{
HSErrorCatcher errorCatcher(retcode_, -UERR_INTERNAL_ERROR, "HSinsertHist::initialize()", TRUE);
retcode_ = SQL_EXEC_ClearDiagnostics(&stmt_);
HSHandleError(retcode_);
#ifndef NA_USTAT_USE_STATIC // extra step for dynamic query
setText();
retcode_ = prepareDynamic();
HSHandleError(retcode_);
#endif
retcode_ = SQL_EXEC_SetRowsetDescPointers(&desc_
,MAX_ROWSET
,0
,1
#ifdef NA_USTAT_USE_STATIC // use static query defined in module file
,26
#else // NA_USTAT_USE_STATIC not defined, use dynamic query
,25
#endif
,0
,&(numRows_)
,(void*)0
,(void*)0
#ifdef NA_USTAT_USE_STATIC // use static query defined in module file
,0
,tableName_
,(void*)0
,(void*)0
#endif
,sizeof(Int64)
,&(tableUid_)
,(void*)0
,(void*)0
,sizeof(ULng32)
,&(histID_)
,(void*)0
,(void*)0
,sizeof(Lng32)
,&(colPosition_)
,(void*)0
,(void*)0
,sizeof(Lng32)
,&(colNumber_)
,(void*)0
,(void*)0
,sizeof(Lng32)
,&(colcount_)
,(void*)0
,(void*)0
,sizeof(signed short)
,&(intCount_)
,(void*)0
,(void*)0
,sizeof(Int64)
,&(rowCount_)
,(void*)0
,(void*)0
,sizeof(Int64)
,&(totalUEC_)
,(void*)0
,(void*)0
,TIMESTAMP_CHAR_LEN+1
,&(statsTime_[0])
,(void*)0
,(void*)0
,250*sizeof(wchar_t)
,&(lowValue_[0])
,(void*)0
,(void*)0
,250*sizeof(wchar_t)
,&(hiValue_[0])
,(void*)0
,(void*)0
// the following are columns for automation
,TIMESTAMP_CHAR_LEN+1
,&(readTime_[0])
,(void*)0
,(void*)0
,sizeof(short)
,&(readCount_)
,(void*)0
,(void*)0
,sizeof(Int64)
,&(sampleSecs_)
,(void*)0
,(void*)0
,sizeof(Int64)
,&(colSecs_)
,(void*)0
,(void*)0
,sizeof(short)
,&(samplePercent_)
,(void*)0
,(void*)0
,sizeof(double)
,&(cv_)
,(void*)0
,(void*)0
,sizeof(char)
,&(reason_)
,(void*)0
,(void*)0
,sizeof(Int64)
,&(v1_)
,(void*)0
,(void*)0
,sizeof(Int64)
,&(v2_)
,(void*)0
,(void*)0
,sizeof(Int64)
,&(v3_)
,(void*)0
,(void*)0
,sizeof(Int64)
,&(v4_)
,(void*)0
,(void*)0
,250*sizeof(wchar_t)
,&(v5_[0])
,(void*)0
,(void*)0
,250*sizeof(wchar_t)
,&(v6_[0])
,(void*)0
,(void*)0
);
HSHandleError(retcode_);
return retcode_;
}
Lng32 HSinsertHist::addRow(const Int64 table_uid,
const ULng32 histogram_id,
const Lng32 col_position,
const Lng32 column_number,
const Lng32 colcount,
const short interval_count,
const Int64 rowcount,
const Int64 total_uec,
const char *stats_time,
const HSDataBuffer &low_value,
const HSDataBuffer &high_value,
// the following columns are for automation
const char *read_time,
const short read_count,
const Int64 sample_secs,
const Int64 col_secs,
const short sample_percent,
const double cv,
const char reason,
const Int64 v1,
const Int64 v2,
const Int64 v3,
const Int64 v4,
const HSDataBuffer &v5,
const HSDataBuffer &v6)
{
Lng32 retcode = 0;
tableUid_[numRows_] = table_uid;
histID_[numRows_] = histogram_id;
colPosition_[numRows_] = col_position;
colNumber_[numRows_] = column_number;
colcount_[numRows_] = colcount;
intCount_[numRows_] = interval_count;
rowCount_[numRows_] = rowcount;
totalUEC_[numRows_] = total_uec;
strcpy(&statsTime_[numRows_][0], stats_time);
// the following columns are for automation
strcpy(&readTime_[numRows_][0], read_time);
readCount_[numRows_] = read_count;
sampleSecs_[numRows_] = sample_secs;
colSecs_[numRows_] = col_secs;
samplePercent_[numRows_] = sample_percent;
cv_[numRows_] = cv;
reason_[numRows_] = reason;
v1_[numRows_] = v1;
v2_[numRows_] = v2;
v3_[numRows_] = v3;
v4_[numRows_] = v4;
v5_[numRows_][0] = v5.length();
memmove(&v5_[numRows_][1], v5.data(), v5.length());
v6_[numRows_][0] = v6.length();
memmove(&v6_[numRows_][1], v6.data(), v6.length());
//set low_value: LEN + DATA
lowValue_[numRows_][0] = low_value.length();
memmove(&lowValue_[numRows_][1], low_value.data(), low_value.length());
//set high_value: LEN + DATA
hiValue_[numRows_][0] = high_value.length();
memmove(&hiValue_[numRows_][1], high_value.data(), high_value.length());
if (++numRows_ == MAX_ROWSET)
{
retcode = flush();
HSHandleError(retcode);
}
return retcode;
}
Lng32 HSinsertHist::flush()
{
HSErrorCatcher errorCatcher(retcode_, -UERR_INTERNAL_ERROR, "HSinsertHist::flush()", TRUE);
HSLogMan *LM = HSLogMan::Instance();
if (numRows_ > 0 && retcode_ == 0)
{
retcode_ = SQL_EXEC_ExecFetch(&stmt_,&desc_,0);
if (LM->LogNeeded())
{
snprintf(LM->msg, sizeof(LM->msg), "HISTOGRAM ROWSET INSERT of %d rows. Retcode=(%d)", numRows_, retcode_);
LM->Log(LM->msg);
print();
}
HSHandleError(retcode_);
numRows_ = 0;
}
return retcode_;
}
void HSinsertHist::print()
{
char tableUidStr[30];
char rowCountStr[30];
char uecStr[30];
NAString low, high;
// the following are for automation
char sampleSecsStr[30];
char colSecsStr[30];
//char v1[30],v2[30],v3[30],v4[30];
//NAString v5, v6;
HSLogMan *LM = HSLogMan::Instance();
for (Lng32 i = 0; i < numRows_; i++)
{
convertInt64ToAscii(tableUid_[i], tableUidStr);
convertInt64ToAscii(rowCount_[i], rowCountStr);
convertInt64ToAscii(totalUEC_[i], uecStr);
ConvWcharToHexadecimal(&lowValue_[i][1], lowValue_[i][0] / 2, low);
ConvWcharToHexadecimal(&hiValue_[i][1], hiValue_[i][0] / 2, high);
// the following are for automation
convertInt64ToAscii(sampleSecs_[i], sampleSecsStr);
convertInt64ToAscii(colSecs_[i], colSecsStr);
//convertInt64ToAscii(v1_[i], v1);
//convertInt64ToAscii(v2_[i], v2);
//convertInt64ToAscii(v3_[i], v3);
//convertInt64ToAscii(v4_[i], v4);
//ConvWcharToHexadecimal(&v5_[i][1], v5_[i][0] / 2, v5);
//ConvWcharToHexadecimal(&v6_[i][1], v6_[i][0] / 2, v6);
snprintf(LM->msg, sizeof(LM->msg), "\t( %s, %u, %d, %d, %d, %d, %s, %s, %s, %s, %s, %s, %d, %s, %s, %d, %f, %c)",
tableUidStr,
histID_[i],
colPosition_[i],
colNumber_[i],
colcount_[i],
intCount_[i],
rowCountStr,
uecStr,
statsTime_[i],
low.data(),
high.data(),
// the following are for automation
readTime_[i],
readCount_[i],
sampleSecsStr,
colSecsStr,
samplePercent_[i],
cv_[i],
reason_[i]
//v1,v2,v3,v4,v5.data(),v6.data()
);
LM->Log(LM->msg);
}
}
HSinsertHistint::HSinsertHistint(const char *stmtID,
const char *histIntTable)
: tableName_(histIntTable),
numRows_(0),
retcode_(0),
stmtText_(""),
stmtAllocated_(FALSE),
srcDescAllocated_(FALSE),
inputDescAllocated_(FALSE)
{
static SQLMODULE_ID module;
static char moduleName[HS_MODULE_LENGTH];
static NABoolean moduleSet = FALSE;
if (!moduleSet)
{
init_SQLMODULE_ID(&module);
strncpy(moduleName, HS_MODULE, HS_MODULE_LENGTH);
module.module_name = (char *)moduleName;
module.module_name_len = strlen((char*)moduleName);
module.creation_timestamp = 1234567890;
moduleSet = TRUE;
}
init_SQLCLI_OBJ_ID(&stmt_);
stmt_.name_mode = stmt_name;
stmt_.module = &module;
strncpy(stmtID_, stmtID, HS_STMTID_LENGTH);
stmt_.identifier = (char *)stmtID_;
stmt_.identifier_len = strlen((char *)stmtID_);
stmt_.handle = 0;
init_SQLCLI_OBJ_ID(&desc_);
desc_.name_mode = desc_name;
desc_.module = &module;
strncpy(descID_, stmtID_, HS_STMTID_LENGTH);
strncat(descID_, "_IVAR", HS_STMTID_LENGTH);
desc_.identifier = (char *)descID_;
desc_.identifier_len = strlen((char *)descID_);
stmt_.handle = 0;
}
// Ctor used for dynamic version of statement.
HSinsertHistint::HSinsertHistint(const char *histIntTable)
: tableName_(histIntTable),
numRows_(0),
retcode_(0),
stmtText_(""),
stmtAllocated_(FALSE),
srcDescAllocated_(FALSE),
inputDescAllocated_(FALSE)
{
static SQLMODULE_ID module;
static NABoolean moduleSet = FALSE;
static const char stmtName[] = "INSERT_HISTINT_STMT";
if (!moduleSet)
{
init_SQLMODULE_ID(&module);
moduleSet = TRUE;
}
init_SQLCLI_OBJ_ID(&stmt_, SQLCLI_CURRENT_VERSION,
stmt_name, &module, stmtName, 0,
SQLCHARSETSTRING_ISO88591, (Lng32)strlen(stmtName), 0);
init_SQLCLI_OBJ_ID(&srcDesc_);
init_SQLCLI_OBJ_ID(&desc_);
srcDesc_.module = &module;
desc_.module = &module;
// ---------------------------------------------------------------------
// Initialize a descriptor which will hold the SQL statement source
// ---------------------------------------------------------------------
srcDesc_.name_mode = desc_handle;
srcDesc_.identifier = 0;
srcDesc_.identifier_len = 0;
srcDesc_.handle = 0;
// ---------------------------------------------------------------------
// Initialize an input descriptor to deliver parameters
// ---------------------------------------------------------------------
desc_.name_mode = desc_handle;
desc_.identifier = 0;
desc_.identifier_len = 0;
desc_.handle = 0;
}
HSinsertHistint::~HSinsertHistint()
{
if (inputDescAllocated_)
SQL_EXEC_DeallocDesc(&desc_);
if (srcDescAllocated_)
SQL_EXEC_DeallocDesc(&srcDesc_);
if (stmtAllocated_)
SQL_EXEC_DeallocStmt(&stmt_);
}
void HSinsertHistint::setText()
{
char rowsetSizeStr[10];
snprintf(rowsetSizeStr, sizeof(rowsetSizeStr), "%d", MAX_ROWSET);
stmtText_ = "ROWSET FOR INPUT SIZE ? "
"INSERT INTO ";
stmtText_.append(tableName_)
.append(" VALUES (");
for (Int32 i=2; i<14; i++)
{
if (i == 8)
// cast std_dev_of_freq as a float, since that's the client data type
stmtText_.append("CAST(?[").append(rowsetSizeStr).append("] AS FLOAT), ");
else
stmtText_.append("?[").append(rowsetSizeStr).append("], ");
}
stmtText_.append("?[").append(rowsetSizeStr).append("])");
}
Lng32 HSinsertHistint::prepareDynamic()
{
HSErrorCatcher errorCatcher(retcode_, -UERR_INTERNAL_ERROR,
"HSinsertHistint::prepareDynamic()", TRUE);
HSLogMan *LM = HSLogMan::Instance();
retcode_ = SQL_EXEC_ClearDiagnostics(&stmt_);
HSHandleError(retcode_);
retcode_ = SQL_EXEC_AllocStmt(&stmt_, 0);
HSHandleError(retcode_);
stmtAllocated_ = TRUE;
retcode_ = SQL_EXEC_AllocDesc(&srcDesc_, 1);
HSHandleError(retcode_);
srcDescAllocated_ = TRUE;
retcode_ = SQL_EXEC_AllocDesc(&desc_, 14);
HSHandleError(retcode_);
inputDescAllocated_ = TRUE;
retcode_ = SQL_EXEC_SetDescItem(&srcDesc_, 1, SQLDESC_TYPE_FS,
REC_BYTE_V_ANSI, 0);
HSHandleError(retcode_);
retcode_ = SQL_EXEC_SetDescItem(&srcDesc_, 1, SQLDESC_VAR_PTR,
(Long)stmtText_.data(), 0);
HSHandleError(retcode_);
retcode_ = SQL_EXEC_SetDescItem(&srcDesc_, 1, SQLDESC_LENGTH,
stmtText_.length() + 1, 0);
HSHandleError(retcode_);
// SQLDESC_CHAR_SET must be the last descriptor item set, otherwise
// it may get reset by other calls to SQL_EXEC_SetDescItem().
NAString charSet = ActiveSchemaDB()->getDefaults().getValue(ISO_MAPPING);
NAString defCS = ActiveSchemaDB()->getDefaults().getValue(DEFAULT_CHARSET);
retcode_ = SQL_EXEC_SetDescItem(&srcDesc_, 1, SQLDESC_CHAR_SET,
SQLCHARSETCODE_UTF8, 0);
HSHandleError(retcode_);
retcode_ = SQL_EXEC_SetStmtAttr(&stmt_, SQL_ATTR_PARENT_QID, 0,
(char *)CmpCommon::context()->sqlSession()->getParentQid());
HSHandleError(retcode_);
SQL_QUERY_COST_INFO query_cost_info;
SQL_QUERY_COMPILER_STATS_INFO comp_stats_info;
LM->StartTimer("SQL_EXEC_Prepare2() from HSinsertHistint::prepareDynamic()");
retcode_ = SQL_EXEC_Prepare2(&stmt_, &srcDesc_, NULL,0,NULL,&query_cost_info, &comp_stats_info,NULL,0,0);
LM->StopTimer();
if (retcode_ < 0 && LM->LogNeeded())
{
LM->Log("Failure in preparing the following statement:");
LM->Log(stmtText_.data());
}
HSHandleError(retcode_);
retcode_ = SQL_EXEC_DescribeStmt(&stmt_, &desc_, NULL);
HSHandleError(retcode_);
return retcode_;
}
Lng32 HSinsertHistint::initialize()
{
HSErrorCatcher errorCatcher(retcode_, -UERR_INTERNAL_ERROR, "HSinsertHistint::initialize()", TRUE);
retcode_ = SQL_EXEC_ClearDiagnostics(&stmt_);
HSHandleError(retcode_);
#ifndef NA_USTAT_USE_STATIC // extra step for dynamic query
setText();
retcode_ = prepareDynamic();
HSHandleError(retcode_);
#endif
// histogram versioning
if (HSGlobalsClass::schemaVersion >= COM_VERS_2300) {
retcode_ = SQL_EXEC_SetRowsetDescPointers(&desc_
,MAX_ROWSET
,0
,1
#ifdef NA_USTAT_USE_STATIC // use static query defined in module file
,15
#else // NA_USTAT_USE_STATIC not defined, use dynamic query
,14
#endif
,0
,&(numRows_)
,(void*)0
,(void*)0
#ifdef NA_USTAT_USE_STATIC // use static query defined in module file
,0
,tableName_
,(void*)0
,(void*)0
#endif
,sizeof(Int64)
,&(tableUid_)
,(void*)0
,(void*)0
,sizeof(ULng32)
,&(histID_)
,(void*)0
,(void*)0
,sizeof(signed short)
,&(intNumber_)
,(void*)0
,(void*)0
,sizeof(Int64)
,&(intRowcount_)
,(void*)0
,(void*)0
,sizeof(Int64)
,&(intUec_)
,(void*)0
,(void*)0
,250*sizeof(wchar_t)
,&(intBoundary_[0])
,(void*)0
,(void*)0
,sizeof(double)
,&(stdDevOfFreq_)
,(void*)0
,(void*)0
,sizeof(Int64)
,&(intMFVrowcount_)
,(void*)0
,(void*)0
,sizeof(Int64)
,&(intMFV2rowcount_)
,(void*)0
,(void*)0
,sizeof(Int64)
,&(v3_)
,(void*)0
,(void*)0
,sizeof(Int64)
,&(v4_)
,(void*)0
,(void*)0
,250*sizeof(wchar_t)
,&(mostFreqVal_[0])
,(void*)0
,(void*)0
,250*sizeof(wchar_t)
,&(v6_[0])
,(void*)0
,(void*)0
);
} else {
retcode_ = SQL_EXEC_SetRowsetDescPointers(&desc_
,MAX_ROWSET
,0
,1
,8
,0
,&(numRows_)
,(void*)0
,(void*)0
,0
,tableName_
,(void*)0
,(void*)0
,sizeof(Int64)
,&(tableUid_)
,(void*)0
,(void*)0
,sizeof(ULng32)
,&(histID_)
,(void*)0
,(void*)0
,sizeof(signed short)
,&(intNumber_)
,(void*)0
,(void*)0
,sizeof(Int64)
,&(intRowcount_)
,(void*)0
,(void*)0
,sizeof(Int64)
,&(intUec_)
,(void*)0
,(void*)0
,250*sizeof(NAWchar)
,&(intBoundary_[0])
,(void*)0
,(void*)0
);
}
HSHandleError(retcode_);
return retcode_;
}
Lng32 HSinsertHistint::addRow(const Int64 table_uid,
const ULng32 histogram_id,
const short interval_number,
const Int64 interval_rowcount,
const Int64 interval_uec,
const HSDataBuffer &interval_boundary,
// the following are added in R2.3
const double std_dev_of_freq,
const Int64 interval_MFV_rowcount,
const Int64 interval_MFV2_rowcount,
const Int64 v3,
const Int64 v4,
const HSDataBuffer &mostFreqVal,
const HSDataBuffer &v6)
{
Lng32 retcode = 0;
tableUid_[numRows_] = table_uid;
histID_[numRows_] = histogram_id;
intNumber_[numRows_] = interval_number;
intRowcount_[numRows_] = interval_rowcount;
intUec_[numRows_] = interval_uec;
//set boundary_value: LEN + DATA
intBoundary_[numRows_][0] = interval_boundary.length();
memmove(&intBoundary_[numRows_][1], interval_boundary.data(), interval_boundary.length());
stdDevOfFreq_[numRows_] = std_dev_of_freq;
intMFVrowcount_[numRows_] = interval_MFV_rowcount;
intMFV2rowcount_[numRows_] = interval_MFV2_rowcount;
v3_[numRows_] = v3;
v4_[numRows_] = v4;
mostFreqVal_[numRows_][0] = mostFreqVal.length();
memmove(&mostFreqVal_[numRows_][1], mostFreqVal.data(), mostFreqVal.length());
v6_[numRows_][0] = v6.length();
memmove(&v6_[numRows_][1], v6.data(), v6.length());
if (++numRows_ == MAX_ROWSET)
{
retcode = flush();
HSHandleError(retcode);
}
return retcode;
}
Lng32 HSinsertHistint::flush()
{
HSErrorCatcher errorCatcher(retcode_, -UERR_INTERNAL_ERROR, "HSinsertHistint::flush()", TRUE);
HSLogMan *LM = HSLogMan::Instance();
if (numRows_ > 0 && retcode_ == 0)
{
retcode_ = SQL_EXEC_ExecFetch(&stmt_,&desc_,0);
if (LM->LogNeeded())
{
snprintf(LM->msg, sizeof(LM->msg), "HISTOGRAM_INTERVALS ROWSET INSERT of %d rows. Retcode=(%d)", numRows_, retcode_);
LM->Log(LM->msg);
print();
}
HSHandleError(retcode_);
numRows_ = 0;
}
return retcode_;
}
void HSinsertHistint::print()
{
HSLogMan *LM = HSLogMan::Instance();
char tableUidStr[30];
char rowCountStr[30];
char uecStr[30];
char MFVrowCountStr[30];
char MFV2rowCountStr[30];
NAString bound;
NAString mostFreqVal;
// the following are for new columns added in R2.3
//char v1Str[30], v2Str[30], v3Str[30], v4Str[30];
//NAString v5, v6;
for (Lng32 i = 0; i < numRows_; i++)
{
convertInt64ToAscii(tableUid_[i], tableUidStr);
convertInt64ToAscii(intRowcount_[i], rowCountStr);
convertInt64ToAscii(intUec_[i], uecStr);
ConvWcharToHexadecimal(&intBoundary_[i][1], intBoundary_[i][0] / 2, bound);
convertInt64ToAscii(intMFVrowcount_[i], MFVrowCountStr);
convertInt64ToAscii(intMFV2rowcount_[i], MFV2rowCountStr);
//convertInt64ToAscii(v3_[i], v3Str);
//convertInt64ToAscii(v4_[i], v4Str);
ConvWcharToHexadecimal(&mostFreqVal_[i][1], mostFreqVal_[i][0] / 2, mostFreqVal);
//ConvWcharToHexadecimal(&v6_[i][1], v6_[i][0] / 2, v6);
snprintf(LM->msg, sizeof(LM->msg), "\t( %s, %u, %d, %s, %s, %s, %f, %s, %s, %s)",
tableUidStr,
histID_[i],
intNumber_[i],
rowCountStr,
uecStr,
bound.data(),
stdDevOfFreq_[i],
MFVrowCountStr,
MFV2rowCountStr,
//v3Str,
//v4Str,
mostFreqVal.data()
//v6.data()
);
LM->Log(LM->msg);
}
}
/**********************************************************/
/* METHOD: addColumn(const Lng32 columnNumber) */
/* PURPOSE: This method is used to add a column in a set */
/* of columns that will be used to construct */
/* empty multi-column histograms */
/* PARAMS: columnNumber - the column number to be added */
/* RETCODE: 0 - successful */
/* -1 - failure */
/**********************************************************/
Lng32 HSinsertEmptyHist::addColumn(const Lng32 columnNumber)
{
HSLogMan *LM = HSLogMan::Instance();
if (colCount_ >= MAX_MC_COLUMNS)
{
if (LM->LogNeeded())
{
snprintf(LM->msg, sizeof(LM->msg), "\t***[ERROR] HSinsertEmptyHist::addColumn: ColCount reaches maximum(%d)\n",
MAX_MC_COLUMNS);
LM->Log(LM->msg);
}
return -1;
}
colNumber_[colCount_++] = columnNumber;
if (LM->LogNeeded())
{
snprintf(LM->msg, sizeof(LM->msg), "\tHSinsertEmptyHist::addColumn: add a colume(%d)\n",
columnNumber);
LM->Log(LM->msg);
}
return 0;
}
/**********************************************************/
/* METHOD: insert() */
/* PURPOSE: This method is used to insert empty */
/* multi-column histograms into the Histograms */
/* table based on the columns in colNumber_. */
/* RETCODE: 0 - successful */
/* -1 - failure */
/* NOTES: If the empty histograms exist, will not */
/* insert again. */
/**********************************************************/
Lng32 HSinsertEmptyHist::insert()
{
HSGlobalsClass::schemaVersion = getTableSchemaVersion(histTable_);
Lng32 retcode = 0;
ULng32 histid = 0;
Lng32 colPos = 0;
Lng32 colNum = 0;
Lng32 colCnt = 0;
char reason = HS_REASON_UNKNOWN;
char readTime[TIMESTAMP_CHAR_LEN+1]; // not used, but retrieved by cursor used
ULng32 maxHistid = 0;
ULng32 prevHistid = 0;
Lng32 matchCount = 0;
DefaultToken jit;
HSLogMan *LM = HSLogMan::Instance();
if (colCount_ == 0) return 0; // it is ok, but do nothing
Lng32 maxMCWidthForAutomation = (Lng32) CmpCommon::getDefaultLong(USTAT_AUTO_MC_MAX_WIDTH);
if (colCount_ > maxMCWidthForAutomation)
{
if (LM->LogNeeded())
{
snprintf(LM->msg, sizeof(LM->msg), "HSinsertEmptyHist::insert: Skipping insert of MC stats for Table(%s), ColCount(%d) > CQD(%d)\n",
tableName_, colCount_, maxMCWidthForAutomation);
LM->Log(LM->msg);
}
return 0;
}
if ((jit = CmpCommon::getDefault(USTAT_JIT_LOGGING)) == DF_ON) // If JIT logging is on, turn it off here.
HSFuncExecQuery("CONTROL QUERY DEFAULT USTAT_JIT_LOGGING 'OFF'"); // do not check for error
if (LM->LogNeeded())
{
snprintf(LM->msg, sizeof(LM->msg), "\nHSinsertEmptyHist::insert: Table(%s), Histograms(%s), ColCount(%d)\n",
tableName_, histTable_, colCount_);
LM->Log(LM->msg);
}
LM->LogTimeDiff("START GET EXISTING HISTOGRAMS BEFORE INSERT EMPTY ONES");
HSTranController TC("INSERT EMPTY HISTOGRAMS", &retcode);
HSLogError(retcode);
// find max hist id and check existing empty histogram
#ifdef NA_USTAT_USE_STATIC // use static query defined in module file
HSCliStatement::statementIndex stmt = HSCliStatement::CURSOR104_MX_2300;
HSCliStatement cursor( stmt,
(char *)histTable_,
(char *)&tableUID_
);
#else // NA_USTAT_USE_STATIC not defined, use dynamic query
char sbuf[25];
NAString qry = "SELECT HISTOGRAM_ID, COL_POSITION, COLUMN_NUMBER, COLCOUNT, "
"cast(READ_TIME as char(19) character set iso88591), REASON "
"FROM ";
qry.append(histTable_);
qry.append( " WHERE TABLE_UID = ");
snprintf(sbuf, sizeof(sbuf), PF64, tableUID_);
qry.append(sbuf);
qry.append( " ORDER BY TABLE_UID, HISTOGRAM_ID, COL_POSITION ");
qry.append( " FOR READ UNCOMMITTED ACCESS");
HSCursor cursor;
retcode = cursor.prepareQuery(qry.data(), 0, 6);
HSLogError(retcode);
#endif // NA_USTAT_USE_STATIC not defined
retcode = cursor.open();
HSLogError(retcode);
// results should be ordered by hist id and col position
// check if the same histograms exist and get max hist id
while ( 0 == (retcode = cursor.fetch(6, (void *)&histid, (void *)&colPos,
(void *)&colNum, (void *)&colCnt,
(void *)&readTime, (void *)&reason)) )
{
maxHistid = MAXOF(maxHistid, histid);
if (histid != prevHistid) matchCount = 0; // reset matchcount if new histogram.
if (colCnt == colCount_) // found histogram with same # cols.
for (Lng32 i=0; i<colCount_; i++)
if (colNumber_[i] == colNum) { matchCount++; break; } // Hist contains same col.
if (matchCount == colCount_) break; // Histogram to insert already exists.
prevHistid = histid;
}
if (retcode != HS_EOF) HSLogError(retcode);
retcode = cursor.close();
HSLogError(retcode);
if (CmpCommon::getDefault(USTAT_AUTO_EMPTYHIST_TWO_TRANS) == DF_ON)
TC.stopStart("INSERT EMPTY HIST - END SELECT START INSERT");
LM->LogTimeDiff("END GET EXISTING HISTOGRAMS BEFORE INSERT EMPTY ONES");
// do not insert empty histograms if they exist
if (matchCount == colCount_ || retcode < 0)
{
if (LM->LogNeeded() && matchCount == colCount_)
{
snprintf(LM->msg, sizeof(LM->msg), "\tThere are existing empty histograms for Hist Id(%d). No new empty histograms will be inserted.\n",
histid);
LM->Log(LM->msg);
}
if (jit == DF_ON) // Turn JIT logging back on if it was ON when function was entered.
HSFuncExecQuery("CONTROL QUERY DEFAULT USTAT_JIT_LOGGING 'ON'"); // do not check for error
return retcode;
}
LM->LogTimeDiff("START INSERT EMPTY HISTOGRAMS");
#ifdef NA_USTAT_USE_STATIC // use static query defined in module file
HSinsertHist* histRS=new HSinsertHist("INSERT104_MX_2300", histTable_);
#else // NA_USTAT_USE_STATIC not defined, use dynamic query
HSinsertHist* histRS=new HSinsertHist(histTable_);
#endif // NA_USTAT_USE_STATIC not defined
// The format expected for an input parameter that is a timestamp value
// depends on the INTERNAL_FORMAT_IO session default. Sqlci sets this to 1
// (meaning use internal format), which affects us if running from sqlci
// and using the embedded compiler. We switch to external format if necessary,
// to match the constant we provide, and switch back afterwards. It must be
// set before calling initialize(), because that includes compiling the
// statement, which sets up its input descriptor.
SessionDefaults* sessionDefaults = GetCliGlobals()->currContext()->getSessionDefaults();
NABoolean usingInternalFormat = sessionDefaults->getInternalFormatIO();
if (usingInternalFormat)
sessionDefaults->setInternalFormatIO(FALSE);
try
{
retcode = histRS->initialize();
if (maxHistid ==0)
{
Int64 timeStamp = NA_JulianTimestamp();
maxHistid = (ULng32) (timeStamp & 0x7FFFFFFF); //histogram ID = Julian Timestamp masked with 0x7FFFFFFF
}
else
// Since we are in a transaction, simply add a 5 to the max histid found.
maxHistid += 5;
Lng32 histId = maxHistid;
const char* statsTime = "0001-01-01 00:00:00"; // external format
HSDataBuffer lval(L"()");
HSDataBuffer hval(L"()");
//HSDataBuffer v5(L"");
//HSDataBuffer v6(L"");
for (Lng32 i = 0; i < colCount_; i++)
{
retcode = histRS->addRow( tableUID_,
histId,
i, // col position
colNumber_[i], // position in table
colCount_, // number of columns
0,
0,
0,
statsTime,
lval,
hval,
statsTime,
0,
0,
0,
0,
0,
HS_REASON_EMPTY);
//0,0,0,0,v5,v6);
HSLogError(retcode);
if (LM->LogNeeded())
{
snprintf(LM->msg, sizeof(LM->msg), "\tAdd an empty histogram: HistID(%d),ColPos(%d),ColNum(%d),ColCount(%d),retcode(%d)\n",
histId, i, colNumber_[i], colCount_, retcode);
LM->Log(LM->msg);
}
} // for
retcode = histRS->flush();
HSLogError(retcode);
if (LM->LogNeeded())
{
snprintf(LM->msg, sizeof(LM->msg), "Flush empty histograms: retcode(%d)\n", retcode);
LM->Log(LM->msg);
}
// Switch back to internal timestamp format if we changed it above.
if (usingInternalFormat)
sessionDefaults->setInternalFormatIO(TRUE);
delete histRS;
histRS = NULL;
}
catch(...)
{
// Make sure session value is reset and histRS is deleted in case of an exception.
if (usingInternalFormat)
sessionDefaults->setInternalFormatIO(TRUE);
delete histRS;
throw;
}
LM->LogTimeDiff("END INSERT EMPTY HISTOGRAMS");
if (jit == DF_ON) // Turn JIT logging back on if it was ON when function was entered.
HSFuncExecQuery("CONTROL QUERY DEFAULT USTAT_JIT_LOGGING 'ON'"); // do not check for error
return retcode;
}
Lng32 HSDataBuffer::addParenthesis()
{
Lng32 retcode = 0;
NAWString tempBuf(WIDE_("("));
tempBuf.append((NAWchar*)this->data(), this->length() / sizeof(NAWchar));
tempBuf.append(WIDE_(")"));
if (tempBuf.length() * sizeof(NAWchar) > HS_MAX_BOUNDARY_LEN)
{
retcode = -1;
HSHandleError(retcode);
}
else
*this = tempBuf.data();
return retcode;
}
Lng32 HSDataBuffer::append(HSDataBuffer &dataBuffer)
{
Lng32 retcode = 0;
NAWString tempBuf(WIDE_(""));
tempBuf.append((NAWchar*)this->data(), this->length() / sizeof(NAWchar));
tempBuf.append((NAWchar*)dataBuffer.data(), dataBuffer.length() / sizeof(NAWchar));
if (tempBuf.length() * sizeof(NAWchar) > HS_MAX_BOUNDARY_LEN)
{
retcode = -1;
HSHandleError(retcode);
}
else
*this = tempBuf.data();
return retcode;
}
/*******************************************************/
/* METHOD : getLocalNodeName() */
/* */
/* PURPOSE : get the name of the local node by making */
/* a Guardian call. Returns NULL if there is an error */
/* This method is almost identical to a part of */
/* CatExecCollectNodeInfo function */
/* in catman/CatExecCommon.cpp. It is being duplicated */
/* here to avoid linking catman code here. */
/*******************************************************/
NAString* getLocalNodeName()
{
char localNodeName[9];
short actualNodeNameLen = 0;
short retcode;
//The NODENUMBER_TO_NODENAME_ procedure converts a node number (system
//number) to the corresponding node name (system name). It can also be used to
//obtain the name of the caller�s node, by passing -1 for the system number,
// the first parameter in the call.
retcode = NODENUMBER_TO_NODENAME_(-1,
localNodeName,
9-1, // leave room for NUL
&actualNodeNameLen);
if (retcode || actualNodeNameLen==0)
{
return NULL;
}
localNodeName[actualNodeNameLen]='\0';
NAString *localNode = new (STMTHEAP) NAString(localNodeName, STMTHEAP);
localNode->toUpper();
return localNode;
}
/*****************************************************/
/* METHOD: getTempTablePartitionInfo() */
/* */
/* PURPOSE: Based on the HIST_SCRATCH_VOL attribute, */
/* appropriate syntax and CQD settings are */
/* are used to create a hash partitioned */
/* temporary table where necessary. */
/* */
/* INPUT: 'unpartitionedSample' - an NABoolean which */
/* if TRUE, specifies that the sample table */
/* should not be partitioned. */
/* 'isPersSample' - NABoolean which is TRUE */
/* if the method is called to create persistent*/
/* sample table */
/* Exceptions: For SQL/MP tables, only the first */
/* value in the attribute is used to specify*/
/* the location of the single partitioned */
/* temporary table. */
/* For SQL/MX base tables that contain a */
/* system generated key, SYSKEY, only the */
/* first value in the attribute is used to */
/* specify the location of the single */
/* partitioned temporary table. */
/*****************************************************/
NAString HSSample::getTempTablePartitionInfo(NABoolean unpartitionedSample,
NABoolean isPersSample)
{
NAString tableOptions = "";
Lng32 partitionCount = 0 ;
Lng32 numScratchVols = 0;
Lng32 numPartitionsNeeded = 0;
char tempStr[4200];
const Lng32 MaxNumVols = 256; // sized to hold max possible vols
char *scratchVols[MaxNumVols];
char *thisVol;
Lng32 volIx;
NABoolean userDefinedScratchVols = FALSE;
NABoolean usePOS = ((CmpCommon::getDefault(POS) == DF_OFF) ? FALSE : TRUE);
NABoolean usePOSMultiNode = FALSE ;
NABoolean usePOSLocalNodeWithTableSize = FALSE ;
Lng32 sampleTableSizeInMB = 0;
Lng32 scratchVolThreshold = 10240000;
if (isPersSample)
scratchVolThreshold = CmpCommon::getDefaultLong(HIST_FETCHCOUNT_SCRATCH_VOL_THRESHOLD);
else
scratchVolThreshold = CmpCommon::getDefaultLong(HIST_SCRATCH_VOL_THRESHOLD);
HSLogMan *LM = HSLogMan::Instance();
LM->Log("Creating partitioning scheme for sample table.");
//Allow user to specify volume location(s) for temporary table
//through HIST_SCRATCH_VOL.
//
//10-050217-4819: Make sure the LOCATION clause is either before or
//after the ATTRIBUTE clause. Normally, we would place it before the
//ATTRIBUTE clause, but since it is not often used, I'm putting it after.
//
//CASE: 10-041025-8056
//Make sure the temporary table is not restricted by the primary
//partitions's extent sizes. The temporary table size should be
//unrestricted, except for limited disk space on the volume.
/*=========================================*/
/* RETRIEVE USER-DEFINED SCRATCH VOLUMES */
/*=========================================*/
CmpCommon::getDefault(HIST_SCRATCH_VOL, tableOptions);
HS_ASSERT(tableOptions.length() <= 4096);
strcpy(tempStr, tableOptions.data());
tableOptions = "";
if (strlen(tempStr))
{
const char *sep = " ,:" ;
char *token = strtok(tempStr, sep);
/* STORE VOLUMES IN ARRAY */
while (token != NULL &&
numScratchVols < MaxNumVols)
{
scratchVols[numScratchVols++] = token;
token = strtok(NULL, sep);
}
//Even after parsing the CQD for volume tokens, it is still possible
//to end up with an empty volume list.
if (numScratchVols > 0)
userDefinedScratchVols = TRUE;
}
/*=========================================*/
/* DETERMINE HOW MANY PARITIONS NEEDED */
/*=========================================*/
// We cannot use hash partitioning scheme when,
// 1. this is an MP tables
// 2. this table has a primary key containing only a system generated column, SYSKEY.
// 3. if USEPOS is false and there are no user defined scratch volumes.
// 4. The table is an hbase table.
// In any of these case, the sample table will be single partitioned.
if (objDef->getObjectFormat() == SQLMP
// Set by HSTableDef::getLabelInfo().
|| (usePOS == FALSE && userDefinedScratchVols == FALSE)
|| HSGlobalsClass::isTrafodionCatalog(objDef->getCatName()) )
{
numPartitionsNeeded = 1;
if (objDef->hasSyskey())
{
usePOS = FALSE;
LM->Log("SYSKEY primary key - sample table will not have partitions.");
HSFuncExecQuery("CONTROL QUERY DEFAULT POS 'OFF'");
}
}
else
// if the table is a native HBase or Hive table
if ( HSGlobalsClass::isNativeCat(objDef->getCatName()) )
{
usePOS = FALSE;
numPartitionsNeeded = (Lng32) ceil((double)sampleRowCount
*
objDef->getRecordLength()
/
scratchVolThreshold
);
NADefaults &defs = CmpCommon::context()->schemaDB_->getDefaults();
NABoolean fakeEnv= FALSE;
ComUInt32 numConfiguredESPs = defs.getTotalNumOfESPsInCluster(fakeEnv);
numPartitionsNeeded = MINOF(numConfiguredESPs, numPartitionsNeeded);
numPartitionsNeeded = MAXOF(1, numPartitionsNeeded);
LM->Log("SYSKEY primary key.");
snprintf(LM->msg, sizeof(LM->msg), "Partitions Needed: %d (TableType=%s, ~SampleSet=%d, RecLen=%d, threshold=%d)"
, numPartitionsNeeded
, "NATIVE"
, (Lng32)sampleRowCount
, (Lng32)objDef->getRecordLength()
, scratchVolThreshold);
LM->Log(LM->msg);
if ( numPartitionsNeeded > 1 ) {
tableOptions +=" salt using ";
snprintf(tempStr, sizeof(tempStr), "%d", numPartitionsNeeded);
tableOptions += tempStr;
tableOptions +=" partitions on( ";
NAFileSet *naSet = objDef->getNATable()->getClusteringIndex();
tableOptions += naSet->getBestPartitioningKeyColumns(',');
tableOptions +=" ) ";
}
return tableOptions;
} else {
numPartitionsNeeded = (Lng32) ceil((double)sampleRowCount
*
objDef->getRecordLength()
/
scratchVolThreshold
);
// For both IUS and RUS, make sure the number of partitions of the source table
// is a multiple of numPartitionsNeeded.
if ( CmpCommon::getDefault(USTAT_USE_GROUPING_FOR_SAMPLING) == DF_ON )
{
Lng32 tblPartns = objDef->getNumPartitions();
if ( tblPartns < numPartitionsNeeded )
numPartitionsNeeded = tblPartns;
if ( numPartitionsNeeded > 0 )
while ( tblPartns % numPartitionsNeeded != 0 )
numPartitionsNeeded++;
// Force to take the default code path of local node POS mode
// (which is identical to multi-node POS for SQ) to set the number of
// partitions for POS. That is, the # of partns of the sample table
// is specified via CQD POS_NUM_OF_PARTNS.
usePOSMultiNode = FALSE;
usePOSLocalNodeWithTableSize = FALSE;
} else {
// part of fix/workaround to bugzilla 2784: we need to guard against
// partitioning the sample table in a way that mxcmp is unable to
// generate a parallel plan for the sideinsert to populate it.
// For now, choosing numPartitionsNeeded to be even is safe.
if (((numPartitionsNeeded % 2) != 0) AND (numPartitionsNeeded > 1))
--numPartitionsNeeded;
} // end of USTAT_USE_GROUPING_FOR_SAMPLING is OFF
} // end of non hive tables
/*=========================================*/
/* FLOAT PRIMARY KEY - NO PARTITIONING */
/*=========================================*/
if (unpartitionedSample && objDef->getObjectFormat() == SQLMX)
{
LM->Log("Float type primary key - sample table will not have partitions.");
tableOptions += HS_EXTENT_SIZE_MX;
tableOptions +=" NO PARTITION ";
}
/*=========================================*/
/* DEFAULT PARTITIONING SCHEME */
/*=========================================*/
else if (NOT userDefinedScratchVols)
{
if (usePOS && objDef->getObjectFormat() == SQLMX)
{
if (usePOSMultiNode)
{
HSFuncExecQuery("CONTROL QUERY DEFAULT POS 'MULTI_NODE'");
tableOptions +=" MAX TABLE SIZE ";
tableOptions.append(LongToNAString(sampleTableSizeInMB));
LM->Log("Setting POS to MULTI_NODE");
snprintf(LM->msg, sizeof(LM->msg), "Sample Table Size (in MB): %d (~SampleSet=%d, RecLen=%d)"
, sampleTableSizeInMB
, (Lng32)sampleRowCount
, (Lng32)objDef->getRecordLength());
}
else if (usePOSLocalNodeWithTableSize)
{
HSFuncExecQuery("CONTROL QUERY DEFAULT POS 'LOCAL_NODE'");
tableOptions +=" MAX TABLE SIZE ";
tableOptions.append(LongToNAString(sampleTableSizeInMB));
LM->Log("Setting POS to LOCAL_NODE");
snprintf(LM->msg, sizeof(LM->msg), "Sample Table Size (in MB): %d (NumPartitionsCreated=%d, ~SampleSet=%d, RecLen=%d)"
, sampleTableSizeInMB
, numPartitionsNeeded
, (Lng32)sampleRowCount
, (Lng32)objDef->getRecordLength());
LM->Log(LM->msg);
}
else
{
//Due to performance reasons - we want POS to only choose volumes
//on the local node, for small sample tables.
HSFuncExecQuery("CONTROL QUERY DEFAULT POS 'LOCAL_NODE'");
snprintf(tempStr, sizeof(tempStr), "CONTROL QUERY DEFAULT POS_NUM_OF_PARTNS '%d'", numPartitionsNeeded);
HSFuncExecQuery(tempStr);
tableOptions += HS_EXTENT_SIZE_MX;
snprintf(LM->msg, sizeof(LM->msg), "Partitions Needed: %d (TableType=%s, ~SampleSet=%d, RecLen=%d, threshold=%d)"
, numPartitionsNeeded
, (objDef->getObjectFormat() == SQLMP) ? "MP" : "MX"
, (Lng32)sampleRowCount
, (Lng32)objDef->getRecordLength()
, scratchVolThreshold);
LM->Log(LM->msg);
}
}
else {
LM->Log("Not using POS for sample table.");
tableOptions += HS_EXTENT_SIZE_MX;
}
}
/*=========================================*/
/* PARTITIONING BASED ON SCRATCH VOLUMES */
/*=========================================*/
else
{
//Disable Partition Overlay Support (POS) when user specified scratch
//volumes.
LM->Log("User has specified scratch volumes for sample. Turning POS off.");
HSFuncExecQuery("CONTROL QUERY DEFAULT POS 'OFF'");
//CASE: 10-041025-8056
//Make sure the temporary table is not restricted by the primary
//partitions's extent sizes. The temporary table size should be
//unrestricted, except for limited disk space on the volume.
tableOptions += HS_EXTENT_SIZE_MX;
// Determine a random starting index in the scratchVols array. This
// will provide better performance, in general, when customers run
// multiple concurrent update stats jobs.
volIx = (Lng32)(NA_JulianTimestamp() % (Int64)numScratchVols);
thisVol = scratchVols[volIx];
//for SQL/MP tables, user may only specify the location of the
//temporary sample table. It will always be single partitioned.
if (objDef->getObjectFormat() == SQLMP)
{
tableOptions = thisVol;
partitionCount++;
}
else
{
//For every volume specified and needed, generate a syntactically
//correct HASH PARTITION.
while ( partitionCount < numPartitionsNeeded &&
partitionCount < numScratchVols)
{
switch (partitionCount)
{
case 0: //first one -- LOCATION CLAUSE
tableOptions = " LOCATION ";
tableOptions.append(thisVol);
partitionCount++;
break;
case 1: //initialize hash-partition clause
tableOptions.append(" HASH PARTITION ( ADD LOCATION ");
tableOptions.append(thisVol);
tableOptions.append(HS_EXTENT_SIZE_MX);
partitionCount++;
break;
default: //additional hash partitions
tableOptions.append( ", ADD LOCATION ");
tableOptions.append(thisVol);
tableOptions.append(HS_EXTENT_SIZE_MX);
partitionCount++;
break;
}
thisVol = scratchVols[(volIx + partitionCount) % numScratchVols];
} // end of while
}
if ( partitionCount > 1 )
tableOptions.append(")");
}
snprintf(LM->msg, sizeof(LM->msg), "returning tableOptions: %s", tableOptions.data());
LM->Log(LM->msg);
return tableOptions;
}
// Print the heading for the display of a query plan to the log.
void printPlanHeader(HSLogMan *LM)
{
snprintf(LM->msg, sizeof(LM->msg), "\t%-3s %-3s %-3s %-20s %-8s %-20s %-11s %-11s %-11s"
, "LC"
, "RC"
, "OP"
, "OPERATOR"
, "OPT"
, "DESCRIPTION"
, "CARDINALITY"
, "OPER_COST"
, "TOT_COST"
);
LM->Log(LM->msg);
snprintf(LM->msg, sizeof(LM->msg), "\t%-3s %-3s %-3s %-20s %-8s %-20s %-11s %-11s %-11s"
, "---"
, "---"
, "---"
, "--------------------"
, "--------"
, "--------------------"
, "-----------"
, "-----------"
, "-----------"
);
LM->Log(LM->msg);
}
#ifdef NA_USTAT_USE_STATIC
/***********************************************/
/* METHOD: printPlan(SQLSTMT_ID*) */
/* PURPOSE: Write plan information pertaining */
/* to query to the log. */
/* RETCODE: 0 - successful */
/* -1 - failure */
/* INPUT: none */
/***********************************************/
Lng32 printPlan(SQLSTMT_ID *stmt)
{
Lng32 retcode = 0;
char lc[4], //left child
rc[4], //right child
seqNum[4], //sequence number
oper[21], //operator
opt[9], //optimization
desc[21], //description
cardinality[12], //cardinality
operCost[12], //operator cost
totalCost[12]; //total cost
HSLogMan *LM = HSLogMan::Instance();
HSCliStatement prntPlan(HSCliStatement::PRINTPLAN,
(char *)stmt->identifier);
retcode = prntPlan.open();
HSHandleError(retcode);
printPlanHeader(LM);
while (retcode == 0)
{
retcode = prntPlan.fetch(9
,(void *)&lc[0]
,(void *)&rc[0]
,(void *)&seqNum[0]
,(void *)&oper[0]
,(void *)&opt[0]
,(void *)&desc[0]
,(void *)&cardinality[0]
,(void *)&operCost[0]
,(void *)&totalCost[0]
);
if (retcode == 0)
{
snprintf(LM->msg, sizeof(LM->msg), "\t%-3s %-3s %-3s %-20s %-8s %-20s %-11s %-11s %-11s"
, lc
, rc
, seqNum
, oper
, opt
, desc
, cardinality
, operCost
, totalCost
);
LM->Log(LM->msg);
}
else if (retcode != 100)
HSLogError(retcode);
}
retcode = prntPlan.close();
HSHandleError(retcode);
return retcode;
}
#else // NA_USTAT_USE_STATIC not defined
static char ppStmtText[] =
"select"
// LEFT CHILD
" case"
" when LEFT_CHILD_SEQ_NUM is null then"
" '. '"
" else"
" cast(cast(LEFT_CHILD_SEQ_NUM as numeric(3)) as char(3) character set iso88591)"
" end"
""
// RIGHT CHILD
" , case"
" when RIGHT_CHILD_SEQ_NUM is null then"
" '. '"
" else"
" cast(cast(RIGHT_CHILD_SEQ_NUM as numeric(3)) as char(3) character set iso88591)"
" end"
""
// SEQUENCE NUMBER
" , cast(cast(SEQ_NUM as numeric(3)) as char(3) character set iso88591)"
""
// OPERATOR
" , cast(substring(lower(OPERATOR) from 1 for 20) as char(20))"
""
// OPTIMIZATION
" , cast (case when position('olt_opt_lean: used' in description) > 0 then 'ol ' else"
" case when position('olt_optimization: used' in description) > 0 then 'o ' else '' end end"
" ||"
" case when position('fast_scan: used' in description) > 0 then 'fs ' else '' end"
" ||"
" case when position('fast_replydata_move: used' in description) > 0 then 'fr ' else '' end"
" as char(8))"
""
// DESCRIPTION
" , cast(substring("
" case"
" when OPERATOR = 'ROOT' then"
" case"
" when position('upd_action_on_error: return' IN DESCRIPTION) > 0 then 'r'"
" when position('upd_action_on_error: xn_rollback' IN DESCRIPTION) > 0 then 'x'"
" when position('upd_action_on_error: partial_upd' IN DESCRIPTION) > 0 then 'p'"
" when position('upd_action_on_error: savepoint' IN DESCRIPTION) > 0 then 's'"
" else ' '"
" end"
" when OPERATOR in ('INDEX_SCAN', 'INDEX_SCAN_UNIQUE') then"
" substring("
" substring("
" substring("
" DESCRIPTION from position("
" 'index_scan' in DESCRIPTION"
" )"
" )"
" from ("
" position("
" '.' in substring("
" DESCRIPTION from position("
" 'index_scan' in DESCRIPTION"
" )"
" )"
" ) + 1"
" )"
" )"
" from ("
" position("
" '.' in substring("
" substring("
" DESCRIPTION from position("
" 'index_scan' in DESCRIPTION"
" )"
" )"
" from ("
" position("
" '.' in substring("
" DESCRIPTION from position("
" 'index_scan' in DESCRIPTION"
" )"
" )"
" ) + 1"
" )"
" )"
" ) + 1"
" )"
" for ("
" position("
" '(' in substring("
" substring("
" substring("
" DESCRIPTION from position("
" 'index_scan' in DESCRIPTION"
" )"
" )"
" from ("
" position("
" '.' in substring("
" DESCRIPTION from position("
" 'index_scan' in DESCRIPTION"
" )"
" )"
" ) + 1"
" )"
" )"
" from ("
" position("
" '.' in substring("
" substring("
" DESCRIPTION from position("
" 'index_scan' in DESCRIPTION"
" )"
" )"
" from ("
" position("
" '.' in substring("
" DESCRIPTION from position("
" 'index_scan' in DESCRIPTION"
" )"
" )"
" ) + 1"
" )"
" )"
" ) + 1"
" )"
" )"
" ) - 1"
" )"
" )"
" || ' ('"
" || substring(DESCRIPTION"
" from (position('key_type' in DESCRIPTION) + 10)"
" for 1)"
" || ')'"
" when OPERATOR in ('ESP_EXCHANGE', 'SPLIT_TOP') then"
" trim(substring(DESCRIPTION"
" from (position('parent_processes' in DESCRIPTION) + 18)"
" for (position(' ' in substring(DESCRIPTION"
" from (position('parent_processes' in DESCRIPTION) + 18)"
" )"
" ) - 1"
" )"
" )"
" )"
" || case position('top_partitioning_function' in DESCRIPTION)"
" when 0 then"
" ':'"
" else"
" '(' ||"
" case position('replicate no broadcast' in substring(DESCRIPTION"
" from (position('top_partitioning_function' in DESCRIPTION) + 27)"
" for 22"
" )"
" )"
" when 0 then"
" case position('replicate via broadcast' in substring(DESCRIPTION"
" from (position('top_partitioning_function' in DESCRIPTION) + 27)"
" for 23"
" )"
" )"
" when 0 then"
" trim(substring(DESCRIPTION"
" from (position('top_partitioning_function' in DESCRIPTION) + 27)"
" for 5"
" )"
" )"
" else"
" 'rep-b'"
" end"
" else"
" 'rep-n'"
" end ||"
" '):'"
" end"
" || trim(substring(DESCRIPTION"
" from (position('child_processes' in DESCRIPTION) + 17)"
" for (position(' ' in substring(DESCRIPTION"
" from (position('child_processes' in DESCRIPTION) + 17)"
" )"
" ) - 1"
" )"
" )"
" )"
" || case position('bottom_partitioning_function' in DESCRIPTION)"
" when 0 then"
" ' '"
" else"
" '(' ||"
" case position('replicate no broadcast' in substring(DESCRIPTION"
" from (position('bottom_partitioning_function' in DESCRIPTION) + 30)"
" for 22"
" )"
" )"
" when 0 then"
" case position('replicate via broadcast' in substring(DESCRIPTION"
" from (position('bottom_partitioning_function' in DESCRIPTION) + 30)"
" for 23"
" )"
" )"
" when 0 then"
" trim(substring(DESCRIPTION"
" from (position('bottom_partitioning_function' in DESCRIPTION) + 30)"
" for 5"
" )"
" )"
" else"
" 'rep-b'"
" end"
" else"
" 'rep-n'"
" end ||"
" ')'"
" end"
" else"
" trim("
" trailing ')' from trim("
" trailing ' ' from substring("
" substring("
" TNAME"
" from ("
" position("
" '.' in TNAME"
" ) + 1"
" )"
" )"
" from ("
" position("
" '.' in substring("
" TNAME"
" from ("
" position("
" '.' in TNAME"
" ) + 1"
" )"
" )"
" ) + 1"
" )"
" )"
" )"
" )"
" || case position('key_type' in DESCRIPTION)"
" when 0 then"
" ' '"
" else"
" ' (' || substring(DESCRIPTION"
" from (position('key_type' in DESCRIPTION) + 10)"
" for 1"
" )"
" || ')'"
" end"
" end"
" from 1 for 20) as char(20))"
""
// CARDINALITY
" , CAST(CARDINALITY AS CHAR(11) character set iso88591)"
""
// OPERATOR COST
" , CAST(OPERATOR_COST AS CHAR(11) character set iso88591)"
""
// TOTAL COST
" , CAST(TOTAL_COST AS CHAR(11) character set iso88591)"
""
" FROM TABLE(EXPLAIN(NULL, '";
// Version of printPlan() that gets compiled if using dynamic queries.
Lng32 printPlan(SQLSTMT_ID *stmt)
{
Lng32 retcode = 0;
struct
{
char lc[4]; //left child
char rc[4]; //right child
char seqNum[4]; //sequence number
char oper[21]; //operator
char opt[9]; //optimization
char desc[21]; //description
char cardinality[12]; //cardinality
char operCost[12]; //operator cost
char totalCost[12]; //total cost
} row;
HSLogMan *LM = HSLogMan::Instance();
HSFuncExecQuery("CQD DEFAULT_CHARSET 'ISO88591'"); // to avoid buffer overruns in row
NAString ppStmtStr = ppStmtText;
ppStmtStr.append((char *)stmt->identifier).append("')) ORDER BY SEQ_NUM DESC;");
HSCursor ppCursor(STMTHEAP, "HS_CLI_PPSTMT");
retcode = ppCursor.prepareQuery(ppStmtStr.data(), 0, 9);
HSHandleError(retcode);
SQLSTMT_ID* ppStmtId = ppCursor.getStmt();
SQLDESC_ID* ppOutputDesc = ppCursor.getOutDesc();
retcode = ppCursor.open();
HSFilterWarning(retcode);
HSHandleError(retcode);
printPlanHeader(LM);
memset(&row, '\0', sizeof row);
while (retcode == 0)
{
retcode = SQL_EXEC_Fetch(ppStmtId, ppOutputDesc, 9
,(void*)&row.lc[0], NULL
,(void*)&row.rc[0], NULL
,(void*)&row.seqNum[0], NULL
,(void*)&row.oper[0], NULL
,(void*)&row.opt[0], NULL
,(void*)&row.desc[0], NULL
,(void*)&row.cardinality[0], NULL
,(void*)&row.operCost[0], NULL
,(void*)&row.totalCost[0], NULL
);
if (retcode == 0)
{
snprintf(LM->msg, sizeof(LM->msg), "\t%-3s %-3s %-3s %-20s %-8s %-20s %-11s %-11s %-11s"
, row.lc
, row.rc
, row.seqNum
, row.oper
, row.opt
, row.desc
, row.cardinality
, row.operCost
, row.totalCost
);
LM->Log(LM->msg);
}
else if (retcode != 100)
{
HSFuncExecQuery("CQD DEFAULT_CHARSET RESET");
HSLogError(retcode);
}
}
// ppStmtId will be closed by ~HSCursor if closeStmtNeeded_ is set.
HSFuncExecQuery("CQD DEFAULT_CHARSET RESET");
return retcode;
}
#endif // NA_USTAT_USE_STATIC not defined
// Use a query on the Explain virtual table to see if the plan for stmt uses
// MDAM, and include this information in the ulog. This function is called
// only if logging is turned on.
Lng32 checkMdam(SQLSTMT_ID *stmt)
{
Lng32 retcode = 0;
HSLogMan *LM = HSLogMan::Instance();
NAString stmtStr = "select cast(count(*) as int) from table(explain(null,'";
stmtStr.append((char*)stmt->identifier)
.append("')) where description like '%mdam%'");
HSCursor cursor(STMTHEAP, "HS_CLI_CHECK_MDAM");
retcode = cursor.prepareQuery(stmtStr.data(), 0, 1);
HSHandleError(retcode);
SQLSTMT_ID* stmtId = cursor.getStmt();
SQLDESC_ID* outputDesc = cursor.getOutDesc();
retcode = cursor.open();
HSHandleError(retcode);
Int32 rowCount = 0;
retcode = SQL_EXEC_Fetch(stmtId, outputDesc, 1, &rowCount, NULL);
if (retcode == 0)
{
if (rowCount > 0)
LM->Log("MDAM is used for this query.");
else
LM->Log("MDAM is NOT used for this query.");
}
else if (retcode == 100)
LM->Log("MDAM check query returned no rows.");
else
{
LM->Log("MDAM check query failed.");
HSLogError(retcode);
}
return retcode;
}
/***********************************************/
/* METHOD: getRowCountFromStats(Int64* ) */
/* PURPOSE: Get row count from stats for the */
/* previously executed statement. */
/* Currently this method cannot access*/
/* stats for an arbitrary statement. */
/* Used to get an accurate value for */
/* rowcount when EID sampling is used */
/* INPUT: Int64* rowsAffected */
/***********************************************/
void getRowCountFromStats(Int64 * rowsAffected, const HSTableDef *tabDef)
{
// 9/18/2013: The query underlying this function no longer works. It depended
// on specific information (in a specific location) for the variable_info
// column of the Statistics virtual table, which seems to have changed. The
// necessary info (table name and # accessed rows) is not present in a single
// row of that table.
return;
Lng32 retcode = 0;
char rowcount[31];
char tabName[600] ;
if (!tabDef) return;
str_pad(tabName, 600, ' ') ;
// longest valid ANSI name is 128*3. Allowing some extra space
// for funny delimited names.
if (tabDef->getObjectFullName().length() > 596) return;
tabName[0] = '%';
strcpy(&(tabName[1]), tabDef->getObjectFullName().data());
tabName[tabDef->getObjectFullName().length()+1] = ' ';
tabName[tabDef->getObjectFullName().length()+2] = '%';
tabName[tabDef->getObjectFullName().length()+3] = 0;
HSCliStatement getStats(HSCliStatement::ROWCOUNT_FROM_STATS,
(char *)&tabName);
retcode = getStats.open();
if (retcode !=0 ) return ;
retcode = getStats.fetch(1,(void *)&rowcount[0]);
if ((retcode != 0) && (retcode !=100)) return;
retcode = getStats.close();
if (retcode !=0 ) return ;
*rowsAffected = atoInt64(rowcount) ;
return ;
}
/**************************************************************************/
/* METHOD: ucsToDouble() */
/* PURPOSE: Interpret the Unicode string pointed to by ucs as a numeric */
/* type and return it as a double precision value. */
/* PARAMS: ucs(in) -- The Unicode representation of the value. */
/* RETCODE: Double precision value corresponding to the input string. */
/**************************************************************************/
double ucsToDouble(Int16 len, const char* val)
{
// Create arrays for holding 7 byte decoded values.
static char returnVal[HS_MAX_UCS_BOUNDARY_CHAR]; // make this larger for BigNum
ComDiagsArea diagsArea;
ComDiagsArea *diagsAreaPtr = &diagsArea;
Lng32 dataConversionErrorFlag;
// Convert the argument to a value from UCS2 format and place in val.
ex_expr::exp_return_type ok =
convDoIt((char *)val,
len,
REC_NCHAR_F_UNICODE,
0, // sourcePrecision,
0, // sourceScale,
returnVal,
30,
REC_IEEE_FLOAT64,
0, // targetPrecision,
0, // targetScale,
NULL, // varCharLen
0, // varCharLenSize
STMTHEAP, // heap,
&diagsAreaPtr,
CONV_UNICODE_FLOAT64,
&dataConversionErrorFlag,
0);
return *((double*)returnVal);
}
double ucsToDouble(myVarChar* ucs)
{
return ucsToDouble(ucs->len, ucs->val);
}
double ucsToDouble(const HSDataBuffer& hsDataBuff)
{
return ucsToDouble(hsDataBuff.length(), hsDataBuff.data());
}
/****************************************************************************/
/* METHOD: profileGaps() */
/* PURPOSE: Go through the distinct values that make up a rowset, and set */
/* gap magnitude for each. The running overall gap magnitude */
/* average is updated with the information from this rowset. */
/* PARAMS: group(in) -- Group the rowset is for. */
/* boundaryRowSet(in) -- Contains the distinct values and */
/* their frequencies for this rowset. */
/* overallGapAvg(in/out) -- Average gap size for all values */
/* seen so far. Updated with */
/* information from this rowset. */
/* overallGapCount(in/out) -- Total number of gaps considered so */
/* far. Updated with the count from */
/* this rowset. */
/* firstRowset(in) -- TRUE if this is the first rowset for */
/* this group. If this is NOT the case, */
/* the static prevValue has the last */
/* value from the previous rowset. */
/****************************************************************************/
template <class T>
void profileGaps(HSColGroupStruct *group, boundarySet<T> *boundaryRowSet,
double &overallGapAvg, Int64 &overallGapCount,
NABoolean firstRowset)
{
Int32 i;
double gapAvg = 0; // avg for this rowset
// Values to compare to get gap size, which is calculated simply as the
// difference between two successive distinct values. prevValue is static so
// it can retain the last value of a rowset for comparison with the first
// value of the next rowset.
static double prevValue;
double currentValue;
Lng32 dataType = group->colSet[0].datatype;
// Get the number of non-null values in the rowset. If there is a null, it
// will be represented by the last row of the rowset, because we do an
// ascending sort and SQLMX orders null as greater than any non-null value.
Int32 numNonNullRows = boundaryRowSet->size;
if (boundaryRowSet->nullInd[boundaryRowSet->size-1] == -1)
numNonNullRows--;
// These values are adjusted immediately below for the first rowset.
Int32 numGaps = numNonNullRows;
Int32 startIndex = 0;
if (firstRowset)
{
boundaryRowSet->gapMagnitude[0] = 0; // No gap before 1st overall value
prevValue = getValueAsDouble(&boundaryRowSet->data[0]);
numGaps--; // no value from previous rowset to compare to 1st value
startIndex = 1; // for 1st rowset, prevValue is from index 0
}
// Save the lowest value used in determining gaps for this rowset (saved over
// from previous rowset except for the initial rowset), so we can calculate
// the total of gap sizes in a single step.
double lowValue = prevValue;
// Traverse the distinct values of this rowset, calculating the gap between
// each pair.
for (i=startIndex; i<numNonNullRows; i++)
{
currentValue = getValueAsDouble(&boundaryRowSet->data[i]);
boundaryRowSet->gapMagnitude[i] = (currentValue - prevValue);
prevValue = currentValue;
}
// prevValue is now the last one of this rowset, lowValue the one we started
// with; their difference is the sum of gap magnitudes for this rowset.
gapAvg = (prevValue - lowValue) / numGaps;
// Combine the average gap for this rowset with the running average so far.
// Gaps in this rowset will be compared to the current overall average.
mergeAverage(firstRowset, gapAvg, numGaps, overallGapAvg, overallGapCount);
}
// Explicit instantiation of each type
template
void profileGaps(HSColGroupStruct *, boundarySet<myVarChar>*, double&, Int64&,
NABoolean);
template
void profileGaps(HSColGroupStruct *, boundarySet<Int32>*, double&, Int64&,
NABoolean);
template
void profileGaps(HSColGroupStruct *, boundarySet<UInt32>*, double&, Int64&,
NABoolean);
template
void profileGaps(HSColGroupStruct *, boundarySet<Int8>*, double&, Int64&,
NABoolean);
template
void profileGaps(HSColGroupStruct *, boundarySet<UInt8>*, double&, Int64&,
NABoolean);
template
void profileGaps(HSColGroupStruct *, boundarySet<short>*, double&, Int64&,
NABoolean);
template
void profileGaps(HSColGroupStruct *, boundarySet<unsigned short>*, double&, Int64&,
NABoolean);
template
void profileGaps(HSColGroupStruct *, boundarySet<Int64>*, double&, Int64&,
NABoolean);
template
void profileGaps(HSColGroupStruct *, boundarySet<UInt64>*, double&, Int64&,
NABoolean);
template
void profileGaps(HSColGroupStruct *, boundarySet<ISFixedChar>*, double&, Int64&,
NABoolean);
template
void profileGaps(HSColGroupStruct *, boundarySet<ISVarChar>*, double&, Int64&,
NABoolean);
template
void profileGaps(HSColGroupStruct *, boundarySet<float>*, double&, Int64&,
NABoolean);
template
void profileGaps(HSColGroupStruct *, boundarySet<double>*, double&, Int64&,
NABoolean);
template
void profileGaps(HSColGroupStruct *, boundarySet<MCWrapper>*, double&, Int64&,
NABoolean);
//=========================================================
//@ZXnew
Lng32 HSCursor::prepareQuery(const char *cliStr, Lng32 numParams, Lng32 numResults)
{
HSLogMan *LM = HSLogMan::Instance();
LM->Log("In HSCursor::prepareQuery: Query Text ");
LM->Log(cliStr);
LM->StartTimer("HSCursor::prepareQuery()");
retcode_ = SQL_EXEC_ClearDiagnostics(stmt_);
HSHandleError(retcode_);
retcode_ = SQL_EXEC_AllocStmt(stmt_, 0);
HSHandleError(retcode_);
stmtAllocated_ = TRUE;
retcode_ = SQL_EXEC_AllocDesc(srcDesc_, 1);
HSHandleError(retcode_);
srcDescAllocated_ = TRUE;
retcode_ = SQL_EXEC_AllocDesc(outputDesc_, numResults);
HSHandleError(retcode_);
outputDescAllocated_ = TRUE;
retcode_ = SQL_EXEC_AllocDesc(inputDesc_, numParams);
HSHandleError(retcode_);
inputDescAllocated_ = TRUE;
retcode_ = SQL_EXEC_SetDescItem(srcDesc_, 1, SQLDESC_TYPE_FS,
REC_BYTE_V_ANSI, 0);
HSHandleError(retcode_);
retcode_ = SQL_EXEC_SetDescItem(srcDesc_, 1, SQLDESC_VAR_PTR,
(Long)cliStr, 0);
HSHandleError(retcode_);
retcode_ = SQL_EXEC_SetDescItem(srcDesc_, 1, SQLDESC_LENGTH,
strlen(cliStr) + 1, 0);
HSHandleError(retcode_);
// SQLDESC_CHAR_SET must be the last descriptor item set, otherwise
// it may get reset by other calls to SQL_EXEC_SetDescItem().
NAString charSet = ActiveSchemaDB()->getDefaults().getValue(ISO_MAPPING);
NAString defCS = ActiveSchemaDB()->getDefaults().getValue(DEFAULT_CHARSET);
retcode_ = SQL_EXEC_SetDescItem(srcDesc_, 1, SQLDESC_CHAR_SET,
SQLCHARSETCODE_UTF8
, 0);
HSHandleError(retcode_);
retcode_ = SQL_EXEC_SetStmtAttr(stmt_, SQL_ATTR_PARENT_QID, 0,
(char *)CmpCommon::context()->sqlSession()->getParentQid());
HSHandleError(retcode_);
SQL_QUERY_COST_INFO query_cost_info;
SQL_QUERY_COMPILER_STATS_INFO comp_stats_info;
LM->StartTimer("SQL_EXEC_Prepare2()");
// SQL_EXEC_Prepare2 is executed in an embedded compiler which might change the value
// of HSGlobalsClass::schemaVersion so we need to save it here and then restore it after
// the execution of SQL_EXEC_Prepare2
COM_VERSION schemaVersion_sav = HSGlobalsClass::schemaVersion;
retcode_ = SQL_EXEC_Prepare2(stmt_, srcDesc_,NULL,0,NULL,&query_cost_info, &comp_stats_info,NULL,0,0);
HSGlobalsClass::schemaVersion = schemaVersion_sav;
LM->StopTimer();
if (retcode_ < 0)
{
if (LM->LogNeeded())
{
LM->Log("Failure in preparing the following query:");
LM->Log(cliStr);
}
// @ZX: CLI error not visible to user without following step; investigate
// possibility that this is the case for other CLI invocations.
HSFuncMergeDiags(- UERR_INTERNAL_ERROR, NULL, NULL, TRUE);
}
HSHandleError(retcode_);
//if (LM->LogNeeded())
// {
// printPlan(stmt_);
// SQL_EXEC_ClearDiagnostics(stmt_);
// }
retcode_ = SQL_EXEC_DescribeStmt(stmt_, inputDesc_, outputDesc_);
HSHandleError(retcode_);
LM->StopTimer(); // HSCursor::prepareQuery()
return retcode_;
}
//@ZXnew
Lng32 HSCursor::open(Lng32 numParams, void * in01)
{
Lng32 retcode = SQL_EXEC_ClearDiagnostics(stmt_);
HSHandleError(retcode);
retcode = SQL_EXEC_Exec(stmt_, inputDesc_, numParams, in01, NULL);
HSHandleError(retcode);
closeStmtNeeded_ = TRUE;
lastFetchReturned100_ = FALSE;
return retcode;
}
//@ZXnew
Lng32 HSCursor::fetch(Lng32 numResults,
void * out01, void * out02, void * out03,
void * out04, void * out05, void * out06,
void * out07, void * out08, void * out09,
void * out10, void * out11, void * out12,
void * out13, void * out14, void * out15,
void * out16, void * out17, void * out18,
void * out19, void * out20, void * out21,
void * out22, void * out23, void * out24,
void * out25)
{
HS_ASSERT((numResults == 0) || (numResults > 0 && out01 != NULL));
Lng32 retcode = SQL_EXEC_Fetch(stmt_, outputDesc_, numResults,
out01, NULL, out02, NULL, out03, NULL,
out04, NULL, out05, NULL, out06, NULL,
out07, NULL, out08, NULL, out09, NULL,
out10, NULL, out11, NULL, out12, NULL,
out13, NULL, out14, NULL, out15, NULL,
out16, NULL, out17, NULL, out18, NULL,
out19, NULL, out20, NULL, out21, NULL,
out22, NULL, out23, NULL, out24, NULL,
out25, NULL);
lastFetchReturned100_ = (retcode == 100);
return retcode;
}
Lng32 HSCursor::close()
{
// This is done by the dtor ordinarily. However, if a cursor needs to
// be reused (e.g., different input parameters), we need to be able
// to close without destroying the object.
Lng32 retcode = 0;
if (closeStmtNeeded_)
{
if (lastFetchReturned100_)
{
// if the last fetch simply ran to the end of the cursor, reset
// the diags area so we don't return the 100 again on the close
SQL_EXEC_ClearDiagnostics(stmt_);
}
retcode = SQL_EXEC_CloseStmt(stmt_);
closeStmtNeeded_ = FALSE; // always set to FALSE so we don't try again
}
lastFetchReturned100_ = FALSE;
return retcode;
}
// Set the cursor name used for a dynamic statement.
Lng32 HSCursor::setCursorName(const char* name)
{
if (cursorName_)
delete cursorName_;
cursorName_ = new SQLCLI_OBJ_ID;
init_SQLCLI_OBJ_ID(cursorName_, SQLCLI_CURRENT_VERSION,
stmt_name, stmt_->module, name, 0,
SQLCHARSETSTRING_ISO88591, (Lng32)strlen(name), 0);
Lng32 retcode = SQL_EXEC_SetCursorName(stmt_, cursorName_);
return retcode;
}