// We come here from syntax like // SELECT * FROM :hv PROTOTYPE 'cat.sch.tbl'; -- host variable, static SQL // TABLE $ev; -- env var, static or dynam // (Internally, both host vars and env vars are HostVar objects.) // // If there's an environment variable, get its value at time of compilation, // and stick that into the internal prototype value. Generator will need // to save these env var name/value pairs in the generated code; // Executor needs to use the saved compile-time value of any name that is not // defined at run-time. // // If there's a prototype value, parse it as an actual table name // (1, 2, or 3-part name) and overwrite the bogus values in *this with the // parsed prototype value. Our caller, applyDefaults, will overwrite any // remaining blank name parts. Generator needs to save the host var names // for actual input at run-time, and to save the prototype values for // similarity check at run-time. // // We avoid re-parsing and re-overwriting this CorrName by checking/setting // its bound state. Note that we do not rely on its prototype's bound state // because that is a separate, pointed at object: some Binder subroutines // make local copies of CorrNames, and any apply methods invoked on the locals // would not be propagated to the caller's originals: relying on the then True // value of the prototype's bound state would be fallacious. // // If no error in proto, node is bound and bindWA errStatus unchanged // (note that not having a proto is not an error). // If error in proto, node is left unbound and bindWA errStatus is set. // void CorrName::applyPrototype(BindWA *bindWA) { if (nodeIsBound()) return; HostVar *proto = getPrototype(); if (!proto) { markAsBound(); return; } // CMPASSERT(this == proto->getPrototypeTarget()); CMPASSERT(!proto->getName().isNull()); if (proto->isEnvVar()) { char *value = getenv(proto->getName()); if (!value) { // Environment variable has no defined value. *CmpCommon::diags() << DgSqlCode(-4086) << DgString0(proto->getName()); bindWA->setErrStatus(); return; } // upcase value returned by getenv #pragma nowarn(1506) // warning elimination Int32 len = strlen(value); #pragma warn(1506) // warning elimination char * ucValue = new (bindWA->wHeap()) char[len+1]; str_cpy_convert(ucValue, value, len, -1/*upshift*/); ucValue[len] = 0; proto->getPrototypeValue() = ucValue; // do not free "ucValue" here, it is still used in "proto" // to prevent Coverity RESOURCE_LEAK error, add the following // coverity[leaked_storage] } // defines can not be used on linux platform if (proto->isDefine()) { *CmpCommon::diags() << DgSqlCode(-4155) << DgString0(proto->getName()); bindWA->setErrStatus(); return; } CMPASSERT(!proto->getPrototypeValue().isNull()); // Some of the following code is cloned from the QualifiedName ctor above Parser parser(bindWA->currentCmpContext()); NAString ns("TABLE " + proto->getPrototypeValue() + ";", CmpCommon::statementHeap()); #pragma nowarn(1506) // warning elimination // save the current parserflags setting ULng32 savedParserFlags = Get_SqlParser_Flags (0xFFFFFFFF); StmtQuery *stmt = (StmtQuery *)parser.parseDML(ns, ns.length(), GetAnsiNameCharSet()); // Restore parser flags settings Set_SqlParser_Flags (savedParserFlags); #pragma warn(1506) // warning elimination if (stmt) { CMPASSERT(stmt->getOperatorType() == STM_QUERY); CorrName &protoCorrName = stmt->getQueryExpression()->getScanNode()->getTableName(); // Unless the hostvar type was forced directly, // copy the special table type to me, the prototype may be a // resource fork if (getSpecialType() == ExtendedQualName::NORMAL_TABLE) setSpecialType(protoCorrName); // This if-test prevents pathologies such as // SELECT col FROM :hv1 PROTOTYPE ':hv2 PROTOTYPE ''tbl'''; // but allows // SELECT col FROM :hv1 PROTOTYPE '$ev'; // // (The assertion below ensures that only host var syntax allows prototypes, // that you can't say ... FROM $ev PROTOTYPE '...'.) // HostVar *proto2 = protoCorrName.getPrototype(); CMPASSERT(!proto2 || !proto->isEnvVar()); if (!proto2 || proto2->isEnvVar()) { if (proto2) { // Here if proto2->isEnvVar. // Recurse (once only; the assertion above ensures that) // to get the value of the var, parse it and all. protoCorrName.applyPrototype(bindWA); if (bindWA->errStatus()) return; } #ifndef NDEBUG if (getenv("HV_DEBUG")) cout << "HostVar/Prototype: parsed (" << (Int32)proto->nodeIsBound() << ") " << proto->getName() << " " << protoCorrName.getExposedNameAsAnsiString() << endl; #endif // assert *before* overwriting with 0 // CMPASSERT(!getQualifiedNameObj().getNamePosition()); getQualifiedNameObj() = protoCorrName.getQualifiedNameObj(); proto->setPrototypeType(HostVar::QUALIFIEDNAME); // mark that we, the trusted CorrName, are bound markAsBound(); } } if (!nodeIsBound()) { // Clear parser syntax error and emit "Prototype value not valid" #ifndef NDEBUG if (!getenv("HV_DEBUG")) #endif CmpCommon::diags()->clear(); *CmpCommon::diags() << DgSqlCode(-4087) << DgString0(proto->getPrototypeValue()); bindWA->setErrStatus(); } delete stmt; } // applyPrototype
RelExpr* Scan::normalizeForCache(CacheWA& cwa, BindWA& bindWA) { if (nodeIsNormalizedForCache()) { return this; } if (CmpCommon::getDefault(QUERY_CACHE_TABLENAME) == DF_OFF) { // replace descendants' literals into ConstantParameters return RelExpr::normalizeForCache(cwa, bindWA); } // replace tablename with a prototyped tablename. TableDesc * td = getTableDesc(); CorrName &origName = td->getCorrNameObj(); if (origName.getPrototype() == NULL) { Lng32 CACHED_MAX_ANSI_NAME_EXTERNAL_LEN = 128; NAString hvName("dummy_name"); HostVar * hv = new(bindWA.wHeap()) HostVar(hvName, new(bindWA.wHeap()) SQLChar(CACHED_MAX_ANSI_NAME_EXTERNAL_LEN)); hv->setPrototypeValue(origName.getQualifiedNameAsString()); hv->synthTypeAndValueId(); hv->setIsCachedParam(TRUE); CorrName cn("HostVar$", bindWA.wHeap(), hv->getName(), // debugging ease "$bogus"); cn.setPrototype(hv); NAString *tmpName = new (bindWA.wHeap()) NAString(hv->getPrototypeValue(), bindWA.wHeap()); cn.setUgivenName(*tmpName); cn.applyDefaults(&bindWA, bindWA.getDefaultSchema()); td->setCorrName(cn); setTableName(cn); char * strval = new(bindWA.wHeap()) char[CACHED_MAX_ANSI_NAME_EXTERNAL_LEN]; strcpy(strval, origName.getQualifiedNameAsString().data()); CharType * typ = new(bindWA.wHeap()) SQLChar(CACHED_MAX_ANSI_NAME_EXTERNAL_LEN, FALSE); ConstValue * cv = new(bindWA.wHeap()) ConstValue(typ, strval, CACHED_MAX_ANSI_NAME_EXTERNAL_LEN); ConstantParameter* result = new(bindWA.wHeap()) ConstantParameter (*cv, bindWA.wHeap(), cwa.getPhase() == CmpMain::PARSE); result->synthTypeAndValueId(); cwa.addConstParam(result, bindWA); hv->setPMOrdPosAndIndex(COM_UNKNOWN_DIRECTION, -1, (Int32)cwa.getConstParams().entries()); } // replace descendants' literals into ConstantParameters return RelExpr::normalizeForCache(cwa, bindWA); }