| /********************************************************************** |
| // @@@ 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: ControlDB.C |
| * Description: Repository for CONTROL statements |
| * (the ControlDB retains info during compilation of multiple |
| * statements, similar to SchemaDB) |
| * Created: 6/12/96 |
| * Language: C++ |
| * |
| * |
| * |
| * |
| ***************************************************************************** |
| */ |
| |
| //##TBD: In ControlDB.[ch]*, and consumer CmpDescribe.cpp: |
| //## Save the ControlXXX::sqlText of each CQD/CT/CS |
| //## (CQS's shapeText already IS the sqlText), |
| //## and use that to reestablish Arkcmp environment |
| //## (e.g. as done in CmpDescribe's sendAllControls func). |
| |
| |
| #include "ControlDB.h" |
| |
| #include "CmpCommon.h" |
| #include "CmpContext.h" |
| #include "ComSchemaName.h" |
| |
| #include "AllRelExpr.h" // various operators for CQS |
| #include "RelPackedRows.h" |
| #include "RelSample.h" |
| #include "RelSequence.h" |
| #include "ItemColRef.h" // ConstValue for CQS |
| #include "CmpMain.h" |
| #include "QCache.h" |
| #include "CmpMemoryMonitor.h" |
| #include "OptimizerSimulator.h" |
| |
| #include "hs_log.h" |
| #include "PCodeExprCache.h" |
| #include "cli_stdh.h" |
| |
| extern THREAD_P CmpMemoryMonitor *cmpMemMonitor; |
| |
| #define CONTROLDBHEAP CTXTHEAP |
| |
| // ----------------------------------------------------------------------- |
| // global functions |
| // ----------------------------------------------------------------------- |
| |
| ControlDB * ActiveControlDB() |
| { |
| if (!CmpCommon::context()->controlDB_) |
| CmpCommon::context()->initControlDB(); |
| return CmpCommon::context()->controlDB_; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // member functions for class ControlDB |
| // ----------------------------------------------------------------------- |
| |
| #define REMOVEAT(list,i) { \ |
| delete list[i]; \ |
| list.removeAt(i); \ |
| } |
| |
| ControlDB::ControlDB() |
| :cqdList_(CONTROLDBHEAP), |
| csList_(CONTROLDBHEAP) |
| { |
| requiredShape_ = NULL; |
| requiredShapeWasOnceNonNull_ = FALSE; |
| savedRequiredShape_ = NULL; |
| outStream_ = NULL; |
| |
| ctList_ = new CONTROLDBHEAP LIST(ControlTableOptions *)(CONTROLDBHEAP); |
| savedCtList_ = NULL; |
| } |
| |
| ControlDB::~ControlDB() |
| { |
| delete requiredShape_; |
| delete ctList_; |
| } |
| |
| void ControlDB::setRequiredShape(ControlQueryShape *shape) |
| { |
| if (requiredShape_) |
| { |
| delete requiredShape_->getShape(); |
| delete requiredShape_; |
| } |
| |
| if (!shape |
| || |
| !shape->getShape() |
| || |
| //++MV Edward Bortnikov 02/25/2001 |
| // CONTROL QUERY SHAPE CUT must produce no query shape |
| shape->getShape()->isCutOp() |
| //--MV |
| ) |
| { |
| requiredShape_ = NULL; |
| return; |
| } |
| |
| requiredShape_ = (ControlQueryShape *) shape->copyTree(CONTROLDBHEAP); |
| requiredShape_->setShape(shape->getShape()->copyTree(CONTROLDBHEAP)); |
| requiredShapeWasOnceNonNull_ = TRUE; |
| } |
| |
| |
| void ControlDB::setHistogramCacheState() |
| { |
| UInt32 size = 0; |
| if (CmpCommon::getDefault(CACHE_HISTOGRAMS) == DF_ON) |
| size = (ActiveSchemaDB()->getDefaults().getAsLong |
| (CACHE_HISTOGRAMS_IN_KB)*1024); |
| CURRCONTEXT_HISTCACHE->resizeCache(size); |
| } |
| |
| extern char *__progname; |
| |
| void ControlDB::setControlDefault(ControlQueryDefault *def) |
| { |
| CMPASSERT(def->getAttrEnum() >= 0); |
| |
| if (def->getToken() == "") |
| { |
| // CONTROL QUERY DEFAULT * RESET; |
| CMPASSERT(def->reset()); |
| for (CollIndex i = 0; i < cqdList_.entries(); i++) |
| { |
| ControlQueryDefault *cqd = cqdList_.at(i); |
| const char * name = |
| ActiveSchemaDB()->getDefaults().lookupAttrName(cqd->getAttrEnum()); |
| NABoolean isResetable = |
| (NOT ActiveSchemaDB()->getDefaults().isNonResetableAttribute(name)); |
| |
| if (cqd->getAttrEnum()==ROBUST_QUERY_OPTIMIZATION) |
| resetRobustQueryOptimizationCQDs(); |
| |
| if (cqd->getAttrEnum()==CACHE_HISTOGRAMS || |
| cqd->getAttrEnum()==CACHE_HISTOGRAMS_IN_KB) |
| { |
| setHistogramCacheState(); |
| } |
| |
| if (isResetable) |
| { |
| delete cqd; |
| |
| cqdList_.removeAt(i); |
| i--; // because we just removed this pos, and now continue the loop |
| } |
| } |
| // cqdList_.clear(); |
| return; |
| } |
| |
| NABoolean removeCat = FALSE; |
| if (def->getAttrEnum() == SCHEMA) |
| { |
| ComSchemaName nam(def->getValue()); |
| if (!nam.getCatalogNamePart().isEmpty()) |
| removeCat = TRUE; |
| } |
| |
| // Remove attr in list if it matches me |
| // (also remove CATALOG attr if I am SCHEMA) |
| for (CollIndex i = 0; i < cqdList_.entries(); i++) |
| if ((cqdList_[i]->getAttrEnum() == def->getAttrEnum()) || |
| (removeCat && cqdList_[i]->getAttrEnum() == CATALOG)) |
| { |
| ControlQueryDefault *cqd=cqdList_.at(i); |
| delete cqd; |
| cqdList_.removeAt(i); |
| if (!removeCat) |
| break; |
| i--; // because we just removed this pos, and now continue the loop |
| } |
| |
| // return if this default is to be reset. We have already removed |
| // it from CQD list. |
| if (def->reset()) { |
| switch (def->getAttrEnum()) { |
| case ROBUST_QUERY_OPTIMIZATION: |
| resetRobustQueryOptimizationCQDs(); |
| break; |
| case MVQR_REWRITE_LEVEL: |
| resetMVQRCQDs(); |
| break; |
| case CACHE_HISTOGRAMS_TRACE_OUTPUT_FILE: |
| { |
| const char* fname = ActiveSchemaDB()->getDefaults(). |
| getValue(CACHE_HISTOGRAMS_TRACE_OUTPUT_FILE); |
| fname = (fname && stricmp(fname,"") != 0) ? fname : NULL; |
| CURRCONTEXT_HISTCACHE->traceTablesFinalize(); |
| // close any old file descriptor |
| CURRCONTEXT_HISTCACHE->closeTraceFile(); |
| // open new file descriptor |
| if (fname) |
| { |
| CURRCONTEXT_HISTCACHE->openTraceFile(fname); |
| FILE *fd = CURRCONTEXT_HISTCACHE->getTraceFileDesc(); |
| if (fd) |
| fprintf(fd, "query:CQD CACHE_HISTOGRAMS_TRACE_OUTPUT_FILE \'%s\';\n",fname); |
| } |
| break; |
| } |
| case CACHE_HISTOGRAMS_MONITOR_OUTPUT_FILE: |
| { |
| const char* fname = ActiveSchemaDB()->getDefaults(). |
| getValue(CACHE_HISTOGRAMS_MONITOR_OUTPUT_FILE); |
| fname = (fname && stricmp(fname,"") != 0) ? fname : NULL; |
| CURRCONTEXT_HISTCACHE->monitor(); |
| // close any old file descriptor |
| CURRCONTEXT_HISTCACHE->closeMonitorFile(); |
| // open new file descriptor |
| if (fname) |
| { |
| CURRCONTEXT_HISTCACHE->openMonitorFile(fname); |
| FILE *fd = CURRCONTEXT_HISTCACHE->getMonitorFileDesc(); |
| if (fd) |
| fprintf(fd, "query:CQD CACHE_HISTOGRAMS_MONITOR_OUTPUT_FILE \'%s\';\n",fname); |
| } |
| break; |
| } |
| case CACHE_HISTOGRAMS: |
| case CACHE_HISTOGRAMS_IN_KB: |
| setHistogramCacheState(); |
| break; |
| } |
| |
| // for embedded compilers, keep a copy of the reset CQD so it can be passed to a cmp Context |
| // we switch to. This is needed in case the CQD was changed earlier and the changed value |
| // was passed on to the same cmp Context in a previous switch. |
| if ((IdentifyMyself::GetMyName() == I_AM_EMBEDDED_SQL_COMPILER) && |
| (def->getValue() != "")) // internal reset not through the CLI |
| { |
| ControlQueryDefault * defCopy = (ControlQueryDefault *) |
| def->copyTree(CONTROLDBHEAP); |
| cqdList_.insert(defCopy); |
| } |
| |
| return; |
| } |
| |
| if (def->getAttrEnum() == NSK_DBG_LOG_FILE) |
| { |
| DefaultConstants attrEnum = def->getAttrEnum(); |
| const char * optDbgLog = |
| ActiveSchemaDB()->getDefaults().getValue(attrEnum); |
| |
| char* logFileName = NULL; |
| Lng32 pid = getpid(); |
| |
| Lng32 len = strlen(optDbgLog); |
| logFileName = new(CmpCommon::statementHeap()) char[len+128]; |
| memset(logFileName, 0, len+128); |
| |
| strcpy(logFileName, optDbgLog); |
| strcat(logFileName, "."); |
| strcat(logFileName, __progname); |
| strcat(logFileName, "."); |
| |
| str_itoa(pid, logFileName+strlen(logFileName)); |
| |
| CURRCONTEXT_OPTDEBUG->openLog(logFileName); |
| |
| } |
| |
| if (def->getAttrEnum() == NSK_DBG_COMPILE_INSTANCE) |
| { |
| const char * compileInstance = |
| ActiveSchemaDB()->getDefaults().getValue(NSK_DBG_COMPILE_INSTANCE); |
| |
| CURRCONTEXT_OPTDEBUG->setCompileInstanceClass(compileInstance); |
| } |
| |
| if (def->getAttrEnum() == MEMORY_MONITOR || |
| def->getAttrEnum() == MEMORY_MONITOR_IN_DETAIL || |
| def->getAttrEnum() == MEMORY_MONITOR_LOGFILE || |
| def->getAttrEnum() == MEMORY_MONITOR_LOG_INSTANTLY) |
| { |
| DefaultConstants attrEnum = def->getAttrEnum(); |
| const char *mmCqd = ActiveSchemaDB()->getDefaults().getValue(attrEnum); |
| if (def->getAttrEnum() == MEMORY_MONITOR) |
| cmpMemMonitor->setIsMemMonitor((mmCqd && stricmp(mmCqd,"ON") == 0) ? TRUE : FALSE); |
| if (def->getAttrEnum() == MEMORY_MONITOR_IN_DETAIL) |
| cmpMemMonitor->setIsMemMonitorInDetail((mmCqd && stricmp(mmCqd,"ON") == 0) ? TRUE : FALSE); |
| if (def->getAttrEnum() == MEMORY_MONITOR_LOGFILE) { |
| |
| if (mmCqd && stricmp(mmCqd,"NONE") != 0) { |
| |
| pid_t pid = getpid(); |
| |
| NAString x(mmCqd); |
| x.append("."); |
| |
| x.append(__progname); |
| x.append("."); |
| |
| char buf[30]; |
| str_itoa((ULng32)pid, buf); |
| |
| x.append(buf); |
| |
| cmpMemMonitor->setLogFilename(x.data()); |
| } else |
| cmpMemMonitor->setLogFilename(NULL); |
| } |
| if (def->getAttrEnum() == MEMORY_MONITOR_LOG_INSTANTLY) |
| cmpMemMonitor->setIsLogInstantly((mmCqd && stricmp(mmCqd,"ON") == 0) ? TRUE : FALSE); |
| } |
| |
| ControlQueryDefault * defCopy = (ControlQueryDefault *) |
| def->copyTree(CONTROLDBHEAP); |
| |
| // Now append. Thus the list is strictly in sequential order, which is good |
| // (because we might have |
| // SET SCHEMA 'A.B'; |
| // SET CATALOG 'C'; -- i.e. default schema now C.B |
| // and need to display both in CmpDescribeControl). |
| cqdList_.insert(defCopy); |
| Lng32 metadata_cache_size =0; |
| |
| NAString val(STMTHEAP); |
| switch (def->getAttrEnum()) { |
| // Setting this default to 'ON' allows users to specify their own syskey. |
| // Internally, the syskey is treated as a user key. Invalidate the |
| // NATable cache (a.k.a. metadata cache) for this default. |
| case OVERRIDE_SYSKEY: |
| // Basically flushing the NaTable cache. |
| ActiveSchemaDB()->getNATableDB()->setCachingOFF(); |
| // Turning it back on, so that the successive queries' |
| // NaTables are cached. |
| ActiveSchemaDB()->getNATableDB()->setCachingON(); |
| break; |
| case TRAF_TABLE_SNAPSHOT_SCAN: |
| if (CmpCommon::getDefault(TRAF_TABLE_SNAPSHOT_SCAN) == DF_LATEST) |
| { |
| //when the user sets TRAF_TABLE_SNAPSHOT_SCAN to LATEST |
| //we flush the metadata and then we set the caching back to on so that metadata |
| //get cached again. If newer snapshots are created after setting the cqd they |
| //won't be seen if they are already cached unless the user issue a command/cqd |
| //to invalidate or flush the cache. One way for doing that can be to issue |
| //"cqd TRAF_TABLE_SNAPSHOT_SCAN 'latest';" again |
| ActiveSchemaDB()->getNATableDB()->setCachingOFF(); |
| ActiveSchemaDB()->getNATableDB()->setCachingON(); |
| } |
| break; |
| //need to flush histogram cache, if we change HIST_MC_STATS_NEEDED |
| case HIST_MC_STATS_NEEDED: |
| if(CURRCONTEXT_HISTCACHE) |
| CURRCONTEXT_HISTCACHE->invalidateCache(); |
| break; |
| case CACHE_HISTOGRAMS_TRACE_OUTPUT_FILE: |
| { |
| const char* fname = ActiveSchemaDB()->getDefaults(). |
| getValue(CACHE_HISTOGRAMS_TRACE_OUTPUT_FILE); |
| fname = (fname && stricmp(fname,"") != 0) ? fname : NULL; |
| CURRCONTEXT_HISTCACHE->traceTablesFinalize(); |
| // close any old file descriptor |
| CURRCONTEXT_HISTCACHE->closeTraceFile(); |
| // open new file descriptor |
| if (fname) |
| { |
| CURRCONTEXT_HISTCACHE->openTraceFile(fname); |
| FILE *fd = CURRCONTEXT_HISTCACHE->getTraceFileDesc(); |
| if (fd) |
| fprintf(fd, "query:CQD CACHE_HISTOGRAMS_TRACE_OUTPUT_FILE \'%s\';\n",fname); |
| } |
| break; |
| } |
| case CACHE_HISTOGRAMS_MONITOR_OUTPUT_FILE: |
| { |
| const char* fname = ActiveSchemaDB()->getDefaults(). |
| getValue(CACHE_HISTOGRAMS_MONITOR_OUTPUT_FILE); |
| fname = (fname && stricmp(fname,"") != 0) ? fname : NULL; |
| CURRCONTEXT_HISTCACHE->monitor(); |
| // close any old file descriptor |
| CURRCONTEXT_HISTCACHE->closeMonitorFile(); |
| // open new file descriptor |
| if (fname) |
| { |
| CURRCONTEXT_HISTCACHE->openMonitorFile(fname); |
| FILE *fd = CURRCONTEXT_HISTCACHE->getMonitorFileDesc(); |
| if (fd) |
| fprintf(fd, "query:CQD CACHE_HISTOGRAMS_MONITOR_OUTPUT_FILE \'%s\';\n",fname); |
| } |
| break; |
| } |
| case METADATA_CACHE_SIZE: |
| metadata_cache_size = getDefaultAsLong(METADATA_CACHE_SIZE)*1024*1024; |
| ActiveSchemaDB()->getNATableDB()->resizeCache(metadata_cache_size); |
| break; |
| case PCODE_EXPR_CACHE_SIZE: |
| { |
| // |
| // Increase OR decrease the size of the PCode Expression Cache |
| // for the current Context. |
| // |
| Int32 newsiz = getDefaultAsLong( PCODE_EXPR_CACHE_SIZE ); |
| Int32 currSiz = CURROPTPCODECACHE->getMaxSize() ; |
| |
| // Note: If new size is negative or the same size, just leave cache alone |
| if ( ( newsiz >= 0 ) && ( newsiz != currSiz ) ) |
| { |
| CURROPTPCODECACHE->resizeCache( newsiz ); |
| if ( newsiz < currSiz ) |
| CURROPTPCODECACHE->clearStats(); // Do this after resizeCache(...) |
| } |
| } |
| break; |
| case QUERY_CACHE: |
| { |
| ULng32 newsiz = getDefaultInK(QUERY_CACHE); |
| const NAArray<CmpContextInfo *> & cmpCtxInfo = |
| GetCliGlobals()->currContext()->getCmpContextInfo(); |
| for (short i = 0; i < cmpCtxInfo.entries(); i++) |
| { |
| QueryCache * qc = cmpCtxInfo[i]->getCmpContext()->getQueryCache(); |
| if (newsiz <= 0) qc->clearStats(); |
| qc->resizeCache(newsiz); |
| } |
| } |
| break; |
| |
| case QUERY_CACHE_STATISTICS: // is now a no-op |
| break; |
| case QUERY_CACHE_AVERAGE_PLAN_SIZE: |
| CURRENTQCACHE->setAvgPlanSz |
| (getDefaultInK(QUERY_CACHE_AVERAGE_PLAN_SIZE)); |
| break; |
| case QUERY_CACHE_MAX_VICTIMS: |
| CURRENTQCACHE->setMaxVictims(getDefaultAsLong(QUERY_CACHE_MAX_VICTIMS)); |
| break; |
| case HQC_LOG_FILE: |
| CURRENTQCACHE->invalidateHQCLogging(); |
| break; |
| case HQC_MAX_VALUES_PER_KEY: |
| CURRENTQCACHE->setHQCMaxValuesPerKey(getDefaultAsLong(HQC_MAX_VALUES_PER_KEY)); |
| break; |
| case QUERY_CACHE_REQUIRED_PREFIX_KEYS: |
| case QUERY_CACHE_STATISTICS_FILE: |
| // the above CQD changes do not affect compiler plan quality |
| break; |
| case ROBUST_QUERY_OPTIMIZATION: |
| setRobustQueryOptimizationCQDs(); |
| break; |
| case MVQR_REWRITE_LEVEL: |
| setMVQRCQDs(); |
| break; |
| case CACHE_HISTOGRAMS: |
| case CACHE_HISTOGRAMS_IN_KB: |
| setHistogramCacheState(); |
| break; |
| case DEFAULT_SCHEMA_ACCESS_ONLY: |
| if ((CmpCommon::getDefault(DEFAULT_SCHEMA_ACCESS_ONLY)==DF_ON) && |
| (CmpCommon::getDefault(DEFAULT_SCHEMA_NAMETYPE)==DF_USER)) |
| { |
| // update SCHEMA in CQD list |
| NAString schName = ""; |
| CmpCommon::getDefault(SCHEMA, schName, 0); |
| NAString cqdSchema("CONTROL QUERY DEFAULT SCHEMA ", STMTHEAP); |
| cqdSchema += "'"; |
| cqdSchema += schName; |
| cqdSchema += "';"; |
| ControlQueryDefault *cqdS = |
| new(STMTHEAP) ControlQueryDefault(cqdSchema, |
| CharInfo::UTF8 |
| , "SCHEMA", schName, FALSE, 0, STMTHEAP, 0); |
| cqdS->setAttrEnum(SCHEMA); |
| setControlDefault(cqdS); |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| |
| void ControlDB::do_one_CQD(const char *attrName, |
| const char *attrValue, |
| NABoolean reset, |
| NADefaults::Provenance masterOrigin) |
| { |
| // make sure cqd name is good |
| NADefaults &defs = ActiveSchemaDB()->getDefaults(); |
| DefaultConstants cqdEnum = defs.lookupAttrName(attrName); |
| if (cqdEnum < 0) |
| return; // cqd name is bad |
| |
| // was it explicitly set by some user action? |
| NADefaults::Provenance |
| cqdOrigin = defs.getProvenance(cqdEnum); |
| if (cqdOrigin >= NADefaults::READ_FROM_SQL_TABLE && |
| cqdOrigin >= masterOrigin) { // yes, it was. |
| return; // so, leave it alone. |
| } |
| // no, it is not user-specified. |
| // (re)set it accordingly |
| // put together cqd statement |
| NAString text("CONTROL QUERY DEFAULT ", 200, STMTHEAP), |
| name(attrName, STMTHEAP), value(attrValue, STMTHEAP); |
| text += attrName; |
| if (reset) { |
| text += " RESET;"; |
| } else { |
| text += " '"; text += attrValue; text += "';"; |
| } |
| ControlQueryDefault *cqdP = new(STMTHEAP) ControlQueryDefault |
| (text, |
| CharInfo::UTF8, |
| name, value, FALSE, 0, STMTHEAP, reset); |
| // "execute" cqd statement |
| NADefaults::Provenance prevState = defs.getState(); |
| defs.setState(NADefaults::DERIVED); |
| cqdP->setAttrEnum(defs.validateAndInsert(attrName, value, reset)); |
| setControlDefault(cqdP); |
| defs.setState(prevState); |
| } |
| |
| void ControlDB::do_one_RQO_CQD(const char *attrName, |
| const char *attrValue, |
| NABoolean reset) |
| { |
| do_one_CQD(attrName, attrValue, reset, |
| ActiveSchemaDB()->getDefaults().getProvenance |
| (ROBUST_QUERY_OPTIMIZATION)); |
| } |
| |
| void ControlDB::doRobustQueryOptimizationCQDs(RQOsetting set) |
| { |
| // ROBUST_QUERY_OPTIMIZATION is an external master CQD that sets its |
| // internal CQDs to max, high, min, system, or reset |
| switch (set) { |
| case RQO_MAX: |
| do_one_RQO_CQD("RISK_PREMIUM_NJ" , "5.0", FALSE); |
| do_one_RQO_CQD("RISK_PREMIUM_SERIAL" , "2.0", FALSE); |
| do_one_RQO_CQD("PARTITIONING_SCHEME_SHARING" , "2" , FALSE); |
| do_one_RQO_CQD("ROBUST_HJ_TO_NJ_FUDGE_FACTOR", "1.0", FALSE); |
| do_one_RQO_CQD("ROBUST_SORTGROUPBY" , "2" , FALSE); |
| do_one_RQO_CQD("RISK_PREMIUM_MJ" , "2.0", FALSE); |
| break; |
| case RQO_HIGH: |
| do_one_RQO_CQD("RISK_PREMIUM_NJ" , "2.5", FALSE); |
| do_one_RQO_CQD("RISK_PREMIUM_SERIAL" , "1.5", FALSE); |
| do_one_RQO_CQD("PARTITIONING_SCHEME_SHARING" , "1" , FALSE); |
| do_one_RQO_CQD("ROBUST_HJ_TO_NJ_FUDGE_FACTOR", "3.0", FALSE); |
| do_one_RQO_CQD("ROBUST_SORTGROUPBY" , "2" , FALSE); |
| do_one_RQO_CQD("RISK_PREMIUM_MJ" , "1.5", FALSE); |
| break; |
| case RQO_MIN: |
| do_one_RQO_CQD("RISK_PREMIUM_NJ" , "1.0", FALSE); |
| do_one_RQO_CQD("RISK_PREMIUM_SERIAL" , "1.0", FALSE); |
| do_one_RQO_CQD("PARTITIONING_SCHEME_SHARING" , "0" , FALSE); |
| do_one_RQO_CQD("ROBUST_HJ_TO_NJ_FUDGE_FACTOR", "0.0", FALSE); |
| do_one_RQO_CQD("ROBUST_SORTGROUPBY" , "0" , FALSE); |
| do_one_RQO_CQD("RISK_PREMIUM_MJ" , "1.0", FALSE); |
| break; |
| case RQO_SYS: |
| do_one_RQO_CQD("RISK_PREMIUM_NJ" , "SYSTEM", FALSE); |
| do_one_RQO_CQD("RISK_PREMIUM_SERIAL" , "SYSTEM", FALSE); |
| do_one_RQO_CQD("PARTITIONING_SCHEME_SHARING" , "SYSTEM", FALSE); |
| do_one_RQO_CQD("ROBUST_HJ_TO_NJ_FUDGE_FACTOR", "SYSTEM", FALSE); |
| do_one_RQO_CQD("ROBUST_SORTGROUPBY" , "SYSTEM", FALSE); |
| do_one_RQO_CQD("RISK_PREMIUM_MJ" , "SYSTEM", FALSE); |
| break; |
| case RQO_RESET: |
| do_one_RQO_CQD("RISK_PREMIUM_NJ" , "", TRUE); |
| do_one_RQO_CQD("RISK_PREMIUM_SERIAL" , "", TRUE); |
| do_one_RQO_CQD("PARTITIONING_SCHEME_SHARING" , "", TRUE); |
| do_one_RQO_CQD("ROBUST_HJ_TO_NJ_FUDGE_FACTOR", "", TRUE); |
| do_one_RQO_CQD("ROBUST_SORTGROUPBY" , "", TRUE); |
| do_one_RQO_CQD("RISK_PREMIUM_MJ" , "", TRUE); |
| break; |
| } |
| } |
| |
| void ControlDB::resetRobustQueryOptimizationCQDs() |
| { |
| // ROBUST_QUERY_OPTIMIZATION reset is an |
| // external master CQD that resets its internal CQDs |
| doRobustQueryOptimizationCQDs(RQO_RESET); |
| } |
| |
| void ControlDB::setRobustQueryOptimizationCQDs() |
| { |
| // ROBUST_QUERY_OPTIMIZATION is an |
| // external master CQD that sets following internal CQDs |
| // robust_query_optimization |
| // MINIMUM SYSTEM MAXIMUM |
| // risk_premium_NJ 1.0 system 5.0 |
| // risk_premium_SERIAL 1.0 system 2.0 |
| // partitioning_scheme_sharing 0 system 2 |
| // robust_hj_to_nj_fudge_factor 0.0 system 1.0 |
| // robust_sortgroupby 0 system 2 |
| // risk_premium_MJ 1.0 system 2.0 |
| // NB: We want the robust_query_optimization master CQD to behave like so: |
| // 1) controlled internal CQDs appear to have been set thru normal means. |
| // 2) user can set, reset these internal CQDs as they choose. |
| // 3) user-specified setttings of internal CQDs are honored (ie, left |
| // alone) by master CQD. |
| DefaultToken rqo = CmpCommon::getDefault(ROBUST_QUERY_OPTIMIZATION); |
| if (rqo == DF_MINIMUM) |
| doRobustQueryOptimizationCQDs(RQO_MIN); |
| else if (rqo == DF_MAXIMUM) |
| doRobustQueryOptimizationCQDs(RQO_MAX); |
| else if (rqo == DF_SYSTEM) |
| doRobustQueryOptimizationCQDs(RQO_SYS); |
| else if (rqo == DF_HIGH) |
| doRobustQueryOptimizationCQDs(RQO_HIGH); |
| } |
| |
| void ControlDB::do_one_MVQR_CQD(const char *attrName, |
| const char *attrValue, |
| NABoolean reset) |
| { |
| do_one_CQD(attrName, attrValue, reset, |
| ActiveSchemaDB()->getDefaults().getProvenance |
| (MVQR_REWRITE_LEVEL)); |
| } |
| |
| void ControlDB::doMVQRCQDs(Lng32 level) |
| { |
| // MVQR_REWRITE_LEVEL is a master CQD that sets its subordinate CQDs |
| switch (level) { |
| case 0: // off |
| do_one_MVQR_CQD("MULTI_JOIN_THRESHOLD" , "3" , FALSE); |
| break; |
| case 1: case 2: case 3: case 4: // on |
| do_one_MVQR_CQD("MULTI_JOIN_THRESHOLD" , "2" , FALSE); |
| break; |
| case -1: // reset |
| do_one_MVQR_CQD("MULTI_JOIN_THRESHOLD" , "", TRUE); |
| break; |
| } |
| } |
| |
| void ControlDB::resetMVQRCQDs() |
| { |
| doMVQRCQDs(-1); // reset |
| } |
| |
| void ControlDB::setMVQRCQDs() |
| { |
| // MVQR_REWRITE_LEVEL is a |
| // master CQD that sets following subordinate CQDs |
| // MVQR_REWRITE_LEVEL |
| // 0 SYSTEM 1 |
| // mvqr_rewrite_level 0 system 1 |
| // multi_join_threshold 3 system 2 |
| Lng32 level = CmpCommon::getDefaultLong(MVQR_REWRITE_LEVEL); |
| doMVQRCQDs(level); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////// |
| // |
| // Implementation of CONTROL TABLE statement. |
| // |
| // NOTE: The tableName passed in had better be the external name, |
| // fully qualified, via |
| // getExtFileName(), ansiName(), getXxxAsAnsiString() |
| // or else mismatches/false-nonmatches may result in the case |
| // of DELIMITED identifiers in the table name. |
| // |
| //////////////////////////////////////////////////////////////////////////// |
| static const ControlTableOptions::CTTokens controlTableTokens[] = { |
| {"IF_LOCKED", ControlTableOptions::IF_LOCKED}, |
| {"MDAM", ControlTableOptions::MDAM}, |
| {"NOWAIT", ControlTableOptions::NOWAIT}, |
| {"PRIORITY", ControlTableOptions::PRIORITY}, |
| {"PRIORITY_DELTA", ControlTableOptions::PRIORITY_DELTA}, |
| {"SIMILARITY_CHECK", ControlTableOptions::SIMILARITY_CHECK}, |
| {"TABLELOCK", ControlTableOptions::TABLELOCK}, |
| {"TIMEOUT", ControlTableOptions::TIMEOUT} |
| }; |
| |
| static const size_t numControlTableTokens = |
| sizeof(controlTableTokens) / sizeof(ControlTableOptions::CTTokens); |
| |
| ControlTableOptions * ControlDB::getControlTableOption( |
| const NAString &tableName, |
| CollIndex &i) |
| { |
| for (i = 0; i < getCTList().entries(); i++) |
| { |
| if (getCTList()[i]->tableName() == tableName) |
| { |
| return getCTList()[i]; |
| } |
| } |
| |
| return NULL; // not found |
| } |
| |
| void ControlDB::resetTableValue(const NAString &tableName, const NAString &token) |
| { |
| ControlTableOptions * cto = NULL; |
| if (tableName == "*") |
| { |
| if (token == "") |
| { |
| // CONTROL TABLE * RESET; -- reset everything. |
| for (CollIndex i = 0; i < getCTList().entries(); i++) |
| { |
| cto = getCTList().at(i); |
| delete cto; |
| } |
| getCTList().clear(); |
| } |
| else |
| { |
| // CONTROL TABLE * tok RESET; |
| // CONTROL TABLE * tok 'val'; |
| for (CollIndex i = 0; i < getCTList().entries(); i++) |
| { |
| cto = getCTList()[i]; |
| cto->remove(token); |
| } |
| } |
| } |
| else |
| { |
| CollIndex index; |
| cto = getControlTableOption(tableName, index); |
| if (cto) |
| { |
| if (token == "") |
| { |
| // CONTROL TABLE t RESET; -- remove entry for this tableName. |
| REMOVEAT(getCTList(), index); |
| } |
| else |
| { |
| // CONTROL TABLE t tok RESET; |
| cto->remove(token); |
| } |
| } |
| } |
| } |
| |
| CollIndex ControlDB::isValidToken(const NAString &token) |
| { |
| for (CollIndex index = 0; index < numControlTableTokens; index++) |
| { |
| if (token == controlTableTokens[index].token_) |
| return index; |
| } |
| |
| return NULL_COLL_INDEX; |
| } |
| |
| // Returns TRUE, if a CT value specified is valid for that option. |
| NABoolean ControlDB::validate(ControlTable *ct) |
| { |
| // Put value in canonical form, first of all. |
| NAString &value = (NAString &)ct->getValue(); // cast away constness |
| TrimNAStringSpace(value); |
| value.toUpper(); |
| |
| if (value == "" || value == "RESET") |
| if (!ct->reset()) ct->reset() = 1; // 0->1 but not 2->1 ! |
| |
| const NAString &token = ct->getToken(); |
| if (token == "") return TRUE; // wildcard valid |
| |
| CollIndex index = isValidToken(token); |
| NABoolean valid = (index != NULL_COLL_INDEX); |
| |
| if (valid) { |
| |
| if (ct->reset()) return TRUE; |
| |
| switch (controlTableTokens[index].const_) |
| { |
| case ControlTableOptions::IF_LOCKED: |
| { |
| if ((value == "RETURN") || (value == "WAIT")) |
| valid = TRUE; |
| } |
| break; |
| |
| case ControlTableOptions::MDAM: |
| { |
| if ((value == "OFF") || (value == "ON") || (value == "ENABLE")) |
| valid = TRUE; |
| } |
| break; |
| |
| case ControlTableOptions::NOWAIT: |
| { |
| if ((value == "ON") || (value == "OFF")) |
| valid = TRUE; |
| } |
| break; |
| |
| case ControlTableOptions::PRIORITY: |
| { |
| valid = TRUE; |
| for (size_t i = 0; (i < value.length() && valid); i++) |
| { |
| if (value.data()[i] < '0' || |
| value.data()[i] > '9') |
| valid = FALSE; |
| } |
| if (valid) |
| { |
| Int32 priority = atoi(value.data()); |
| |
| // priority must be between 1 and 199. |
| if (priority < 1 || priority > 199) |
| valid = FALSE; |
| |
| } |
| } |
| break; |
| |
| case ControlTableOptions::PRIORITY_DELTA: |
| { |
| valid = TRUE; |
| NABoolean negative = FALSE; |
| if (value.data()[0] == '-') |
| { |
| negative = TRUE; |
| } |
| for (size_t i = 0 + (negative ? 1 : 0); |
| (i < value.length() && valid); i++) |
| { |
| if (value.data()[i] < '0' || |
| value.data()[i] > '9') |
| valid = FALSE; |
| } |
| if (valid) |
| { |
| Int32 priority = atoi(&value.data()[(negative ? 1 : 0)]); |
| if (negative) |
| priority = - priority; |
| // priority must be between 1 and 199. |
| if (priority < -199 || priority > 199) |
| valid = FALSE; |
| } |
| } |
| break; |
| |
| case ControlTableOptions::SIMILARITY_CHECK: |
| { |
| if ((value == "ON") || (value == "OFF")) |
| valid = TRUE; |
| } |
| break; |
| |
| case ControlTableOptions::TABLELOCK: |
| { |
| if ((value == "ON") || (value == "OFF") || (value == "ENABLE")) |
| valid = TRUE; |
| } |
| break; |
| |
| case ControlTableOptions::TIMEOUT: |
| { |
| valid = TRUE; |
| if ( value == "-1" ) break; // 11-8-2000 |
| for (size_t i = 0; (i < value.length() && valid); i++) |
| { |
| if (value.data()[i] < '0' || |
| value.data()[i] > '9') |
| valid = FALSE; |
| } |
| } |
| break; |
| |
| default: |
| CMPASSERT(FALSE); |
| break; |
| |
| } // switch |
| |
| } // isValidToken |
| |
| if (! valid) |
| { |
| *CmpCommon::diags() << DgSqlCode(-2051) |
| << DgString0("CONTROL TABLE") |
| << DgString1(token) |
| << DgString2(value); |
| } |
| |
| return valid; |
| } |
| |
| NABoolean ControlDB::setControlTableValue(ControlTable *ct) |
| { |
| if (! validate(ct)) return FALSE; |
| |
| // Do this AFTER validate() has altered the value string |
| const NAString &value = ct->getValue(); |
| const NAString &token = ct->getToken(); |
| |
| // CONTROL TABLE * tok 'v' -- name here will be "*" |
| // CONTROL TABLE "*" t 'v' -- name here will be "\"*\"" (it's an Ansi name!) |
| NAString tableName(CmpCommon::statementHeap()); |
| |
| if (ct->getTableName().isFabricated()) |
| tableName = "*"; |
| else |
| //ct-bug-10-030102-3803 -Begin |
| tableName = ct->getTableName().getUgivenName(); |
| //ct-bug-10-030102-3803 -End |
| |
| if (ct->reset()) |
| { |
| // CONTROL TABLE * RESET; (equiv:) CONTROL TABLE * * RESET; |
| // CONTROL TABLE t RESET; (equiv:) CONTROL TABLE t * RESET; |
| // CONTROL TABLE * tok RESET; |
| // CONTROL TABLE t tok RESET; |
| // and all the above forms twice more, |
| // replacing RESET by value string of '' and 'reset'. |
| resetTableValue(tableName, token); |
| return TRUE; |
| } |
| |
| if (token == "") |
| { |
| // CONTROL TABLE t * 'val'; -- is allowed by Parser for all them 'reset' |
| // things preceding, but here we flag it as illegal. |
| ct->mutateToken() = "*"; |
| NABoolean starValid = validate(ct); |
| CMPASSERT(!starValid); // it better not be valid |
| return FALSE; |
| } |
| |
| if (tableName == "*") |
| { |
| // CONTROL TABLE * tok 'val'; -- erase all prior <C.T. t tok 'val';> |
| resetTableValue(tableName, token); |
| } |
| |
| CollIndex index; |
| ControlTableOptions * cto = getControlTableOption(tableName, index); |
| if (NOT cto) |
| { |
| // this table control option doesn't exist. Create an entry for it. |
| cto = new CONTROLDBHEAP ControlTableOptions(tableName); |
| |
| getCTList().insert(cto); |
| } |
| |
| cto->addTokenAndValue(token, value); |
| |
| return TRUE; |
| } |
| |
| const NAString * ControlDB::getControlTableValue(const NAString &tableName, |
| const NAString &token) |
| { |
| const NAString * controlValue = NULL; |
| if (isValidToken(token) == NULL_COLL_INDEX) |
| { |
| CMPASSERT(FALSE); |
| return NULL; |
| } |
| |
| // Return the 'value' for 'token'. First search using tableName. |
| CollIndex index; |
| ControlTableOptions * cto = getControlTableOption(tableName, index); |
| if (! cto) |
| { |
| // Didn't find in the tableName entry. Look up in the "*" entry. |
| if (tableName != "*") |
| { |
| NAString star("*",STMTHEAP); |
| controlValue = getControlTableValue(star, token); |
| } |
| } |
| else |
| controlValue = cto->getValue(token); |
| |
| if (controlValue == NULL) |
| { |
| |
| //ct-bug-10-030102-3803 -Begin |
| // Check if we have a C.T * set on this token. |
| if (tableName != "*") |
| { |
| NAString star("*",STMTHEAP); |
| controlValue = getControlTableValue(star, token); |
| } |
| |
| if(controlValue == NULL) // Still no match, Set System defaults. |
| { |
| //ct-bug-10-030102-3803 -End |
| // see if this value exists in the CQD table. |
| NAString defVal(CmpCommon::statementHeap()); |
| DefaultConstants dc = NADefaults::lookupAttrName(token, FALSE); |
| if (dc >= 0) // valid |
| { |
| CmpCommon::getDefault(dc, defVal, FALSE /*do not return err/warn*/); |
| if (! defVal.isNull()) |
| controlValue = new STMTHEAP NAString(defVal, STMTHEAP); |
| } |
| //ct-bug-10-030102-3803 -Begin |
| } |
| //ct-bug-10-030102-3803 -End |
| } |
| |
| return controlValue; |
| } |
| |
| ControlTableOptions::ControlTableOptions() |
| : tableName_(CONTROLDBHEAP) |
| { |
| tokens_ = NULL; |
| values_ = NULL; |
| } |
| |
| ControlTableOptions::ControlTableOptions(const NAString &tableName) |
| : tableName_(tableName, CONTROLDBHEAP) |
| { |
| tokens_ = new CONTROLDBHEAP LIST(NAString*)(CONTROLDBHEAP); |
| values_ = new CONTROLDBHEAP LIST(NAString*)(CONTROLDBHEAP); |
| } |
| |
| ControlTableOptions::~ControlTableOptions() |
| { |
| CollIndex i=0; |
| NAString *ns=NULL; |
| |
| for (i = 0; i < tokens_->entries(); i++) |
| { |
| ns = tokens_->at(i); |
| delete ns; |
| } |
| delete tokens_; |
| |
| for (i = 0; i < values_->entries(); i++) |
| { |
| ns = values_->at(i); |
| delete ns; |
| } |
| delete values_; |
| } |
| |
| void ControlTableOptions::remove(const NAString &token) |
| { |
| CMPASSERT(tokens_->entries() == values_->entries()); |
| |
| CollIndex i; |
| CollIndex index = NULL_COLL_INDEX; |
| |
| for (i = 0; i < tokens_->entries(); i++) |
| { |
| NAString *ns = tokens_->at(i); |
| |
| // compareTo method returns zero if two strings are equal |
| if (!ns->compareTo(token) ) |
| { |
| index = i; |
| break; |
| } |
| } |
| |
| if (index != NULL_COLL_INDEX) |
| { |
| NAString *ns=tokens_->at(index); |
| delete ns; |
| tokens_->removeAt(index); |
| ns=values_->at(index); |
| delete ns; |
| values_->removeAt(index); |
| } |
| } |
| |
| const NAString * ControlTableOptions::getValue(const NAString &token) |
| { |
| CMPASSERT(tokens_->entries() == values_->entries()); |
| |
| CollIndex i; |
| CollIndex index = NULL_COLL_INDEX; |
| |
| for (i = 0; i < tokens_->entries(); i++) |
| { |
| NAString *ns = tokens_->at(i); |
| |
| // CompareTo returns zero if two strings are equal |
| if (!ns->compareTo(token) ) |
| { |
| index = i; |
| break; |
| } |
| } |
| |
| if (index != NULL_COLL_INDEX) |
| return (*values_)[index]; |
| else |
| return NULL; |
| } |
| |
| void ControlTableOptions::addTokenAndValue(const NAString &token, |
| const NAString &value) |
| { |
| remove(token); |
| NAString *ctToken = new CONTROLDBHEAP NAString(token, CONTROLDBHEAP); |
| tokens_->insert(ctToken); |
| NAString *ctValue = new CONTROLDBHEAP NAString(value, CONTROLDBHEAP); |
| values_->insert(ctValue); |
| } |
| |
| const NAString &ControlTableOptions::getToken(CollIndex index) |
| { |
| CMPASSERT((index >= 0) && (index < tokens_->entries())); |
| |
| return *((*tokens_)[index]); |
| } |
| |
| const NAString &ControlTableOptions::getValue(CollIndex index) |
| { |
| CMPASSERT((index >= 0) && (index < values_->entries())); |
| |
| return *((*values_)[index]); |
| } |
| |
| |
| |
| |
| //////////////////////////////////////////////////////////////////////////// |
| // |
| // Implementation of CONTROL SESSION statement. |
| // |
| //////////////////////////////////////////////////////////////////////////// |
| |
| // PRIVATE methods |
| |
| void ControlDB::resetSessionValue(const NAString &token) |
| { |
| ControlSessionOption * cso = NULL; |
| if (token == "") |
| { |
| // CONTROL SESSION * RESET; -- reset everything. |
| for (CollIndex i = 0; i < csList_.entries(); i++) |
| { |
| cso = csList_.at(i); |
| delete cso; |
| } |
| csList_.clear(); |
| } |
| else |
| { |
| CollIndex index; |
| cso = getControlSessionOption(token, index); |
| if (cso) |
| REMOVEAT(csList_, index); |
| } |
| } |
| |
| ControlSessionOption * ControlDB::getControlSessionOption |
| (const NAString &token, CollIndex &i) |
| { |
| // return the 'value' for 'token'. |
| for (i = 0; i < csList_.entries(); i++) |
| { |
| ControlSessionOption * cso = csList_[i]; |
| if (cso->getToken() == token) |
| return cso; |
| } |
| |
| return NULL; |
| } |
| |
| |
| // PUBLIC methods |
| |
| NABoolean ControlDB::validate(ControlSession *cs) |
| { |
| // Put things into canonical form, then validate. |
| |
| // Must cast away constness here, for these Trim/toUpper's |
| |
| NAString &token = (NAString &)cs->getToken(); |
| TrimNAStringSpace(token); |
| token.toUpper(); |
| |
| NAString &value = (NAString &)cs->getValue(); |
| TrimNAStringSpace(value); |
| // but NOT: value.toUpper(); // values can be any case |
| |
| NABoolean valid = TRUE; |
| |
| if (value == "") // value=="RESET" *is* a valid Session value |
| { |
| if (!cs->reset()) cs->reset() = 1; // 0->1 but not 2->1 ! |
| } |
| else if (token == "") |
| { |
| valid = FALSE; // CONTROL SESSION '' 'nonblank'; -- error |
| } |
| else |
| { |
| // Token must begin with [A-Z_$=], continue with [A-Z0-9_$=.] only. |
| const char *str = token.data(); |
| if (!isalpha(*str) && !strchr("_$=", *str)) |
| valid = FALSE; |
| while (*++str && valid) |
| { |
| if (!isalnum(*str) && !strchr("_$=.", *str)) |
| valid = FALSE; |
| } |
| } |
| |
| if (! valid) |
| { |
| token.prepend("'"); |
| token.append("'"); |
| *CmpCommon::diags() << DgSqlCode(-2051) |
| << DgString0("CONTROL SESSION") |
| << DgString1(token) |
| << DgString2(value); |
| } |
| |
| return valid; |
| } |
| |
| NABoolean ControlDB::setControlSessionValue(ControlSession *cs) |
| { |
| if (! validate(cs)) return FALSE; |
| |
| const NAString &token = cs->getToken(); |
| const NAString &value = cs->getValue(); |
| |
| if (cs->reset()) |
| { |
| resetSessionValue(token); |
| return TRUE; |
| } |
| |
| CollIndex index; |
| ControlSessionOption * cso = getControlSessionOption(token, index); |
| if (NOT cso) |
| { |
| // this Session control option doesn't exist. Create an entry for it. |
| cso = new CONTROLDBHEAP ControlSessionOption(); |
| |
| csList_.insert(cso); |
| } |
| |
| cso->addTokenAndValue(token, value); |
| |
| return TRUE; |
| } |
| |
| const NAString * ControlDB::getControlSessionValue(const NAString &token) |
| { |
| // return the 'value' for 'token'. |
| CollIndex index; |
| NAString t(token, CmpCommon::statementHeap()); |
| t.toUpper(); |
| |
| ControlSessionOption * cso = getControlSessionOption(t, index); |
| if (cso) |
| return &cso->getValue(); |
| else |
| return NULL; |
| } |
| |
| /////////////////////////////////////////////////////////////////////// |
| // Packed form: |
| // |
| // num of Tablenames (long) |
| // num of CTO for a table (long) |
| // Name of Table (char*, null terminated) |
| // Token (char*, null terminated) |
| // Value (char*, null terminated) |
| // ... |
| // |
| /////////////////////////////////////////////////////////////////////// |
| Lng32 ControlDB::packedLengthControlTableOptions() |
| { |
| Lng32 size = 0; |
| |
| if (getCTList().entries() > 0) |
| { |
| size = sizeof(Lng32); |
| for (CollIndex i = 0; i < getCTList().entries(); i++) |
| { |
| size += sizeof(Lng32); |
| |
| ControlTableOptions *cto = getCTList()[i]; |
| size += strlen(cto->tableName().data()) + 1; |
| |
| for (CollIndex j = 0; j < cto->numEntries(); j++) |
| { |
| size += strlen(cto->getToken(j).data()) + 1 |
| + strlen(cto->getValue(j).data()) + 1; |
| } // j |
| |
| size = ROUND8(size); |
| } // i |
| } |
| |
| return size; |
| } |
| |
| // see ControlDB::packedLengthControlTableOptions() for packed layout |
| Lng32 ControlDB::packControlTableOptionsToBuffer(char * buffer) |
| { |
| Lng32 curPos = 0; |
| |
| ULng32 tempSize = 0; |
| |
| if (getCTList().entries() > 0) |
| { |
| tempSize = getCTList().entries(); |
| str_cpy_all(&buffer[curPos], (char*)&tempSize, sizeof(Lng32)); |
| curPos = sizeof(Lng32); |
| for (CollIndex i = 0; i < getCTList().entries(); i++) |
| { |
| ControlTableOptions *cto = getCTList()[i]; |
| |
| tempSize = cto->numEntries(); |
| str_cpy_all(&buffer[curPos], (char*)&tempSize, sizeof(Lng32)); |
| curPos += sizeof(Lng32); |
| |
| strcpy(&buffer[curPos], cto->tableName().data()); |
| curPos += strlen(cto->tableName().data()) + 1; |
| |
| for (CollIndex j = 0; j < cto->numEntries(); j++) |
| { |
| strcpy(&buffer[curPos], cto->getToken(j).data()); |
| curPos += strlen(cto->getToken(j).data()) + 1; |
| |
| strcpy(&buffer[curPos], cto->getValue(j).data()); |
| curPos += strlen(cto->getValue(j).data()) + 1; |
| |
| } // j |
| |
| curPos = ROUND8(curPos); |
| } // i |
| } |
| |
| return curPos; |
| } |
| |
| // see ControlDB::packedLengthControlTableOptions() for packed layout |
| Lng32 ControlDB::unpackControlTableOptionsFromBuffer(char * buffer) |
| { |
| if (!ctList_) |
| ctList_ = new CONTROLDBHEAP LIST(ControlTableOptions *)(CONTROLDBHEAP); |
| |
| Lng32 numEntries; |
| Lng32 curPos = 0; |
| str_cpy_all((char*)&numEntries, buffer, sizeof(Lng32)); |
| curPos += sizeof(Lng32); |
| for (Int32 i = 0; i < numEntries; i++) |
| { |
| Lng32 numCTO; |
| str_cpy_all((char*)&numCTO, &buffer[curPos], sizeof(Lng32)); |
| curPos += sizeof(Lng32); |
| |
| char * tableName = &buffer[curPos]; |
| ControlTableOptions * cto = |
| new CONTROLDBHEAP ControlTableOptions(tableName); |
| getCTList().insert(cto); |
| curPos += strlen(&buffer[curPos]) + 1; |
| |
| for (Int32 j = 0; j < numCTO; j++) |
| { |
| char * token = &buffer[curPos]; |
| curPos += strlen(&buffer[curPos]) + 1; |
| char * value = &buffer[curPos]; |
| curPos += strlen(&buffer[curPos]) + 1; |
| |
| cto->addTokenAndValue(token, value); |
| } |
| curPos = ROUND8(curPos); |
| } |
| |
| return curPos; |
| } |
| |
| NABoolean ControlDB::isSameCTO(char * buffer, Lng32 bufLen) |
| { |
| return FALSE; |
| } |
| |
| Lng32 ControlDB::saveCurrentCTO() |
| { |
| if (savedCtList_) |
| delete savedCtList_; |
| |
| savedCtList_ = ctList_; |
| |
| ctList_ = new CONTROLDBHEAP LIST(ControlTableOptions *)(CONTROLDBHEAP); |
| |
| return 0; |
| } |
| |
| Lng32 ControlDB::restoreCurrentCTO() |
| { |
| if (ctList_) |
| delete ctList_; |
| |
| ctList_ = savedCtList_; |
| |
| savedCtList_ = NULL; |
| |
| return 0; |
| } |
| |
| Lng32 ControlDB::packedLengthControlQueryShape() |
| { |
| Lng32 size = 0; |
| |
| if (getRequiredShape()) |
| size = (Lng32)getRequiredShape()->getShapeText().length() + 1; |
| |
| return size; |
| } |
| |
| Lng32 ControlDB::packControlQueryShapeToBuffer(char * buffer) |
| { |
| Lng32 size = 0; |
| |
| if (getRequiredShape()) |
| { |
| size = (Lng32)getRequiredShape()->getShapeText().length() + 1; |
| |
| strcpy(buffer, getRequiredShape()->getShapeText().data()); |
| } |
| |
| return size; |
| } |
| |
| Lng32 ControlDB::unpackControlQueryShapeFromBuffer(char * buffer) |
| { |
| |
| return 0; |
| } |
| |
| Lng32 ControlDB::saveCurrentCQS() |
| { |
| if (savedRequiredShape_) |
| delete savedRequiredShape_; |
| |
| savedRequiredShape_ = requiredShape_; |
| |
| requiredShape_ = NULL; |
| |
| return 0; |
| } |
| |
| Lng32 ControlDB::restoreCurrentCQS() |
| { |
| if (requiredShape_) |
| delete requiredShape_; |
| |
| requiredShape_ = savedRequiredShape_; |
| |
| savedRequiredShape_ = NULL; |
| |
| return 0; |
| } |
| |
| NABoolean ControlDB::isSameCQS(char * buffer, Lng32 bufLen) |
| { |
| if ((getRequiredShape()) && |
| (bufLen > 0) && |
| (strcmp(getRequiredShape()->getShapeText().data(), buffer) == 0)) |
| return TRUE; |
| |
| return FALSE; |
| } |
| |
| ControlSessionOption::ControlSessionOption() |
| { |
| token_ = NULL; |
| value_ = NULL; |
| } |
| |
| ControlSessionOption::~ControlSessionOption() |
| { |
| delete token_; |
| delete value_; |
| } |
| |
| void ControlSessionOption::addTokenAndValue(const NAString &token, |
| const NAString &value) |
| { |
| delete token_; |
| delete value_; |
| |
| token_ = new CONTROLDBHEAP NAString(token, CONTROLDBHEAP); |
| value_ = new CONTROLDBHEAP NAString(value, CONTROLDBHEAP); |
| } |
| |
| // =========================================================================== |
| |
| // --------------------------------------------------------------------------- |
| // This code is called by SqlParser.y. |
| // It was moved out of there because that file was getting too big -- |
| // the c89 compiler was choking on it (ugen assertion failure). |
| // --------------------------------------------------------------------------- |
| |
| // argument checks for CONTROL QUERY SHAPE syntax |
| static NABoolean badSingleArg(const NAString &fname, ExprNodePtrList *args, |
| ComDiagsArea *diags) |
| { |
| if (args->entries() != 1 OR |
| args->at(0)->castToRelExpr() == NULL) |
| { |
| *diags << DgSqlCode(-3113) << |
| DgString0(NAString("One relational argument expected for ") + fname); |
| return TRUE; |
| } |
| return FALSE; |
| } |
| |
| static NABoolean badTwoArgs(const NAString &fname, ExprNodePtrList *args, |
| ComDiagsArea *diags) |
| { |
| if (args->entries() != 2 OR |
| args->at(0)->castToRelExpr() == NULL OR |
| args->at(1)->castToRelExpr() == NULL) |
| { |
| *diags << DgSqlCode(-3113) << |
| DgString0(NAString("Two relational arguments expected for ") + fname); |
| return TRUE; |
| } |
| return FALSE; |
| } |
| |
| // decode one relational expression for CONTROL QUERY SHAPE |
| ExprNode *DecodeShapeSyntax(const NAString &fname, |
| ExprNodePtrList *args, |
| ComDiagsArea *diags, |
| CollHeap *heap) |
| { |
| ExprNode * result = NULL; |
| enum { FORCED_EXCHANGE, |
| FORCED_JOIN, |
| FORCED_OTHER } forcedType = FORCED_OTHER; |
| OperatorTypeEnum wildcard; |
| ExchangeForceWildCard::forcedExchEnum whichExch; |
| |
| // **** NOTE: this procedure basically adds functions that could |
| // equally well be handled by parsing rules. The reason for doing |
| // it inside this procedure is to avoid mixing the CONTROL QUERY SHAPE |
| // syntax details with the rest of the SQL parser. Once CONTROL QUERY |
| // shape has become an accepted part of our product this procedure |
| // could be changed. Error messages should then be parameterized. |
| |
| // supports scan, scan(table), scan(table, index), |
| // join, tsj, groupby, materialize, exchange, union, mvi, root, |
| // mj, hj, sort_groupby, hash_groupby, sort, esp_exchange, pa, papa |
| // at this time and supports type1, type2 and <num of esps> as third |
| // and fourth arguments of join operators (join, tsj, hj, mj). |
| // In addition to the short names (a deprecated feature), the syntax |
| // now includes identifiers that are equal to those printed by EXPLAIN. |
| |
| // support is also added to handle scan(list of scan options) where |
| // scan options are passed as ForcedScanWildCard objects in the list |
| // *args. support for scan(table) and scan(table, index) remains. |
| // Also have support for MDAM_COLUMNS(columns list) were column list |
| // is a list of sparse, dense, or system options passed as ConstValues. |
| |
| if (fname == "SCAN" OR |
| fname == "FILE_SCAN" OR |
| fname == "INDEX_SCAN") |
| { |
| Int32 numArgs = args->entries(); |
| Int32 firstNonStringArg=0; |
| NAString tableName(CmpCommon::statementHeap()); |
| NAString indexName(CmpCommon::statementHeap()); |
| NABoolean dummyNegate = FALSE; |
| ItemExpr *itm; |
| |
| // first we would like to find the first non String argument |
| // this is because we like to handle the shortcut cases |
| // scan('t1',...) and scan('t1','i1',...). |
| |
| NABoolean exitFlag = FALSE; |
| Int32 i; |
| |
| for (i=0; i<numArgs; i++) |
| { |
| itm = args->at(i)->castToItemExpr(); |
| if (itm && itm->castToConstValue(dummyNegate)) |
| { |
| const NAType *nat = (itm->castToConstValue(dummyNegate))->getType(); |
| if (nat->getTypeQualifier() != NA_CHARACTER_TYPE) |
| exitFlag = TRUE; |
| } |
| else |
| exitFlag = TRUE; |
| if (exitFlag) break; |
| } |
| firstNonStringArg = i; |
| |
| if (firstNonStringArg >= 3) |
| { |
| // no more than two literals can be passed |
| *diags << DgSqlCode(-3113) << |
| DgString0("More than 2 string arguments in scan(...) statement."); |
| return NULL; |
| } |
| |
| if ((numArgs >= 1)&&(firstNonStringArg >= 1)) |
| { |
| // first argument is a string specifying the table name |
| itm = args->at(0)->castToItemExpr(); |
| if (itm == NULL OR |
| itm->castToConstValue(dummyNegate) == NULL) |
| { |
| *diags << DgSqlCode(-3113) << |
| DgString0("Character constant for table name expected"); |
| return NULL; |
| } |
| tableName = NAString( |
| (char *) itm->castToConstValue(dummyNegate)->getConstValue(), |
| (size_t) itm->castToConstValue(dummyNegate)->getStorageSize()); |
| // sorry, no delimited identifiers |
| tableName.toUpper(); |
| } |
| if ((numArgs >= 2)&&(firstNonStringArg >= 2)) |
| { |
| // second argument is the index name |
| itm = args->at(1)->castToItemExpr(); |
| if (itm == NULL OR |
| itm->castToConstValue(dummyNegate) == NULL) |
| { |
| *diags << DgSqlCode(-3113) << |
| DgString0("Character constant for index name expected"); |
| return NULL; |
| } |
| indexName = NAString( |
| (char *) itm->castToConstValue(dummyNegate)->getConstValue(), |
| (size_t) itm->castToConstValue(dummyNegate)->getStorageSize()); |
| // sorry, no delimited identifiers |
| indexName.toUpper(); |
| } |
| |
| if (indexName != "") |
| result = new (heap) ScanForceWildCard(tableName,indexName); |
| else if(tableName != "") |
| result = new (heap) ScanForceWildCard(tableName); |
| else |
| result = new (heap) ScanForceWildCard(); |
| |
| // Now update result with other scan options using |
| // mergeScanOptions() functions |
| for (i=firstNonStringArg; i<numArgs; i++) |
| { |
| if (args->at(i)->getOperatorType()!=REL_FORCE_ANY_SCAN) |
| { |
| *diags << DgSqlCode(-3113) << |
| DgString0("Illegal scan(...) argument."); |
| return NULL; |
| } |
| ScanForceWildCard* tmpScan = (ScanForceWildCard*)args->at(i); |
| if (!((ScanForceWildCard*)result)->mergeScanOptions(*tmpScan)) |
| { |
| // This can only happen if conflicting scan options were given |
| *diags << DgSqlCode(-3113) << |
| DgString0("Conflicting scan options."); |
| return NULL; |
| } |
| } |
| |
| if (((ScanForceWildCard*)result)->doesThisCoflictMasterSwitch()) |
| { |
| *diags << DgSqlCode(-3113) << |
| DgString0("MDAM options Conflicting With Master Switch."); |
| return NULL; |
| } |
| ((ScanForceWildCard*)result)->prepare(); |
| } |
| |
| else if (fname == "MDAM_COLUMNS") |
| { |
| result = new (heap) ScanForceWildCard(); |
| NABoolean dummyNegate = FALSE; |
| Int32 numColumns = args->entries(); |
| ItemExpr *itm; |
| |
| ScanForceWildCard::scanOptionEnum* columnAlgorithms |
| = new (heap) ScanForceWildCard::scanOptionEnum[numColumns]; |
| |
| for (Int32 i=0; i<numColumns; i++) |
| { |
| itm = args->at(i)->castToItemExpr(); |
| if (itm == NULL OR itm->castToConstValue(dummyNegate) == NULL) |
| { |
| *diags << DgSqlCode(-3113) << |
| DgString0("Illegal MDAM_COLUMNS (...) argument"); |
| return NULL; |
| } |
| const NAType *nat = itm->castToConstValue(dummyNegate)->getType(); |
| if (nat->getTypeQualifier() != NA_NUMERIC_TYPE) |
| { |
| *diags << DgSqlCode(-3113) << |
| DgString0("Illegal MDAM_COLUMNS (...) argument"); |
| return NULL; |
| } |
| short arg = |
| itm->castToConstValue(dummyNegate)->getExactNumericValue(); |
| |
| if (arg == _SYSTEM_) |
| { |
| columnAlgorithms[i] = ScanForceWildCard::COLUMN_SYSTEM; |
| } |
| else if (arg == _SPARSE_) |
| { |
| columnAlgorithms[i] = ScanForceWildCard::COLUMN_SPARSE; |
| } |
| else if (arg == _DENSE_) |
| { |
| columnAlgorithms[i] = ScanForceWildCard::COLUMN_DENSE; |
| } |
| else |
| { |
| *diags << DgSqlCode(-3113) << |
| DgString0("Illegal MDAM_COLUMNS (...) argument."); |
| return NULL; |
| } |
| } |
| ((ScanForceWildCard*)result)-> |
| setColumnOptions(numColumns, columnAlgorithms, |
| ScanForceWildCard::MDAM_COLUMNS_NO_MORE); |
| } |
| |
| else if (fname == "JOIN") |
| { |
| forcedType = FORCED_JOIN; |
| wildcard = REL_FORCE_JOIN; |
| } |
| else if (fname == "TSJ" OR |
| fname == "NESTED_JOIN") |
| { |
| forcedType = FORCED_JOIN; |
| wildcard = REL_FORCE_NESTED_JOIN; |
| } |
| else if (fname == "GROUPBY") |
| { |
| if (badSingleArg(fname,args,diags)) |
| return NULL; |
| result = new (heap) WildCardOp( |
| REL_ANY_GROUP, |
| 0, |
| args->at(0)->castToRelExpr()); |
| } |
| else if (fname == "UNION") |
| { |
| if (badTwoArgs(fname,args,diags)) |
| return NULL; |
| |
| result = new (heap) MergeUnion( |
| args->at(0)->castToRelExpr(), |
| args->at(1)->castToRelExpr()); |
| } |
| else if (fname == "MVI" OR |
| fname == "EXPR") |
| { |
| if (badSingleArg(fname,args,diags)) |
| return NULL; |
| |
| result = new (heap) MapValueIds( |
| args->at(0)->castToRelExpr()); |
| } |
| else if (fname == "FAST_EXTRACT" || |
| fname == "HIVE_INSERT") |
| { |
| if (badSingleArg(fname,args,diags)) |
| return NULL; |
| result = new (heap) WildCardOp( |
| REL_ANY_EXTRACT, |
| 0, |
| args->at(0)->castToRelExpr()); |
| } |
| else if (fname == "SORT") |
| { |
| if (badSingleArg(fname,args,diags)) |
| return NULL; |
| |
| result = new (heap) Sort( |
| args->at(0)->castToRelExpr()); |
| } |
| else if (fname == "EXCHANGE") |
| { |
| forcedType = FORCED_EXCHANGE; |
| wildcard = REL_FORCE_EXCHANGE; |
| whichExch = ExchangeForceWildCard::ANY_EXCH; |
| } |
| else if (fname == "PA" OR |
| fname == "PARTITION_ACCESS") |
| { |
| forcedType = FORCED_EXCHANGE; |
| wildcard = REL_FORCE_EXCHANGE; |
| whichExch = ExchangeForceWildCard::FORCED_PA; |
| } |
| else if (fname == "PAPA" OR |
| fname == "SPLIT_TOP_PA") |
| { |
| forcedType = FORCED_EXCHANGE; |
| wildcard = REL_FORCE_EXCHANGE; |
| whichExch = ExchangeForceWildCard::FORCED_PAPA; |
| } |
| else if (fname == "ESP_EXCHANGE" OR |
| fname == "REPARTITION") |
| { |
| forcedType = FORCED_EXCHANGE; |
| wildcard = REL_FORCE_EXCHANGE; |
| whichExch = ExchangeForceWildCard::FORCED_ESP_EXCHANGE; |
| } |
| else if (fname == "MJ" OR |
| fname == "MERGE_JOIN") |
| { |
| forcedType = FORCED_JOIN; |
| wildcard = REL_FORCE_MERGE_JOIN; |
| } |
| |
| // previous HJ (hybrid_hash_join) is now split into three |
| // HJ (hash_join), type unknown, the nextSubstitute will pick |
| // HHJ (hybrid_hash_join) and OHJ (ordered_hash_join) |
| else if (fname == "HJ" OR |
| fname == "HASH_JOIN") |
| { |
| forcedType = FORCED_JOIN; |
| wildcard = REL_FORCE_HASH_JOIN; |
| } |
| |
| else if (fname == "HHJ" OR |
| fname == "HYBRID_HASH_JOIN") |
| { |
| forcedType = FORCED_JOIN; |
| wildcard = REL_FORCE_HYBRID_HASH_JOIN; |
| } |
| else if (fname == "OHJ" OR |
| fname == "ORDERED_HASH_JOIN") |
| { |
| forcedType = FORCED_JOIN; |
| wildcard = REL_FORCE_ORDERED_HASH_JOIN; |
| } |
| |
| // Ordered cross product type for star schema change. |
| else if (fname == "OCP" OR |
| fname == "ORDERED_CROSS_PRODUCT") |
| { |
| forcedType = FORCED_JOIN; |
| wildcard = REL_FORCE_ORDERED_CROSS_PRODUCT; |
| } |
| |
| else if (fname == "SORT_GROUPBY") |
| { |
| if (badSingleArg(fname,args,diags)) |
| return NULL; |
| |
| result = new (heap) SortGroupBy( |
| args->at(0)->castToRelExpr()); |
| } |
| else if (fname == "HG" OR |
| fname == "HASH_GROUPBY") |
| { |
| if (badSingleArg(fname,args,diags)) |
| return NULL; |
| |
| result = new (heap) HashGroupBy( |
| args->at(0)->castToRelExpr()); |
| } |
| else if (fname == "SHORTCUT_GROUPBY") |
| { |
| if (badSingleArg(fname,args,diags)) |
| return NULL; |
| |
| result = new (heap) ShortCutGroupBy( |
| args->at(0)->castToRelExpr()); |
| } |
| else if (fname == "TRANSPOSE") |
| { |
| if (badSingleArg(fname,args,diags)) |
| return NULL; |
| |
| result = new (heap) Transpose(NULL,NULL, |
| args->at(0)->castToRelExpr()); |
| } |
| else if (fname == "SEQUENCE") |
| { |
| if (badSingleArg(fname,args,diags)) |
| return NULL; |
| |
| result = new (heap) RelSequence(args->at(0)->castToRelExpr()); |
| } |
| else if (fname == "UNPACK") |
| { |
| if (badSingleArg(fname,args,diags)) |
| return NULL; |
| |
| result = new (heap) UnPackRows(0, NULL, NULL, NULL, |
| args->at(0)->castToRelExpr(), |
| NULL_VALUE_ID); |
| } |
| else if (fname == "PACK") |
| { |
| if (badSingleArg(fname,args,diags)) |
| return NULL; |
| |
| result = new (heap) Pack(0, args->at(0)->castToRelExpr()); |
| } |
| else if (fname == "SAMPLE") |
| { |
| if (badSingleArg(fname,args,diags)) |
| return NULL; |
| |
| result = new (heap) RelSample(args->at(0)->castToRelExpr(), |
| RelSample::ANY, |
| NULL); |
| } |
| else if (fname == "ISOLATED_SCALAR_UDF") |
| { |
| Int32 numArgs = args->entries(); |
| if (numArgs > 2) |
| { |
| // No more than two arguments allowed. |
| *diags << DgSqlCode(-3113) << |
| DgString0("Expected no more than 2 arguments in isolated_scalar_udf(...) statement."); |
| return NULL; |
| } |
| |
| // Result is a wildcard for UDF. |
| result = new (heap) UDFForceWildCard(REL_FORCE_ANY_SCALAR_UDF); |
| |
| // Update result with the 'isolated_scalar_udf' options given |
| // in the CQS using mergeUDFOptions() function. |
| for (Int32 i=0; i<numArgs; i++) |
| { |
| if (args->at(i)->getOperatorType()!=REL_FORCE_ANY_SCALAR_UDF) |
| { |
| *diags << DgSqlCode(-3113) << |
| DgString0("Illegal isolated_scalar_udf argument."); |
| return NULL; |
| } |
| |
| UDFForceWildCard *tmpUDF = (UDFForceWildCard*)args->at(i); |
| if (!((UDFForceWildCard*)result)->mergeUDFOptions(*tmpUDF)) |
| { |
| // This can only happen if conflicting udf options were given |
| *diags << DgSqlCode(-3113) << |
| DgString0("Conflicting isolated_scalar_udf options."); |
| return NULL; |
| } |
| } |
| // If action name argument given, then must also specify function name. |
| if (((UDFForceWildCard*)result)->getFunctionName() == "" && |
| ((UDFForceWildCard*)result)->getActionName() != "") |
| { |
| *diags << DgSqlCode(-3113) << |
| DgString0("Missing isolated_scalar_udf SCALAR_UDF option."); |
| return NULL; |
| } |
| } |
| else if (fname == "TMUDF") |
| { |
| Int32 numArgs = args->entries(); |
| OperatorTypeEnum op = REL_ANY_LEAF_TABLE_MAPPING_UDF; |
| RelExpr *child0 = NULL; |
| RelExpr *child1 = NULL; |
| |
| if (numArgs == 1) |
| { |
| if (badSingleArg(fname,args,diags)) |
| return NULL; |
| op = REL_ANY_UNARY_TABLE_MAPPING_UDF; |
| child0 = args->at(0)->castToRelExpr(); |
| } |
| else if (numArgs == 2) |
| { |
| if (badTwoArgs(fname,args,diags)) |
| return NULL; |
| op = REL_ANY_BINARY_TABLE_MAPPING_UDF; |
| child0 = args->at(0)->castToRelExpr(); |
| child1 = args->at(1)->castToRelExpr(); |
| } |
| else if (numArgs > 2) |
| { |
| *diags << DgSqlCode(-3113) << |
| DgString0("TMUDF operator must have 0, 1 or 2 arguments."); |
| return NULL; |
| } |
| |
| result = new (heap) WildCardOp( |
| op, |
| 0, |
| child0, |
| child1); |
| } |
| else |
| { |
| NAString e(fname, CmpCommon::statementHeap()); |
| e += " is not a valid operator for CONTROL QUERY SHAPE"; |
| *diags << DgSqlCode(-3113) << DgString0(e); |
| return NULL; |
| } |
| |
| if (result == NULL AND forcedType == FORCED_JOIN) |
| { |
| // common code for all forced joins, build a relexpr, interpret |
| // additional parameters, and do an error check |
| if (args->entries() < 2 OR args->entries() > 4) |
| { |
| *diags << DgSqlCode(-3113) << |
| DgString0("Join force operators must have 2, 3, or 4 arguments."); |
| return NULL; |
| } |
| |
| JoinForceWildCard::forcedPlanEnum whichPlan = |
| JoinForceWildCard::ANY_PLAN; |
| Int32 numOfEsps = 0; |
| |
| if (args->entries() > 2) |
| { |
| for (Int32 i = 2; i < (Int32)args->entries(); i++) |
| { |
| NABoolean dummyNegate = FALSE; |
| if (NOT (args->at(i)->castToItemExpr() AND |
| args->at(i)->castToItemExpr()->castToConstValue(dummyNegate))) |
| { |
| *diags << DgSqlCode(-3113) << |
| DgString0("Additional join arguments must be TYPE1, TYPE2 or a number."); |
| return NULL; |
| } |
| |
| ConstValue *cv = |
| args->at(i)->castToItemExpr()->castToConstValue(dummyNegate); |
| const NAType *nat = cv->getType(); |
| |
| if (nat->getTypeQualifier() == NA_CHARACTER_TYPE) |
| { |
| NAString planNum ((char *) cv->getConstValue(), |
| (size_t) cv->getStorageSize(), |
| STMTHEAP); |
| planNum.toUpper(); |
| |
| if (planNum == "PLAN0") |
| whichPlan = JoinForceWildCard::FORCED_PLAN0; |
| else if (planNum == "PLAN1") |
| whichPlan = JoinForceWildCard::FORCED_PLAN1; |
| else if (planNum == "PLAN2") |
| whichPlan = JoinForceWildCard::FORCED_PLAN2; |
| else if (planNum == "PLAN3") |
| whichPlan = JoinForceWildCard::FORCED_PLAN3; |
| else if (planNum == "TYPE1") |
| whichPlan = JoinForceWildCard::FORCED_TYPE1; |
| else if (planNum == "TYPE2") |
| whichPlan = JoinForceWildCard::FORCED_TYPE2; |
| else if (planNum == "INDEXJOIN") |
| whichPlan = JoinForceWildCard::FORCED_INDEXJOIN; |
| else |
| { |
| *diags << DgSqlCode(-3113) << |
| DgString0("Expected TYPE1 or TYPE2."); |
| return NULL; |
| } |
| } |
| else if (nat->getTypeQualifier() == NA_NUMERIC_TYPE) |
| { |
| if (cv->getStorageSize() > 2) |
| { |
| *diags << DgSqlCode(-3113) << |
| DgString0("Number of ESPs (short int) expected."); |
| return NULL; |
| } |
| numOfEsps = cv->getExactNumericValue(); |
| } |
| else |
| { |
| *diags << DgSqlCode(-3113) << |
| DgString0("Additional join arguments must be string or integer."); |
| return NULL; |
| } |
| } |
| } |
| |
| if (NOT (args->at(0)->castToRelExpr() AND |
| args->at(1)->castToRelExpr())) |
| { |
| *diags << DgSqlCode(-3113) << |
| DgString0("Join needs two arguments representing a query."); |
| return NULL; |
| } |
| |
| result = new (heap) |
| JoinForceWildCard(wildcard, |
| args->at(0)->castToRelExpr(), |
| args->at(1)->castToRelExpr(), |
| whichPlan, |
| numOfEsps); |
| } |
| else if (result == NULL AND forcedType == FORCED_EXCHANGE) |
| { |
| // ----------------------------------------------------------------- |
| // process forced exchange and additional arguments that specify the |
| // form of logical partitioning and number of ESPs below the exchange |
| // ----------------------------------------------------------------- |
| |
| ExchangeForceWildCard::forcedLogPartEnum whichLogPart = |
| ExchangeForceWildCard::ANY_LOGPART; |
| Lng32 numOfEsps = -1; |
| |
| // check the first argument |
| if (args->entries() < 1 OR |
| args->at(0)->castToRelExpr() == NULL) |
| { |
| *diags << DgSqlCode(-3113) << DgString0( |
| "Shape as first argument expected for exchange operator"); |
| return NULL; |
| } |
| |
| if (args->entries() > 1) |
| { |
| for (Int32 i = 1; i < (Int32)args->entries(); i++) |
| { |
| NABoolean dummyNegate = FALSE; |
| if (NOT (args->at(i)->castToItemExpr() AND |
| args->at(i)->castToItemExpr()-> |
| castToConstValue(dummyNegate))) |
| { |
| *diags << DgSqlCode(-3113) << |
| DgString0("Additional exchange arguments must be 'GROUP', 'SPLIT', 'REPART' or a number (of ESPs)."); |
| return NULL; |
| } |
| |
| ConstValue *cv = |
| args->at(i)->castToItemExpr()->castToConstValue(dummyNegate); |
| const NAType *nat = cv->getType(); |
| |
| if (nat->getTypeQualifier() == NA_CHARACTER_TYPE) |
| { |
| NAString logPart( (char *) cv->getConstValue(), |
| (size_t) cv->getStorageSize(), |
| CmpCommon::statementHeap() ); |
| logPart.toUpper(); |
| |
| if (logPart == "GROUP") |
| whichLogPart = ExchangeForceWildCard::FORCED_GROUP; |
| else if (logPart == "SPLIT") |
| whichLogPart = ExchangeForceWildCard::FORCED_SPLIT; |
| else if (logPart == "REPART") |
| whichLogPart = ExchangeForceWildCard::FORCED_REPART; |
| else |
| { |
| *diags << DgSqlCode(-3113) << |
| DgString0("Expected GROUP, SPLIT, or REPART."); |
| return NULL; |
| } |
| } |
| else if (nat->getTypeQualifier() == NA_NUMERIC_TYPE) |
| { |
| if (cv->getStorageSize() > 2) |
| { |
| *diags << DgSqlCode(-3113) << |
| DgString0("Number of ESPs (short int) expected."); |
| return NULL; |
| } |
| numOfEsps = cv->getExactNumericValue(); |
| } |
| else |
| { |
| *diags << DgSqlCode(-3113) << |
| DgString0("Additional join arguments must be string or integer."); |
| return NULL; |
| } |
| } |
| } |
| |
| // can't force a number of streams for a generic EXCHANGE or a PA node |
| if ((whichExch == ExchangeForceWildCard::ANY_EXCH OR |
| whichExch == ExchangeForceWildCard::FORCED_PA) AND |
| numOfEsps > 0) |
| { |
| *diags << DgSqlCode(-3113) << DgString0( |
| "Number of ESPs can not be specified for EXCHANGE or PARTITION_ACCESS."); |
| return NULL; |
| } |
| |
| // can't force logical partitioning for EXCHANGE or ESP_EXCHANGE |
| if ((whichExch == ExchangeForceWildCard::ANY_EXCH OR |
| whichExch == ExchangeForceWildCard::FORCED_ESP_EXCHANGE) AND |
| whichLogPart != ExchangeForceWildCard::ANY_LOGPART) |
| { |
| *diags << DgSqlCode(-3113) << DgString0( |
| "GROUP, SPLIT, or REPART are allowed for SPLIT_TOP_PA and PARTITION_ACCESS only."); |
| return NULL; |
| } |
| |
| result = new (heap) ExchangeForceWildCard( |
| args->at(0)->castToRelExpr(), |
| whichExch, |
| whichLogPart, |
| numOfEsps); |
| } |
| |
| return result; |
| } |