// append an ascii-version of Join into cachewa.qryText_ void Join::generateCacheKey(CacheWA &cwa) const { RelExpr::generateCacheKeyNode(cwa); if (isNaturalJoin_) { cwa += " natj "; } ItemExpr *pred = joinPredTree_ ? joinPredTree_ : joinPred_.rebuildExprTree(); if (pred) { cwa += " joinPred:"; pred->generateCacheKey(cwa); } generateCacheKeyForKids(cwa); }
// append an ascii-version of GroupByAgg into cachewa.qryText_ void GroupByAgg::generateCacheKey(CacheWA &cwa) const { RelExpr::generateCacheKey(cwa); // group by col/expr is an important part of the key ItemExpr *grpExpr = groupExprTree_ ? groupExprTree_ : groupExpr_.rebuildExprTree(ITM_ITEM_LIST); if (grpExpr) { cwa += " gBy:"; if (isRollup()) { cwa += " roll:"; ItemExpr * ie = rollupGroupExprList().rebuildExprTree(ITM_ITEM_LIST); if (ie) { ie->generateCacheKey(cwa); } } grpExpr->generateCacheKey(cwa); } }
// append an ascii-version of Tuple into cachewa.qryText_ void Tuple::generateCacheKey(CacheWA &cwa) const { // Do not call RelExpr::generateCacheKey(cwa) here because it's redundant. // It does the same things as the code below. RelExpr::generateCacheKey() // calls Tuple::getText() which has logic similar to the following code. ItemExpr *tExpr = tupleExprTree() ? tupleExprTree() : tupleExpr_.rebuildExprTree(); if (tExpr) { cwa += " tupExpr:"; tExpr->generateCacheKey(cwa); } else { RelExpr::generateCacheKey(cwa); } }
// append an ascii-version of Insert into cachewa.qryText_ void Insert::generateCacheKey(CacheWA &cwa) const { GenericUpdate::generateCacheKey(cwa); if (insertColTree_) { cwa += " insCol:"; insertColTree_->generateCacheKey(cwa); } // order by clause is important ItemExpr *orderBy = orderByTree_ ? orderByTree_ : reqdOrder_.rebuildExprTree(); if (orderBy) { cwa += " order:"; orderBy->generateCacheKey(cwa); } const NATable *tbl; if (cwa.getPhase() >= CmpMain::BIND && getTableDesc() && (tbl=getTableDesc()->getNATable()) != NULL) { // If PARTITION clause has been used we must reflect that in the key. if (tbl->isPartitionNameSpecified()) { cwa += " partition:"; cwa += tbl->getClusteringIndex()->getFileSetName().getQualifiedNameAsString().data(); } // If PARTITION range has been used we must reflect that in the key. else if (tbl->isPartitionRangeSpecified()) { cwa += " partition:"; char str[100]; sprintf(str, " from %d to %d", tbl->getExtendedQualName().getPartnClause().getBeginPartitionNumber() , tbl->getExtendedQualName().getPartnClause().getEndPartitionNumber()); cwa += str; } } if (isUpsert()) { cwa += " upsert:"; } }
// append an ascii-version of RelExpr node into cachewa.qryText_ void RelExpr::generateCacheKeyNode(CacheWA &cwa) const { // emit any "[firstn_sorted]" modifier if (firstNRows_ != -1) { char firstN[40]; convertInt64ToAscii(((RelExpr*)this)->getFirstNRows(), firstN); cwa += firstN; cwa += " "; } // emit other "significant" parts of RelExpr cwa += getText(); ItemExpr *pred = selPredTree() ? selPredTree() : getSelectionPred().rebuildExprTree(); if (pred) { cwa += " selPred:"; pred->generateCacheKey(cwa); } // make any optimizer hints part of the postbinder cache key so that // 2 cacheable queries with different optimizer hints do not match if (hint_) { CollIndex x, cnt=hint_->indexCnt(); if (cnt > 0) { cwa += " xhint:"; for (x = 0; x < cnt; x++) { cwa += (*hint_)[x].data(); cwa += ","; } } char str[100]; if (hint_->hasCardinality()) { sprintf(str, "card:%g", hint_->getCardinality()); cwa += str; } if (hint_->hasSelectivity()) { sprintf(str, ",sel:%g", hint_->getSelectivity()); cwa += str; } } }
// append an ascii-version of RelRoot into cachewa.qryText_ void RelRoot::generateCacheKey(CacheWA &cwa) const { RelExpr::generateCacheKey(cwa); ItemExpr *cExpr = compExprTree_ ? compExprTree_ : compExpr_.rebuildExprTree(); if (cExpr) { // append any select list into cache key cwa += " cExpr:"; cExpr->generateCacheKey(cwa); // reflect any "[first n]" cwa += ((RelRoot*)this)->needFirstSortedRows() ? " 1stN" : " "; // Should the select_list aliases be a part of the cache key? // Their not affecting the compiled plan argues for their exclusion. // Their affecting sqlci's expected output argues for their inclusion. RETDesc *rDesc = getRETDesc(); CollIndex degree, x; if (rDesc && (degree=rDesc->getDegree()) > 0) { cwa += " sla:"; for (x = 0; x < degree; x++){ cwa += rDesc->getColRefNameObj(x).getColName().data(); cwa += " "; } // fix 0-061115-0532 (query cache didn't handle select with embedded // update correctly). New/Old corr. names are recorded for embedded // updates here for exact match. This is important because otherwise // a reuse of a query returning the old/new version of values for // a query requesting new/old version is totally possible and // unacceptable. // // Sample embedded update queries // select * from (update tab1 set x = x + 1 where x > 1 return new.*) y; // select * from (update tab1 set x = x + 1 where x > 1 return new.x, old.y) y; // if ( cwa.isUpdate() && isTrueRoot() == FALSE ) { cwa += " corrNamTok:"; cwa += rDesc->getBindWA()->getCorrNameTokens(); } } } // order by clause is important ItemExpr *orderBy = orderByTree_ ? orderByTree_ : reqdOrder_.rebuildExprTree(); if (orderBy) { cwa += " order:"; orderBy->generateCacheKey(cwa); } // statement-level access type & lock mode are important for multiuser // applications. both are reflected in the stmt-level and/or context-wide // TransMode. So, we mimic RelRoot::codeGen logic here: "copy the current // context-wide TransMode, then overlay with this stmt's 'FOR xxx ACCESS' // setting, if any". TransMode tmode; tmode.updateTransMode(CmpCommon::transMode()); StmtLevelAccessOptions &opts = ((RelRoot*)this)->accessOptions(); if (opts.accessType() != ACCESS_TYPE_NOT_SPECIFIED_) { tmode.updateAccessModeFromIsolationLevel (TransMode::ATtoIL(opts.accessType())); tmode.setStmtLevelAccessOptions(); } if (isTrueRoot()) { // these are needed by Javier's qc stats virtual tbl interface cwa.setIsoLvl(tmode.getIsolationLevel()); cwa.setAccessMode(tmode.getAccessMode()); cwa.setAutoCommit(tmode.getAutoCommit()); cwa.setFlags(tmode.getFlags()); cwa.setRollbackMode(tmode.getRollbackMode()); cwa.setAutoabortInterval(tmode.getAutoAbortIntervalInSeconds()); cwa.setMultiCommit(tmode.getMultiCommit()); } // needed to distinguish these queries and avoid a false hit // select * from (delete from t where a=2) as t; // select * from (delete from t where a=2 for SKIP CONFLICT ACCESS) as t; char mode[40]; str_itoa(tmode.getIsolationLevel(), mode); cwa += " il:"; cwa += mode; str_itoa(tmode.getAccessMode(), mode); cwa += " am:"; cwa += mode; // Solution: 10-060418-5903 str_itoa(cwa.getIsoLvlForUpdates(), mode); cwa += " ilu:"; cwa += mode; str_itoa(tmode.getAutoCommit(), mode); cwa += " ac:"; cwa += mode; str_itoa(tmode.getFlags(), mode); cwa += " fl:"; cwa += mode; str_itoa(tmode.getRollbackMode(), mode); cwa += " rm:"; cwa += mode; str_itoa(tmode.getAutoAbortIntervalInSeconds(), mode); cwa += " ai:"; cwa += mode; str_itoa(tmode.getMultiCommit(), mode); cwa += " mc:"; cwa += mode; if (opts.lockMode() != LOCK_MODE_NOT_SPECIFIED_) { // need to distinguish these queries and avoid a false hit // select * from t in share mode; // select * from t in exclusive mode; str_itoa(opts.lockMode(), mode); cwa += " lm:"; cwa += mode; } // updatableSelect_ is essential. Otherwise, queries like // "select * from t" and "select * from t for update" can confuse // query caching into a false hit, causing fullstack/test051 to fail. if (updatableSelect_) { cwa += " 4updt "; } // for update of col [,col]... clause is important ItemExpr *updCol = updateColTree_ ? updateColTree_ : updateCol_.rebuildExprTree(); if (updCol) { cwa += " updCol:"; updCol->generateCacheKey(cwa); } // making the CQS part of the key is more efficient than calling // CompilerEnv::changeEnv() in ControlDB::setRequiredShape() if (reqdShape_) { reqdShape_->unparse(cwa.reqdShape_); } }
// append an ascii-version of GenericUpdate into cachewa.qryText_ void GenericUpdate::generateCacheKey(CacheWA& cwa) const // NB: This comment applies to all generateCacheKey methods. // generateCacheKey is used to generate a string representation s of the // "parameterized" query. Since this string s is used by QCache::lookUp // to determine if a query is in the cache, it is essential that: // (1) two different queries have different string representations // (2) two queries that differ only in their query literals should // have the same string representations // One possible implementation of generateCacheKey is to use the query's // original query text. But, original query text does not satisfy (2). // To get (2), we call generateCacheKey() from RelRoot::normalizeForCache // which, by definition, replaced query literals with constant parameters. // However, generateCacheKey must also satisfy (1). generateCacheKey must // generate two different strings for two logically different queries. // // To satisfy requirements (1) and (2), generateCacheKey and // normalizeForCache must be in sync -- every user-specified expr that // generateCacheKey emits into cwa.qryText_ must be examined by // normalizeForCache for possible replacement of any literal there into // a constant parameter. // // In order for the literal-into-constantparameter replacement to be safe, // isCacheableExpr must visit all user-specified exprs to make sure that // only constants that can be safely cast into the query's target types // are considered cacheable. For example, given this update query // update t set a = 'xyz' where pk = 1; // isCacheableeExpr, normalizeForCache, and generateCacheKey must cooperate // so that: // 1) isCacheableExpr rejects the query as noncacheble if 'xyz' cannot be // safely cast into a's target type, eg, 'xyz' may be too long if a's // type is char(1). // 2) normalizeForCache must visit and replace both 'xyz' and 1 with // appropriate constant parameters. // 3) generateCacheKey must emit some string representation of the // parameterized query, eg, "update t set a = % where pk = %". // generateCacheKey can emit more stuff, eg, internally specified // begin/end-key predicates, but it must emit a string representation // of all user-specified parts of the query. { // append to cwa.qryText_ GenericUpdate's "essential" data members RelExpr::generateCacheKey(cwa); // An extension of the fix to 10-010618-3505, 10-010619-3515: // for "after bind" Insert/Update/Delete queries, include table's // RedefTime into cwa.qryText_ to make sure we get a cache hit only on // query that reference table(s) that have not changed since the query's // addition to the cache. The queries that reference altered table(s) // will never be hit again and will eventually age out of the cache. // This is not strictly necessary, but it speeds up the processing // of insert/update/delete queries on altered tables. const NATable *tbl; if (cwa.getPhase() >= CmpMain::BIND && getTableDesc() && (tbl=getTableDesc()->getNATable()) != NULL) { char redefTime[40]; convertInt64ToAscii(tbl->getRedefTime(), redefTime); cwa += " redef:"; cwa += redefTime; } ItemExpr *newExpr = newRecExprTree_ ? newRecExprTree_ : newRecExpr_.rebuildExprTree(ITM_ITEM_LIST); if (newExpr) { cwa += " newRecExpr:"; newExpr->generateCacheKey(cwa); } // make sure cache key can distinguish these 2 queries: // prepare s from select * from (update t042qT8 set b=7 where a=2) as t; // prepare s from select * from (update t042qT8 set b=7 set on rollback c=2 // where a=2) as t; ItemExpr *setOnRollback; if (newRecBeforeExpr_.entries() > 0 && (setOnRollback=newRecBeforeExpr_.rebuildExprTree(ITM_ITEM_LIST))) { cwa += " setOnRollback:"; setOnRollback->generateCacheKey(cwa); } ItemExpr *execPred = executorPredTree_ ? executorPredTree_ : executorPred_.rebuildExprTree(); if (execPred) { cwa += " execPred:"; execPred->generateCacheKey(cwa); } // MVs -- // The NOLOG parameter is essential. if (isNoLogOperation()) { cwa += " NOLOG"; } // "current of cursor/hostvar" is essential if (currOfCursorName_) { currOfCursorName_->generateCacheKey(cwa); } // not sure if the following are essential, but better to be safe & // slightly inefficient than to deliver a false hit (ie, wrong plan) cwa += mtsStatement_ ? "m1" : "m0"; cwa += noFlow_ ? "n1" : "n0"; cwa += noRollback_ ? "o1" : "o0"; cwa += noCheck_ ? "nc" : "dc"; // not sure if the following are essential, but we don't know how // to quickly & cheaply include them into our cachekey: // updatedTableName_, tabId_, updateToSelectMap_, indexDesc_, // newRecExprArray_, usedColumns_, newRecBeforeExpr_, // newRecBeforeExprArray_, usedBeforeColumns_, potentialOutputs_ // indexNumberArray_, scanIndexDesc_, rowsAffected_, stoi_, // oldToNewMap_ // The following data members are not "essential" to generateCacheKey // (at least "after bind") because they are either covered by other // data members (eg, beginKeyPred and endKeyPred_ are covered by the // selection pred in RelExpr) or they are not yet defined until later // (eg, after the optimize phase): // indexNewRecExprArrays_, beginKeyPred_, endKeyPred_, // pathKeys_, partKeys_, indexBeginKeyPredArray_, // indexEndKeyPredArray_, checkConstraints_ }