// 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);
}