// 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_
}