| /********************************************************************** |
| // @@@ 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: CacheWA.C |
| * Description: The workarea used by {Rel|Item}Expr::normalizeForCache and |
| * {Rel|Item}Expr::isCacheableExpr |
| * Created: 08/03/2000 |
| * Language: C++ |
| * |
| * |
| * |
| * |
| ************************************************************************* |
| */ |
| |
| #include "CacheWA.h" |
| #include "CmpMain.h" |
| #include "SchemaDB.h" |
| |
| // create an empty list of constant parameters |
| ConstantParameters::ConstantParameters(NAHeap *h) |
| : LIST(ConstantParameter*)(h) |
| { |
| } |
| |
| // free our allocated memory |
| ConstantParameters::~ConstantParameters() |
| { |
| CollIndex x, count = entries(); |
| for (x = 0; x < count; x++) { |
| delete at(x); |
| } |
| } |
| |
| // return our elements' total byte size |
| Lng32 ConstantParameters::getSize() const |
| { |
| Lng32 result = 0; |
| CollIndex x, limit = entries(); |
| for (x=0; x < limit; x++) { |
| result += (*this)[x]->getSize(); |
| } |
| return result; |
| } |
| |
| // create an empty list of SelParameters |
| SelParameters::SelParameters(NAHeap *h) |
| : LIST(SelParameter*)(h) |
| { |
| } |
| |
| // free our allocated memory |
| SelParameters::~SelParameters() |
| { |
| CollIndex x, count = entries(); |
| for (x = 0; x < count; x++) { |
| delete at(x); |
| } |
| } |
| |
| // return our elements' total byte size |
| Lng32 SelParameters::getSize() const |
| { |
| Lng32 result = 0; |
| CollIndex x, limit = entries(); |
| for (x=0; x < limit; x++) { |
| result += (*this)[x]->getSize(); |
| } |
| return result; |
| } |
| |
| // Create and initialize cache work area. Allocate cachewa.qryText_ |
| // with a reasonable estimate of its eventual length to avoid the |
| // penalty of repeated lengthening. Assume current query is not |
| // cacheable until proven otherwise. |
| const NASize_T cacheKeyInitStrLen = 256; |
| CacheWA::CacheWA(NAHeap *h) |
| : heap_(h), qryText_(cacheKeyInitStrLen, h), actuals_(h) |
| , sels_(h), parmTypes_(0), selTypes_(0) |
| , cacheable_(FALSE), conditionallyCacheable_(FALSE) |
| , tkey_(NULL), ckey_(NULL), phase_(CmpMain::END), topRoot_(NULL) |
| , numberOfScans_(0), hasPredicate_(FALSE) |
| , tabDescPtr_(0), usedKyPtr_(0), tabArraySize_(0) |
| , predHasNoLit_(TRUE), reqdShape_("",h) |
| , isoLvl_(TransMode::IL_NOT_SPECIFIED_) |
| , isoLvlIDU_(TransMode::IL_NOT_SPECIFIED_) |
| , accMode_(TransMode::AM_NOT_SPECIFIED_) |
| , autoCmt_(TransMode::AC_NOT_SPECIFIED_) |
| , flags_(0), isViewJoin_(FALSE), useView_(FALSE) |
| , rbackMode_(TransMode::ROLLBACK_MODE_NOT_SPECIFIED_) |
| , isUpdate_(FALSE) |
| , HQCKey_(NULL) |
| , numberOfExprs_(0) |
| , tables_(h,1) |
| , hasRewriteEnabledMV_(FALSE) |
| , hasParameterizedPred_(FALSE) |
| , posCounter_(0) |
| , sqlStmtConstParamPos_(h) |
| , sqlStmtSelParamPos_(h) |
| , hqcSqlConstPos_(h) |
| { |
| requiredPrefixKeys_ = getDefaultAsLong(QUERY_CACHE_REQUIRED_PREFIX_KEYS); |
| } |
| |
| // Free our allocated memory |
| CacheWA::~CacheWA() |
| { |
| if (tkey_) { |
| NADELETE(tkey_,TextKey,heap_); |
| tkey_ = 0; |
| } |
| if (ckey_) { |
| NADELETE(ckey_,CacheKey,heap_); |
| ckey_ = 0; |
| } |
| if (parmTypes_) { |
| NADELETE(parmTypes_,ParameterTypeList,heap_); |
| parmTypes_ = 0; |
| } |
| if (selTypes_) { |
| NADELETE(selTypes_,SelParamTypeList,heap_); |
| selTypes_ = 0; |
| } |
| |
| if (numberOfScans_ > 0) { |
| heap_->deallocateMemory(tabDescPtr_); |
| tabDescPtr_ = 0; |
| |
| // Release the memory used by the ValueIdSets. |
| for (Int32 i = 0; i < numberOfScans_; i++) |
| delete usedKyPtr_[i]; |
| heap_->deallocateMemory(usedKyPtr_); |
| usedKyPtr_ = 0; |
| } |
| } |
| |
| // increment Expr counter and check if current query is still cacheable |
| NABoolean CacheWA::inc_N_check_still_cacheable() |
| { |
| numberOfExprs_++; |
| return numberOfExprs_ <= CmpCommon::getDefaultNumeric(QUERY_CACHE_MAX_EXPRS); |
| } |
| |
| // increase the sizes of the scan arrays by one and insert a new TableDesc |
| // pointer into the table description array. |
| void CacheWA::incNofScans(TableDesc* td) |
| { |
| // Increase the tabArraySize_ and usedKyPtr_ arrays if they are |
| // empty or have used the entire buffer that was allocated during |
| // a previous call to this function. |
| if (numberOfScans_ == tabArraySize_) |
| { |
| // This increases the size that takes advantage of the internal |
| // knowledge of NAMemory overhead, which is 4 bytes minimum. |
| // The sizes of the arrays start at 3 elements, and increase |
| // by 8 after that. |
| if (tabArraySize_ == 0) |
| tabArraySize_ = 3; |
| else |
| tabArraySize_ += 8; |
| |
| TableDescPtr *newT = (TableDescPtr*)heap_->allocateMemory( |
| tabArraySize_ * sizeof(TableDescPtr)); |
| ValueIdSet **newK = (ValueIdSet**)heap_->allocateMemory( |
| tabArraySize_ * sizeof(ValueIdSet*)); |
| |
| // If previous arrays exist, then copy them to the new array and |
| // free the old array |
| if (numberOfScans_ != 0) |
| { |
| memcpy(newT, tabDescPtr_, numberOfScans_ * sizeof(TableDescPtr)); |
| memcpy(newK, usedKyPtr_, numberOfScans_ * sizeof(ValueIdSet*)); |
| heap_->deallocateMemory(tabDescPtr_); |
| heap_->deallocateMemory(usedKyPtr_); |
| } |
| tabDescPtr_ = newT; |
| usedKyPtr_ = newK; |
| } |
| |
| tabDescPtr_[numberOfScans_] = td; |
| usedKyPtr_[numberOfScans_] = new (heap_) ValueIdSet; |
| numberOfScans_++; |
| } |
| |
| void CacheWA::operator+=(const char* s) |
| { |
| // If the current remaining space on qryText_ is less than |
| // strlen(s)+1, we double the capacity for qryText_ in one call |
| // to adjustMemory(). |
| |
| size_t cap = qryText_.capacity(); |
| size_t len = strlen(s); |
| if ( cap - qryText_.length() < len + 1) { |
| qryText_.adjustMemory(MAXOF(len, 2 * cap)); |
| } |
| |
| qryText_.append(s, len); |
| } |
| |
| // add referenced column's valueid to usedKeys |
| void CacheWA::addToUsedKeys(BaseColumn *base) |
| { |
| for (Int32 x=0; x<numberOfScans_; x++) { |
| if (tabDescPtr_[x] == base->getTableDesc()) { |
| usedKyPtr_[x]->addElement(base->getValueId()); |
| return; |
| } |
| } |
| } |
| |
| // is this column parameterizable? |
| NABoolean CacheWA::isParameterizable(BaseColumn *base) |
| { |
| for (Int32 x=0; x<numberOfScans_; x++) { |
| if (tabDescPtr_[x] == base->getTableDesc()) { |
| // column is parameterizable if it's part of a predicate that |
| // specifies at least the requiredPrefixKeys_ |
| return usedKyPtr_[x]->coversFirstN |
| (tabDescPtr_[x]->getClusteringIndex()->getClusteringKeyCols(), |
| requiredPrefixKeys_); |
| } |
| } |
| return FALSE; |
| } |
| |
| // add ConstantParameter to current query's actual parameters |
| void CacheWA::addConstParam(ConstantParameter* p, BindWA& bindwa) |
| { |
| if (p && topRoot_ && phase_ >= CmpMain::BIND) { |
| // we must bind "after-binder" ConstantParameters now and treat them as |
| // "outer references" just like DynamicParams. If we're a cache miss, |
| // our parent query will be fed to the normalizer, transformer, optimizer |
| // which expect to see ConstantParameters as characteristic inputs. |
| p = (ConstantParameter*)p->bindNode(&bindwa); |
| } |
| if (p) { |
| sqlStmtConstParamPos_.insert(actuals_.entries() + sels_.entries()); |
| actuals_.insert(p); |
| topRoot_->addInputVarTree(p); |
| } |
| } |
| |
| // add SelParameter to current query's selection parameters |
| void CacheWA::addSelParam(SelParameter* p, BindWA& bindwa) |
| { |
| if (p && topRoot_ && phase_ >= CmpMain::BIND) { |
| // we must bind "after-binder" ConstantParameters now and treat them as |
| // "outer references" just like DynamicParams. If we're a cache miss, |
| // our parent query will be fed to the normalizer, transformer, optimizer |
| // which expect to see ConstantParameters as characteristic inputs. |
| p = (SelParameter*)p->bindNode(&bindwa); |
| } |
| if (p) { |
| sqlStmtSelParamPos_.insert(actuals_.entries() + sels_.entries()); |
| sels_.insert(p); |
| topRoot_->addInputVarTree(p); |
| } |
| } |
| |
| // return current query's formal parameter types |
| const ParameterTypeList *CacheWA::getFormalParamTypes() |
| { |
| // parmTypes_ is always derived from actuals_; so, free any previous |
| // parmTypes_ before deriving it again from actuals_ |
| if (parmTypes_) { NADELETE(parmTypes_,ParameterTypeList,wHeap()); } |
| // save pointer in parmTypes_ so ~CacheWA() can free it |
| parmTypes_ = new (wHeap()) ParameterTypeList(&actuals_, wHeap()); |
| return parmTypes_; |
| } |
| |
| // return current query's formal SelParamTypes |
| const SelParamTypeList *CacheWA::getFormalSelParamTypes() |
| { |
| // selTypes_ is always derived from sels_; so, free any previous |
| // selTypes_ before deriving it again from sels_ |
| if (selTypes_) { NADELETE(selTypes_,SelParamTypeList,wHeap()); } |
| // save pointer in selTypes_ so ~CacheWA() can free it |
| selTypes_ = new (wHeap()) SelParamTypeList(&sels_, wHeap()); |
| return selTypes_; |
| } |
| |
| // compose and return TextKey of current query |
| TextKey* CacheWA::getTextKey(const char *sText, Lng32 charset, |
| const QryStmtAttributeSet& attrs) |
| { |
| if (!tkey_) { |
| // capture compiler environment for this query |
| CompilerEnv *env = new (wHeap()) CompilerEnv |
| (wHeap(), CmpMain::PREPARSE, attrs); |
| |
| // use query's formal parameter types, etc in creating its candidate Key |
| // because the Key must hash to the same address as its cache entry. |
| tkey_ = new (wHeap()) TextKey(sText, env, wHeap(), charset); |
| } |
| return tkey_; |
| } |
| |
| // compose and return cache key of current query |
| CacheKey* CacheWA::getCacheKey(const QryStmtAttributeSet& attrs) |
| { |
| if (!ckey_) { |
| // capture compiler environment for this query |
| CompilerEnv *env = new (wHeap()) CompilerEnv(wHeap(), phase_, attrs); |
| |
| // use query's formal parameter types, etc in creating its candidate Key |
| // because the Key must hash to the same address as its cache entry. |
| ckey_ = new (wHeap()) CacheKey |
| (qryText_, phase_, env, *getFormalParamTypes(), |
| *getFormalSelParamTypes(), wHeap(), reqdShape_, |
| isoLvl_, accMode_, isoLvlIDU_, autoCmt_, flags_, rbackMode_, |
| tables_, useView_); |
| } |
| return ckey_; |
| } |
| |
| // caller wants us to remember an occurrence of a view join |
| void CacheWA::foundViewJoin() |
| { |
| if (!isViewJoin_) { |
| isViewJoin_ = TRUE; |
| } |
| } |
| |
| // traverse queryExpr and put together its cacheKey |
| void CacheWA::generateCacheKey(RelExpr *queryExpr, const char *sText, |
| Lng32 charset, const NAString& viewsUsed) |
| { |
| if (viewsUsed.length() > 0) { |
| qryText_ += "v:"; |
| qryText_ += viewsUsed.data(); |
| useView_ = TRUE; |
| } |
| if (isViewJoin_) { |
| // prepend view join's text into its cache key |
| qryText_ += sText; |
| char cs[20]; sprintf(cs, "%d", charset); |
| qryText_ += cs; |
| } |
| // save parameterized statement text into cwa.qryText_ |
| queryExpr->generateCacheKey(*this); |
| } |
| |
| // begin part of fix to join plan quality bug. Given a join |
| // select ... from v1, h2, a where ... and h2.id_level = a.id_level |
| // and h2.id_level = '3' and a.id_level = '3' and ... |
| // query caching would normally replace the literals with parameters |
| // ... h2.id_level = ?selParam1 and a.id_level = ?selParam2 |
| // since the same plan can be shared by matching joins whose predicates |
| // have matching selectivities. But, in the above join, this |
| // parameterization of equality selection predicates has the unintended |
| // consequence of preventing a single VEG |
| // ('3' = h2.id_level = a.id_level = '3') |
| // from forming. The result is an inferior hash join that reads 2M rows |
| // from h2 instead of a nested join that reads 29 rows from h2. |
| // When cqd MATCH_CONSTANTS_OF_EQUALITY_PREDICATES is ON, parameterization |
| // of equality selection predicates preserves matches |
| // ... h2.id_level = ?selParam1 and a.id_level = ?selParam1 |
| |
| // helper functions: |
| // return true & matching element if val is in this list |
| NABoolean ConstantParameters::find |
| (ConstValue *val, ConstantParameter **match) |
| { |
| CollIndex x, limit = entries(); |
| for (x = 0; x < limit; x++) { |
| if (at(x)->matches(val)) { |
| *match = at(x); |
| return TRUE; |
| } |
| } |
| return FALSE; |
| } |
| |
| // return true & matching element if val is in this list |
| NABoolean SelParameters::find |
| (ConstValue *val, SelParameter **match) |
| { |
| CollIndex x, limit = entries(); |
| for (x = 0; x < limit; x++) { |
| if (at(x)->matches(val)) { |
| *match = at(x); |
| return TRUE; |
| } |
| } |
| return FALSE; |
| } |
| |
| // replace constant with new or existing ConstantParameter |
| void CacheWA::replaceWithNewOrOldConstParam |
| (ConstValue *val, const NAType *typ, ExprValueId& tgt, BindWA &bindWA) |
| { |
| // match ConstantParameters only in complex queries |
| Lng32 complexity = (Lng32)CmpCommon::getDefaultNumeric |
| (MATCH_CONSTANTS_OF_EQUALITY_PREDICATES); |
| if (complexity >= 0 && numberOfScans_ >= complexity) { |
| ConstantParameter *cParm; |
| if (actuals_.find(val, &cParm)) { |
| cParm->addPosition(++posCounter_); |
| tgt = cParm; // replace with existing ConstantParameter |
| return; |
| } |
| if (sels_.find(val, (SelParameter**)&cParm)) { |
| cParm->addPosition(++posCounter_); |
| tgt = cParm; // replace with existing ConstantParameter |
| return; |
| } |
| } |
| // query is simple or there is no matching constant. |
| // introduce new ConstantParameter. |
| ConstantParameter *nParam = new (wHeap()) ConstantParameter |
| (val, wHeap(), typ, ++posCounter_); |
| addConstParam(nParam, bindWA); |
| tgt = nParam; |
| } |
| |
| // replace constant with new or existing SelParameter |
| void CacheWA::replaceWithNewOrOldSelParam |
| (ConstValue *val, const NAType *typ, const Selectivity sel, ExprValueId& tgt, |
| BindWA &bindWA) |
| { |
| // match SelParameters only in complex queries |
| Lng32 complexity = (Lng32)CmpCommon::getDefaultNumeric |
| (MATCH_CONSTANTS_OF_EQUALITY_PREDICATES); |
| if (complexity >= 0 && numberOfScans_ >= complexity) { |
| SelParameter *sParm; |
| if (actuals_.find(val, (ConstantParameter**)&sParm)) { |
| sParm->addPosition(++posCounter_); |
| tgt = sParm; // replace with existing SelParameter |
| return; |
| } |
| if (sels_.find(val, &sParm)) { |
| sParm->addPosition(++posCounter_); |
| tgt = sParm; // replace with existing SelParameter |
| return; |
| } |
| } |
| // query is simple or there is no matching constant. |
| // introduce new SelParameter. |
| SelParameter *nParam = new (wHeap()) SelParameter |
| (val, wHeap(), typ, sel, ++posCounter_); |
| addSelParam(nParam, bindWA); |
| tgt = nParam; |
| } |
| |