// append an ascii-version of UDFunction into cachewa.qryText_ void UDFunction::generateCacheKey(CacheWA& cwa) const { NARoutine *routine = NULL; NARoutine *action = NULL; cwa += " nam:"; cwa += functionName_.getExternalName().data(); if (cwa.getPhase() >= CmpMain::BIND && getRoutineDesc() && (routine=getRoutineDesc()->getNARoutine()) != NULL) { char redefTime[40]; convertInt64ToAscii(routine->getRedefTime(), redefTime); cwa += " redef:"; cwa += redefTime; if ((routine->getSchemaLabelFileName()) && (str_len(routine->getSchemaLabelFileName()) > 0)) { char schRedefTime[40]; convertInt64ToAscii(routine->getSchemaRedefTime(), schRedefTime); cwa += " schredef:"; cwa += schRedefTime; } } if (getRoutineDesc() && getRoutineDesc()->getActionNameAsGiven().length() != 0) { cwa += " actnam:"; cwa += getRoutineDesc()->getActionNameAsGiven(); if (cwa.getPhase() >= CmpMain::BIND && getRoutineDesc() && (action=getRoutineDesc()->getActionNARoutine()) != NULL) { char redefTime[40]; convertInt64ToAscii(action->getRedefTime(), redefTime); cwa += " actredef:"; cwa += redefTime; } } cwa += "("; Lng32 arity = (Lng32) getArity(); for (Lng32 i = 0; i < arity; i++) { if (i > 0) { cwa += ", "; } child(i)->generateCacheKey(cwa); } if (getRoutineDesc()->getLocale() != 0 ) { cwa += ", LOCALE: "; char dFmt[20]; str_itoa(getRoutineDesc()->getLocale(), dFmt); cwa += dFmt; } cwa += ")"; }
// is this entire expression cacheable after this phase? NABoolean Join::isCacheableExpr(CacheWA& cwa) { if (cwa.getPhase() >= CmpMain::BIND) { // must first descend to scans to get cwa.numberOfScans_ if (!RelExpr::isCacheableExpr(cwa)) { return FALSE; } if (isCacheableNode(cwa.getPhase())) { cwa.setConditionallyCacheable(); } // if we allow joins of views to be cached, query caching cannot // distinguish between (see note at bottom of cachewa.h) // select avg(f.a) from v f, v s group by f.b; // select avg(s.a) from v f, v s group by f.b; // select avg(t.a) from v f, t group by f.b; // assuming v is "create view v from select * from t". We avoid // false cache hits by detecting the possible occurrence of such // view joins here and later using cwa.isViewJoin_ to include // their query texts into their cache keys. // // A view is repsented by a renamed table with isView() returnning // TRUE. RelExpr *c0 = child(0); RelExpr *c1 = child(1); if ((c0->getOperatorType() == REL_RENAME_TABLE && ((RenameTable *)c0)->isView() == TRUE) || (c1->getOperatorType() == REL_RENAME_TABLE && ((RenameTable *)c1)->isView() == TRUE)) { cwa.foundViewJoin(); } // check its join predicate ItemExpr *pred = joinPredTree_ ? joinPredTree_ : joinPred_.rebuildExprTree(); if (pred) { cwa.setHasPredicate(); // is join predicate cacheable? if (pred->hasNoLiterals(cwa)) { // predicate with no literals is cacheable } else { cwa.setPredHasNoLit(FALSE); if (!pred->isCacheableExpr(cwa)) { // a non-cacheable predicate renders Join non-cacheable. setNonCacheable(); return FALSE; } } } return TRUE; // join may be cacheable } return FALSE; }
// is this entire expression cacheable after this phase? NABoolean RelExpr::isCacheableExpr(CacheWA& cwa) { switch (cwa.getPhase()) { case CmpMain::PARSE: case CmpMain::BIND: { // does query have too many ExprNodes? if (cwa.inc_N_check_still_cacheable() == FALSE) { // yes. query with too many ExprNodes is not cacheable. return FALSE; } if (isNonCacheable()) { // this node is not cacheable return FALSE; // so the entire expression is not cacheable // don't mark this node non-cacheable because this // RelExpr may be cacheable after the next phase. } if (isCacheableNode(cwa.getPhase())) { // must be an INSERT, UPDATE, DELETE, or SELECT node; // so, mark this expression as conditionally cacheable. cwa.setConditionallyCacheable(); } // must descend to scans to get cwa.numberOfScans_ if (!cacheableKids(cwa)) { return FALSE; } // this node is either cacheable or maybecacheable // check its selection predicate ItemExpr *pred = selPredTree() ? selPredTree() : getSelectionPred().rebuildExprTree(); if (pred) { cwa.setHasPredicate(); // is selection predicate cacheable? if (pred->hasNoLiterals(cwa)) { // predicate with no literals is cacheable } else { cwa.setPredHasNoLit(FALSE); if (!pred->isCacheableExpr(cwa)) { // a non-cacheable selection predicate // renders entire RelExpr non-cacheable. setNonCacheable(); return FALSE; } } } return TRUE; // RelExpr may be cacheable } default: { const NABoolean notYetImplemented = FALSE; CMPASSERT(notYetImplemented); return FALSE; } } }
// is this entire expression cacheable after this phase? NABoolean Scan::isCacheableExpr(CacheWA& cwa) { if (cwa.getPhase() >= CmpMain::BIND) { // save scan's TableDesc cwa.incNofScans(tabId_); // native hbase access is not cacheable for now. if ((getTableDesc()->getNATable()->isHbaseRowTable()) || (getTableDesc()->getNATable()->isHbaseCellTable())) return FALSE; if (stream_) { // pub-sub streams are not cacheable return FALSE; } // mpalias SELECT is not cacheable unless explicitly requested if (getTableDesc()->getNATable()->isAnMPTableWithAnsiName() && CmpCommon::getDefault(QUERY_CACHE_MPALIAS) == DF_OFF) { return FALSE; } cwa.setConditionallyCacheable(); if (CmpCommon::getDefaultLong(MVQR_REWRITE_LEVEL) >= 1 && QRDescGenerator::hasRewriteEnabledMVs(getTableDesc())) { cwa.setRewriteEnabledMV(); } return RelExpr::isCacheableExpr(cwa); } return FALSE; }
// Only alow UDFunctions to be cached after BIND time. NABoolean UDFunction::isCacheableExpr(CacheWA& cwa) { if (cwa.getPhase() >= CmpMain::BIND) return TRUE; else return FALSE; }
// is any literal in this expr safely coercible to its target type? NABoolean ItemExpr::isSafelyCoercible(CacheWA &cwa) const { if (cwa.getPhase() >= CmpMain::BIND) { Int32 arity = getArity(); for (Int32 x = 0; x < arity; x++) { if (!child(x)->isSafelyCoercible(cwa)) { return FALSE; } } if (arity == 2) { // we have to disallow caching of the following types of exprs: // expr + 123456789012345678901234567890 // expr || 'overlylongstringthatwouldoverflow' ItemExpr *left = child(0), *right = child(1); if (left->getOperatorType() == ITM_CONSTANT) { if (right->getOperatorType() == ITM_CONSTANT) { // "10 + 1" should be safely coercible return TRUE; } else { return ((ConstValue*)left)->canBeSafelyCoercedTo (right->getValueId().getType()); } } else if (right->getOperatorType() == ITM_CONSTANT) { return ((ConstValue*)right)->canBeSafelyCoercedTo (left->getValueId().getType()); } // else both are nonliterals; fall thru } // else nondyadic expr; fall thru return TRUE; } return FALSE; }
// we want BiLogic to be cacheable NABoolean BiLogic::isCacheableExpr(CacheWA& cwa) { if (cwa.getPhase() >= CmpMain::BIND && getArity() == 2) { if (getOperatorType() == ITM_AND) { ItemExpr *leftC=child(0), *rightC=child(1); // we want to descend to both left & right // so cwa.usedKeys & cwa.keyCols get updated NABoolean leftOK = leftC->isCacheableExpr(cwa); NABoolean rightOK = rightC->isCacheableExpr(cwa); // return FALSE if both left & right are not cacheable. if (!leftOK && !rightOK) return FALSE; // allow "select * from t where k1=1 and k2=2" as potentially cacheable return TRUE; // the definitive check whether a query's predicate covers // all referenced tables' key columns can be (and is) done // only in RelRoot::isCacheableExpr() when cwa.usedKeys & // cwa.keyCols are fully defined. Otherwise, queries like // "select * from t1 join t2 on a=x and a=7 where x=7" // may be incorrectly rejected as non-cacheable if t1 has // primary key(a) and t2 has primary key(x). } else if (getOperatorType() == ITM_OR) { return TRUE; // ORs can be cacheable but are not necessarily // parameterizable, see Bilogic::normalizeForCache below. } } return FALSE; }
// are RelExpr's kids cacheable after this phase? NABoolean RelExpr::cacheableKids(CacheWA& cwa) { switch (cwa.getPhase()) { case CmpMain::PARSE: case CmpMain::BIND: { Int32 arity = getArity(); if (arity <= 0) { // we have no kids if (cwa.isConditionallyCacheable()) { // we're conditionally cacheable and have no kids setCacheableNode(cwa.getPhase()); return TRUE; // so, we're cachable } else { return FALSE; // MAYBECACHEABLE is not cacheable at this phase // don't mark this node non-cacheable because this // RelExpr may be cacheable after the next phase. } } // cacheability of child(ren) determine our cacheability for (Int32 x = 0; x < arity; x++) { if (!child(x) || // cases like "insert into t default values" // return 1 from getArity() even if child(0) is NULL; so // guard against this potential mxcmp crash and consider // these cases non-cacheable during the PARSE stage. child(x)->isNonCacheable()) { // the 1st noncacheable child makes us noncacheable setNonCacheable(); return FALSE; } else if (!child(x)->isCacheableExpr(cwa)) { // noncacheable child return FALSE; // don't mark this node non-cacheable because this // RelExpr may be cacheable after the next phase. } else { // cacheable child continue; // look at next child } } // all children are cacheable, so we're cacheable too setCacheableNode(cwa.getPhase()); return TRUE; } default: return FALSE; } }
// append an ascii-version of IsolatedScalarUDF into cachewa.qryText_ void IsolatedScalarUDF::generateCacheKey(CacheWA &cwa) const { NARoutine *routine = NULL; NARoutine *action = NULL; RelExpr::generateCacheKey(cwa); cwa += " UDFname:"; cwa += getRoutineName().getQualifiedNameAsAnsiString().data(); if (cwa.getPhase() >= CmpMain::BIND && getRoutineDesc() && (routine=getRoutineDesc()->getNARoutine()) != NULL) { char redefTime[40]; convertInt64ToAscii(routine->getRedefTime(), redefTime); cwa += " redef:"; cwa += redefTime; } if (getRoutineDesc() != NULL && getRoutineDesc()->isUUDFRoutine()) { cwa += " action:"; cwa += getRoutineDesc()->getActionNameAsGiven(); if (cwa.getPhase() >= CmpMain::BIND && getRoutineDesc() && (action=getRoutineDesc()->getActionNARoutine()) != NULL) { char redefTime[40]; convertInt64ToAscii(action->getRedefTime(), redefTime); cwa += " actredef:"; cwa += redefTime; } } const ItemExpr *paramExpr = (getProcAllParamsTree() == NULL) ? getProcInputParamsVids().rebuildExprTree(ITM_ITEM_LIST) : getProcAllParamsTree(); if (paramExpr) { cwa += " arg:("; paramExpr->generateCacheKey(cwa); cwa += ")"; } }
// is any literal in this expr safely coercible to its target type? NABoolean ValueIdProxy::isSafelyCoercible(CacheWA& cwa) const { if (cwa.getPhase() >= CmpMain::BIND) { if (!derivedFrom_.getItemExpr()->isSafelyCoercible(cwa)) return FALSE; else return TRUE; } return FALSE; }
// does this ItemExpr (dis)qualify query to be cacheable after this phase? NABoolean ConstValue::isCacheableExpr(CacheWA& cwa) { if (cwa.getPhase() <= CmpMain::PARSE && hasUnknownCharSet()) { // string literal with unknown character set cannot be considered // cacheable by pre-binder stages because the information needed // to determine their character set is not known until bind-time. // This fixes genesis case 10-041215-6141, soln 10-041215-2826. return FALSE; } return ItemExpr::isCacheableExpr(cwa); }
// is any literal in this expr safely coercible to its target type? NABoolean UDFunction::isSafelyCoercible(CacheWA& cwa) const { if (cwa.getPhase() >= CmpMain::BIND) { Int32 arity = getArity(); for (Int32 x = 0; x < arity; x++) { if (!child(x)->isSafelyCoercible(cwa)) { return FALSE; } } return TRUE; } return FALSE; }
// is any literal in this expr safely coercible to its target type? NABoolean Cast::isSafelyCoercible(CacheWA &cwa) const { if (cwa.getPhase() >= CmpMain::BIND) { ItemExpr *opd = child(0); if (!opd->isSafelyCoercible(cwa)) { return FALSE; } if (opd->getOperatorType() == ITM_CONSTANT) { return ((ConstValue*)opd)->canBeSafelyCoercedTo(*type_); } return TRUE; } return FALSE; }
// is this entire expression cacheable after this phase? NABoolean RelRoot::isCacheableExpr(CacheWA& cwa) { //queries prefixed by display are not cacheable e.g. display select * from ... if(getDisplayTree()) return FALSE; // Parallel extract producer queries are not cacheable if (numExtractStreams_ > 0) return FALSE; // descend to scans early to get cwa.numberOfScans_ if (!RelExpr::isCacheableExpr(cwa)) { return FALSE; } if (cwa.getPhase() == CmpMain::PARSE) { if (compExprTree_ || compExpr_.entries() > 0) { // insert-returning is not cacheable after parse return FALSE; } } else if (cwa.getPhase() >= CmpMain::BIND) { // make sure select list is cacheable if (compExprTree_) { if (!compExprTree_->isCacheableExpr(cwa)) { return FALSE; } } else if (!compExpr_.isCacheableExpr(cwa)) { return FALSE; } } if (isAnalyzeOnly()) return FALSE; return TRUE; }
// we want BiRelat to be cacheable NABoolean BiRelat::isCacheableExpr(CacheWA& cwa) { if (cwa.getPhase() >= CmpMain::BIND && getArity() == 2) { if (getOperatorType() == ITM_EQUAL) { ItemExpr *leftC=child(0), *rightC=child(1); OperatorTypeEnum leftO = leftC->getOperatorType(); OperatorTypeEnum rightO = rightC->getOperatorType(); BaseColumn *base; if (leftO == ITM_BASECOLUMN) { base = (BaseColumn*)leftC; if (base->isKeyColumnValue(*rightC)) { cwa.addToUsedKeys(base); } return TRUE; } else if (rightO == ITM_BASECOLUMN) { base = (BaseColumn*)rightC; if (base->isKeyColumnValue(*leftC)) { cwa.addToUsedKeys(base); } return TRUE; } else if (leftO == ITM_ITEM_LIST && rightO == ITM_ITEM_LIST && ((ItemList*)leftC)->isListOfCacheableSelPred (cwa, (ItemList*)rightC)) { return TRUE; } else { // we want all other equality comparisons to be cacheable, eg, // retail_div_cd||acct_type_cd||substring(acct_id,1,8)='20V43085193' return TRUE; } } else { return TRUE; // other binary comparison predicates can be cacheable, but are // not necessarily parameterizable, see BiRelat::normalizeForCache } } return FALSE; }
// 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:"; } }
// does this query's selection predicate list qualify query // to be cacheable after this phase? NABoolean ItemList::isListOfCacheableSelPred (CacheWA& cwa, ItemList *other) const { Int32 arity = getArity(); NABoolean result = FALSE; if (cwa.getPhase() >= CmpMain::BIND && other && arity == other->getArity()) { // assume this is an AND list, so, we need only one // cacheable conjunct to consider the list cacheable. for (Int32 x = 0; x < arity; x++) { ItemExpr *leftC = child(x), *rightC = other->child(x); OperatorTypeEnum leftO = leftC->getOperatorType(); OperatorTypeEnum rightO = rightC->getOperatorType(); BaseColumn *base; if (leftO == ITM_BASECOLUMN) { base = (BaseColumn*)leftC; if (base->isKeyColumnValue(*rightC)) { cwa.addToUsedKeys(base); } result = TRUE; continue; } else if (rightO == ITM_BASECOLUMN) { base = (BaseColumn*)rightC; if (base->isKeyColumnValue(*leftC)) { cwa.addToUsedKeys(base); } result = TRUE; continue; } else if (leftO == ITM_ITEM_LIST && rightO == ITM_ITEM_LIST && ((ItemList*)leftC)->isListOfCacheableSelPred (cwa, (ItemList*)rightC)) { result = TRUE; continue; } } } return result; }
// is any literal in this expr safely coercible to its target type? NABoolean Assign::isSafelyCoercible(CacheWA &cwa) const { if (cwa.getPhase() >= CmpMain::BIND) { // we have to disallow caching of the following types of updates: // update t set col = overlylongliteral ItemExpr *src = getSource().getItemExpr(); if (src->getOperatorType() == ITM_CONSTANT) { // source is a literal; make sure this update is cacheable only if // literal can be safely coerced into its target type. return ((ConstValue*)src)->canBeSafelyCoercedTo(getTarget().getType()); } else { // source is not a literal // we need to descend into this expr to verify that no // errors can occur during backpatching. For example, // update t set i = i + 123456789012345678901234567890 // should not be cacheable if i is a smallint. // reject "update t set c=(subquery)" as noncacheable // as part of a fix to CR 10-020108-8401. return src->isSafelyCoercible(cwa) && src->isCacheableExpr(cwa); } } return FALSE; }
// does this entire ItemExpr qualify query to be cacheable after this phase? NABoolean ItemExpr::isCacheableExpr(CacheWA& cwa) { switch (cwa.getPhase()) { default: { const NABoolean notYetImplemented = FALSE; CMPASSERT(notYetImplemented); break; } case CmpMain::PARSE: // ItemExpr::isCacheableExpr is used by Tuple::isCacheable to // determine whether a tuple-insert or a tuplelist-insert is // cacheable after parse. It traverses a tuple's values list // looking for non-cacheable ItemExprs such as HostVar, // DynamicParam, Subquery, etc. // determine cacheability of tuple INSERT's itemexprs here if (isNonCacheable() || !cwa.isConditionallyCacheable()) { return FALSE; } else { // we're either cacheable or maybecacheable Int32 arity = getArity(); if (arity <= 0) { // we have no kids & we're conditionally cacheable return TRUE; // we're cacheable } // cacheability of child(ren) determine our cacheability for (Int32 x = 0; x < arity; x++) { if (!child(x) || child(x)->isNonCacheable()) { // the 1st noncacheable child makes us noncacheable setNonCacheable(); return FALSE; } else if (!child(x)->isCacheableExpr(cwa)) { // noncacheable child return FALSE; } else { // cacheable child continue; // look at next child } } // all children are cacheable, so we're cacheable too setCacheableNode(cwa.getPhase()); return TRUE; } break; case CmpMain::BIND: // does query have too many ExprNodes? if (cwa.inc_N_check_still_cacheable() == FALSE) { // yes. query with too many ExprNodes is not cacheable. return FALSE; } // ItemExpr::isCacheableExpr is used in "after BIND" cases to // visit all operands of an ItemExpr looking for noncacheable // expressions. For example, it discovers that // SELECT (SELECT a FROM t2 WHERE MAX(o.c)>1) FROM t1 o; // is noncacheable only after it visits the outer query's select list. if (isNonCacheable()) { return FALSE; } else if (isCacheableNode(cwa.getPhase())) { return TRUE; } else { // we're either cacheable or maybecacheable // Assume this ItemExpr is an operand of a function or a list. In // this case, a single noncacheable child renders us noncacheable. Int32 arity = getArity(); for (Int32 x = 0; x < arity; x++) { if (!child(x) || !child(x)->isCacheableExpr(cwa)) { // noncacheable child return FALSE; } else { // cacheable child continue; // look at next child } } // all children are cacheable, so we're cacheable too setCacheableNode(cwa.getPhase()); return TRUE; } break; } return FALSE; }
// does this entire ItemExpr qualify query to be cacheable after this phase? NABoolean DynamicParam::isCacheableExpr(CacheWA& cwa) { // a dynamic parameter is not cacheable after parse return (cwa.getPhase() <= CmpMain::PARSE) ? FALSE : TRUE; }
// append an ascii-version of Scan into cachewa.qryText_ void Scan::generateCacheKey(CacheWA &cwa) const { RelExpr::generateCacheKey(cwa); // Fix to 10-010618-3505, 10-010619-3515: include this Scan 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. const NATable *tbl; if (cwa.getPhase() >= CmpMain::BIND && getTableDesc() && (tbl=getTableDesc()->getNATable()) != NULL) { char redefTime[40]; convertInt64ToAscii(tbl->getRedefTime(), redefTime); cwa += " redef:"; cwa += redefTime; if (tbl->isHiveTable()) { char lastModTime[40]; Int64 mTime = tbl->getClusteringIndex()->getHHDFSTableStats()->getModificationTS(); convertInt64ToAscii(mTime, lastModTime); cwa += " lastMod:"; cwa += lastModTime; cwa += " numFiles:"; char numFiles[20]; Int64 numberOfFiles = tbl->getClusteringIndex()->getHHDFSTableStats()->getNumFiles(); sprintf(numFiles, " %ld", numberOfFiles); cwa += numFiles ; } // save pointer to this table. later, QueryCache::addEntry will use // this pointer to get to this table's histograms's timestamp cwa.addTable( (NATable*)tbl ); // 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; } } // We must reflect userTableName_.location into cache key. // Otherwise, two queries which differ only in location such as // table table (table T058a, location $system.zsd12345.x1234500); // table table (table T058a, location $data .zsd12345.x1234500); // can confuse our query caching code to return a false hit and // cause fullstack/test058 to fail. cwa += userTableName_.getLocationName().data(); // Same with stream_ because queries like // "select * from t" and "select * from stream(t)" can // confuse query caching into a false hit causing test079 to fail. if (stream_) { cwa += " stream "; } // mark mpalias queries so they can be decached upon user request if (getTableDesc()->getNATable()->isAnMPTableWithAnsiName()) { cwa += AM_AN_MPALIAS_QUERY; } if (getHbaseAccessOptions()) { cwa += " hbaseVersions: "; char numVersions[20]; sprintf(numVersions, " %d", getHbaseAccessOptions()->getHbaseVersions()); cwa += numVersions ; } }
// is this entire expression cacheable after this phase? NABoolean GenericUpdate::isCacheableExpr(CacheWA& cwa) { // descend to scans early to get cwa.numberOfScans_ if (!RelExpr::isCacheableExpr(cwa)) { return FALSE; } // Make "{update|delete} ... where current of cursor" non-cacheable // so that stale cache will not lead to timestamp mismatch error at // runtime. AQR attempts to handle this error, but only after the // referenced cursor is closed due to transaction rollback. This is // Solution 10-100425-9755. if (currOfCursorName()) { return FALSE; } if (cwa.getPhase() >= CmpMain::BIND) { // make sure any literals in the assignment clause can be safely // cast and assigned to their target types at plan-backpatch-time ItemExpr *newExpr = newRecExprTree_ ? newRecExprTree_ : newRecExpr_.rebuildExprTree(ITM_ITEM_LIST); if (newExpr && !newExpr->isSafelyCoercible(cwa)) { return FALSE; } // reject as non-cacheable queries such as // prepare s from select * from (update t042qT8 set b=7 // set on rollback c=12345678901234567890 where a=2) as t; ItemExpr *setOnRollback; if (newRecBeforeExpr_.entries() > 0 && (setOnRollback=newRecBeforeExpr_.rebuildExprTree(ITM_ITEM_LIST)) && !setOnRollback->isSafelyCoercible(cwa)) { return FALSE; } // make sure any executor predicate is cacheable ItemExpr *execPred = executorPredTree_ ? executorPredTree_ : executorPred_.rebuildExprTree(); if (execPred) { cwa.setHasPredicate(); if (execPred->hasNoLiterals(cwa)) { // predicate with no literals is cacheable } else { cwa.setPredHasNoLit(FALSE); return execPred->isCacheableExpr(cwa); } } // at this time, not cacheable if subquery is specified in // UPDATE SET clause. // This could be enabled later. if (subqInUpdateAssign()) { return FALSE; } } else { if ((getTableName().isPartitionNameSpecified()) || (getTableName().isLocationNameSpecified()) || (getTableName().isPartitionRangeSpecified())) return FALSE; // If PartnClause is used no cache hit before bind stage. } return TRUE; // may be cacheable }
// 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_ }