text: /* -------------------------------------------------------------------------
   *
   * parse_target.cpp
   *	  handle target lists
   *
   * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
   * Portions Copyright (c) 1994, Regents of the University of California
   *
   *
   * IDENTIFICATION
   *	  src/common/backend/parser/parse_target.cpp
   *
   * -------------------------------------------------------------------------
   */
  #include "postgres.h"
  #include "knl/knl_variable.h"
  
  #include "catalog/pg_type.h"
  #include "commands/dbcommands.h"
  #include "funcapi.h"
  #include "miscadmin.h"
  #include "nodes/makefuncs.h"
  #include "nodes/nodeFuncs.h"
  #include "parser/parsetree.h"
  #include "parser/parse_coerce.h"
  #include "parser/parse_expr.h"
  #include "parser/parse_func.h"
  #include "parser/parse_relation.h"
  #include "parser/parse_target.h"
  #include "parser/parse_type.h"
  #include "utils/builtins.h"
  #include "utils/lsyscache.h"
  #include "utils/rel.h"
  #include "utils/rel_gs.h"
  #include "utils/typcache.h"
  
  static void markTargetListOrigin(ParseState* pstate, TargetEntry* tle, Var* var, int levelsup);
  static Node* transformAssignmentIndirection(ParseState* pstate, Node* basenode, const char* targetName,
      bool targetIsArray, Oid targetTypeId, int32 targetTypMod, Oid targetCollation, ListCell* indirection, Node* rhs,
      int location);
  static Node* transformAssignmentSubscripts(ParseState* pstate, Node* basenode, const char* targetName, Oid targetTypeId,
      int32 targetTypMod, Oid targetCollation, List* subscripts, bool isSlice, ListCell* next_indirection, Node* rhs,
      int location);
  static List* ExpandColumnRefStar(ParseState* pstate, ColumnRef* cref, bool targetlist);
  static List* ExpandAllTables(ParseState* pstate, int location);
  static List* ExpandIndirectionStar(ParseState* pstate, A_Indirection* ind, bool targetlist);
  static List* ExpandSingleTable(ParseState* pstate, RangeTblEntry* rte, int location, bool targetlist);
  static List* ExpandRowReference(ParseState* pstate, Node* expr, bool targetlist);
  static int FigureColnameInternal(Node* node, char** name);
  
  /*
   * @Description: return the last filed's name and ignore * or subscrpts
   * @in field: list of field to search
   * @return the found field's name
   */
  static char* find_last_field_name(List* field)
  {
      char* fname = NULL;
      ListCell* l = NULL;
      Node* i = NULL;
      /* find last field name, if any, ignoring "*" and subscripts */
      foreach (l, field) {
          i = (Node*)lfirst(l);
          if (IsA(i, String)) {
              fname = strVal(i);
          }
      }
      return fname;
  }
  
  /*
   * transformTargetEntry()
   *	Transform any ordinary "expression-type" node into a targetlist entry.
   *	This is exported so that parse_clause.c can generate targetlist entries
   *	for ORDER/GROUP BY items that are not already in the targetlist.
   *
   * node		the (untransformed) parse tree for the value expression.
   * expr		the transformed expression, or NULL if caller didn't do it yet.
   * colname	the column name to be assigned, or NULL if none yet set.
   * resjunk	true if the target should be marked resjunk, ie, it is not
   *			wanted in the final projected tuple.
   */
  TargetEntry* transformTargetEntry(ParseState* pstate, Node* node, Node* expr, char* colname, bool resjunk)
  {
      /* Generate a suitable name for column shown in error case */
      if (colname == NULL && !resjunk) {
          colname = FigureIndexColname(node);
      }
      ELOG_FIELD_NAME_START(colname);
  
      /* Transform the node if caller didn't do it already */
      if (expr == NULL) {
          expr = transformExpr(pstate, node);
      }
      ELOG_FIELD_NAME_END;
  
      if (colname == NULL && !resjunk) {
          /*
           * Generate a suitable column name for a column without any explicit
           * 'AS ColumnName' clause, may be different from above call due to
           * change of node expression
           */
          colname = FigureColname(node);
      }
  
      return makeTargetEntry((Expr*)expr, (AttrNumber)pstate->p_next_resno++, colname, resjunk);
  }
  
  /*
   * transformTargetList()
   * Turns a list of ResTarget's into a list of TargetEntry's.
   *
   * At this point, we don't care whether we are doing SELECT, INSERT,
   * or UPDATE; we just transform the given expressions (the "val" fields).
   */
  List* transformTargetList(ParseState* pstate, List* targetlist)
  {
      List* p_target = NIL;
      ListCell* o_target = NULL;
  
      foreach (o_target, targetlist) {
          ResTarget* res = (ResTarget*)lfirst(o_target);
  
          /*
           * Check for "something.*".  Depending on the complexity of the
           * "something", the star could appear as the last field in ColumnRef,
           * or as the last indirection item in A_Indirection.
           */
          if (IsA(res->val, ColumnRef)) {
              ColumnRef* cref = (ColumnRef*)res->val;
  
              if (IsA(llast(cref->fields), A_Star)) {
                  /* It is something.*, expand into multiple items */
                  p_target = list_concat(p_target, ExpandColumnRefStar(pstate, cref, true));
                  continue;
              }
          } else if (IsA(res->val, A_Indirection)) {
              A_Indirection* ind = (A_Indirection*)res->val;
  
              if (IsA(llast(ind->indirection), A_Star)) {
                  /* It is something.*, expand into multiple items */
                  p_target = list_concat(p_target, ExpandIndirectionStar(pstate, ind, true));
                  continue;
              }
          }
  
          /*
           * Not "something.*", so transform as a single expression
           */
          p_target = lappend(p_target, transformTargetEntry(pstate, res->val, NULL, res->name, false));
          pstate->p_target_list = p_target;
      }
  
      return p_target;
  }
  
  /*
   * transformExpressionList()
   *
   * This is the identical transformation to transformTargetList, except that
   * the input list elements are bare expressions without ResTarget decoration,
   * and the output elements are likewise just expressions without TargetEntry
   * decoration.	We use this for ROW() and VALUES() constructs.
   */
  List* transformExpressionList(ParseState* pstate, List* exprlist)
  {
      List* result = NIL;
      ListCell* lc = NULL;
  
      foreach (lc, exprlist) {
          Node* e = (Node*)lfirst(lc);
  
          /*
           * Check for "something.*".  Depending on the complexity of the
           * "something", the star could appear as the last field in ColumnRef,
           * or as the last indirection item in A_Indirection.
           */
          if (IsA(e, ColumnRef)) {
              ColumnRef* cref = (ColumnRef*)e;
  
              if (IsA(llast(cref->fields), A_Star)) {
                  /* It is something.*, expand into multiple items */
                  result = list_concat(result, ExpandColumnRefStar(pstate, cref, false));
                  continue;
              }
          } else if (IsA(e, A_Indirection)) {
              A_Indirection* ind = (A_Indirection*)e;
  
              if (IsA(llast(ind->indirection), A_Star)) {
                  /* It is something.*, expand into multiple items */
                  result = list_concat(result, ExpandIndirectionStar(pstate, ind, false));
                  continue;
              }
          }
  
          /*
           * Not "something.*", so transform as a single expression
           */
          result = lappend(result, transformExpr(pstate, e));
      }
  
      return result;
  }
  
  /*
   * resolveTargetListUnknowns()
   *		Convert any unknown-type targetlist entries to type TEXT.
   *
   * We do this after we've exhausted all other ways of identifying the output
   * column types of a query.
   */
  void resolveTargetListUnknowns(ParseState* pstate, List* targetlist)
  {
      ListCell* l = NULL;
  
      foreach (l, targetlist) {
          TargetEntry* tle = (TargetEntry*)lfirst(l);
          Oid restype = exprType((Node*)tle->expr);
          if (UNKNOWNOID == restype) {
              tle->expr = (Expr*)coerce_type(
                  pstate, (Node*)tle->expr, restype, TEXTOID, -1, COERCION_IMPLICIT, COERCE_IMPLICIT_CAST, -1);
          }
      }
  }
  
  /*
   * markTargetListOrigins()
   *		Mark targetlist columns that are simple Vars with the source
   *		table's OID and column number.
   *
   * Currently, this is done only for SELECT targetlists, since we only
   * need the info if we are going to send it to the frontend.
   */
  void markTargetListOrigins(ParseState* pstate, List* targetlist)
  {
      ListCell* l = NULL;
  
      foreach (l, targetlist) {
          TargetEntry* tle = (TargetEntry*)lfirst(l);
  
          markTargetListOrigin(pstate, tle, (Var*)tle->expr, 0);
      }
  }
  
  /*
   * markTargetListOrigin()
   *		If 'var' is a Var of a plain relation, mark 'tle' with its origin
   *
   * levelsup is an extra offset to interpret the Var's varlevelsup correctly.
   *
   * This is split out so it can recurse for join references.  Note that we
   * do not drill down into views, but report the view as the column owner.
   */
  static void markTargetListOrigin(ParseState* pstate, TargetEntry* tle, Var* var, int levelsup)
  {
      int netlevelsup;
      RangeTblEntry* rte = NULL;
      AttrNumber attnum;
  
      if (var == NULL || !IsA(var, Var)) {
          return;
      }
      netlevelsup = var->varlevelsup + levelsup;
      rte = GetRTEByRangeTablePosn(pstate, var->varno, netlevelsup);
      attnum = var->varattno;
  
      switch (rte->rtekind) {
          case RTE_RELATION:
              /* It's a table or view, report it */
              tle->resorigtbl = rte->relid;
              tle->resorigcol = attnum;
              break;
          case RTE_SUBQUERY:
              /* Subselect-in-FROM: copy up from the subselect */
              if (attnum != InvalidAttrNumber) {
                  TargetEntry* ste = get_tle_by_resno(rte->subquery->targetList, attnum);
  
                  if (ste == NULL || ste->resjunk) {
                      ereport(ERROR,
                          (errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE),
                              errmsg("subquery %s does not have attribute %d", rte->eref->aliasname, attnum)));
                  }
                  tle->resorigtbl = ste->resorigtbl;
                  tle->resorigcol = ste->resorigcol;
              }
              break;
          case RTE_JOIN:
              /* Join RTE --- recursively inspect the alias variable */
              if (attnum != InvalidAttrNumber) {
                  Var* aliasvar = NULL;
  
                  AssertEreport(attnum > 0, MOD_OPT, "");
                  AssertEreport(attnum <= list_length(rte->joinaliasvars), MOD_OPT, "");
                  aliasvar = (Var*)list_nth(rte->joinaliasvars, attnum - 1);
                  markTargetListOrigin(pstate, tle, aliasvar, netlevelsup);
              }
              break;
          case RTE_FUNCTION:
          case RTE_VALUES:
              /* not a simple relation, leave it unmarked */
              break;
          case RTE_CTE:
  
              /*
               * CTE reference: copy up from the subquery, if possible. If the
               * RTE is a recursive self-reference then we can't do anything
               * because we haven't finished analyzing it yet. However, it's no
               * big loss because we must be down inside the recursive term of a
               * recursive CTE, and so any markings on the current targetlist
               * are not going to affect the results anyway.
               */
              if (attnum != InvalidAttrNumber && !rte->self_reference) {
                  CommonTableExpr* cte = GetCTEForRTE(pstate, rte, netlevelsup);
                  TargetEntry* ste = NULL;
  
                  ste = get_tle_by_resno(GetCTETargetList(cte), attnum);
                  if (ste == NULL || ste->resjunk) {
                      ereport(ERROR,
                          (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
                              errmsg("subquery %s does not have attribute %d", rte->eref->aliasname, attnum)));
                  }
                  tle->resorigtbl = ste->resorigtbl;
                  tle->resorigcol = ste->resorigcol;
              }
              break;
  #ifdef PGXC
          case RTE_REMOTE_DUMMY:
              ereport(ERROR, (errcode(ERRCODE_INVALID_OPERATION), errmsg("Invalid RTE found")));
              break;
  #endif /* PGXC */
          default:
              break;
      }
  }
  
  /*
   * transformAssignedExpr()
   *	This is used in INSERT and UPDATE (including DUPLICATE KEY UPDATE)
   *	statements only. It prepares an expression for assignment to a column
   *	of the target table. This includes coercing the given value
   *	to the target column's type (if necessary), and dealing with
   *	any subfield names or subscripts attached to the target column itself
   *	The input expression has already been through transformExpr().
   *
   * pstate		parse state
   * expr			expression to be modified
   * colname		target column name (ie, name of attribute to be assigned to)
   * attrno		target attribute number
   * indirection	subscripts/field names for target column, if any
   * location		error cursor position for the target column, or -1
   *
   * Returns the modified expression.
   *
   * Note: location points at the target column name (SET target or INSERT
   * column name list entry), and must therefore be -1 in an INSERT that
   * omits the column name list.	So we should usually prefer to use
   * exprLocation(expr) for errors that can happen in a default INSERT.
   */
  Expr* transformAssignedExpr(ParseState* pstate, Expr* expr, char* colname, int attrno, List* indirection, int location)
  {
      Oid type_id;    /* type of value provided */
      int32 type_mod; /* typmod of value provided */
      Oid attrtype;   /* type of target column */
      int32 attrtypmod;
      Oid attrcollation; /* collation of target column */
      Relation rd = pstate->p_target_relation;
  
      AssertEreport(rd != NULL, MOD_OPT, "");
      if (attrno <= 0) {
          ereport(ERROR,
              (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                  errmsg("cannot assign to system column \"%s\"", colname),
                  parser_errposition(pstate, location)));
      }
      attrtype = attnumTypeId(rd, attrno);
      attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod;
      attrcollation = rd->rd_att->attrs[attrno - 1]->attcollation;
  
      /*
       * If the expression is a DEFAULT placeholder, insert the attribute's
       * type/typmod/collation into it so that exprType etc will report the
       * right things.  (We expect that the eventually substituted default
       * expression will in fact have this type and typmod.  The collation
       * likely doesn't matter, but let's set it correctly anyway.)  Also,
       * reject trying to update a subfield or array element with DEFAULT, since
       * there can't be any default for portions of a column.
       */
      if (expr && IsA(expr, SetToDefault)) {
          SetToDefault* def = (SetToDefault*)expr;
  
          def->typeId = attrtype;
          def->typeMod = attrtypmod;
          def->collation = attrcollation;
          if (indirection != NULL) {
              if (IsA(linitial(indirection), A_Indices)) {
                  ereport(ERROR,
                      (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                          errmsg("cannot set an array element to DEFAULT"),
                          parser_errposition(pstate, location)));
              } else {
                  ereport(ERROR,
                      (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                          errmsg("cannot set a subfield to DEFAULT"),
                          parser_errposition(pstate, location)));
              }
          }
      }
  
      /* Now we can use exprType() safely. */
      type_id = exprType((Node*)expr);
      type_mod = exprTypmod((Node*)expr);
  
      ELOG_FIELD_NAME_START(colname);
  
      /*
       * If there is indirection on the target column, prepare an array or
       * subfield assignment expression.	This will generate a new column value
       * that the source value has been inserted into, which can then be placed
       * in the new tuple constructed by INSERT or UPDATE.
       */
      if (indirection != NULL) {
          Node* colVar = NULL;
  
          if (pstate->p_is_insert) {
              /*
               * The command is INSERT INTO table (col.something) ... so there
               * is not really a source value to work with. Insert a NULL
               * constant as the source value.
               */
              colVar = (Node*)makeNullConst(attrtype, attrtypmod, attrcollation);
          } else {
              /*
               * Build a Var for the column to be updated.
               */
              colVar = (Node*)make_var(pstate, pstate->p_target_rangetblentry, attrno, location);
          }
  
          expr = (Expr*)transformAssignmentIndirection(pstate,
              colVar,
              colname,
              false,
              attrtype,
              attrtypmod,
              attrcollation,
              list_head(indirection),
              (Node*)expr,
              location);
      } else {
          /*
           * For normal non-qualified target column, do type checking and
           * coercion.
           */
          Node* orig_expr = (Node*)expr;
  
          /*
           * For the insert statement, we may do auto truncation.
           * When (1) p_is_td_compatible_truncation is true;
           * 		(2) target column type is char and varchar type;
           * 		(3) the target column length is smaller than source column length;
           * 				a) the type_mod <= 0, it because the source maybe a const,
           * 					then we all add cast
           * 				b) the source column length and target column length both exist,
           *					then we only add explict cast when the target length is smaller
           *					than source length
           * all satisfied. then we will add an explict cast for source column data to let it
           * could be insert to target column successful.
           * For nvarchar2 type is not support to be truncated automaticlly.
           */
          if (pstate->p_is_td_compatible_truncation && (attrtype == BPCHAROID || attrtype == VARCHAROID) &&
              ((type_mod > 0 && attrtypmod < type_mod) || type_mod < 0)) {
              expr = (Expr*)coerce_to_target_type(
                  pstate, orig_expr, type_id, attrtype, attrtypmod, COERCION_ASSIGNMENT, COERCE_EXPLICIT_CAST, -1);
              pstate->tdTruncCastStatus = TRUNC_CAST_QUERY;
          } else {
              expr = (Expr*)coerce_to_target_type(
                  pstate, orig_expr, type_id, attrtype, attrtypmod, COERCION_ASSIGNMENT, COERCE_IMPLICIT_CAST, -1);
          }
  
          if (expr == NULL) {
              bool invalid_encrypted_column = 
                  ((attrtype == BYTEAWITHOUTORDERWITHEQUALCOLOID || attrtype == BYTEAWITHOUTORDERCOLOID) &&
                  coerce_to_target_type(pstate, orig_expr, type_id, attrtypmod, -1, COERCION_ASSIGNMENT,
                      COERCE_IMPLICIT_CAST, -1));
              if (invalid_encrypted_column) {
                  ereport(ERROR, (errcode(ERRCODE_INVALID_ENCRYPTED_COLUMN_DATA),
                      errmsg("column \"%s\" is of type %s"
                      " but expression is of type %s",
                      colname, format_type_be(attrtype), format_type_be(type_id)),
                      errhint("You will need to rewrite or cast the expression."),
                      parser_errposition(pstate, exprLocation(orig_expr))));
              }
              ereport(ERROR,
                  (errcode(ERRCODE_DATATYPE_MISMATCH),
                      errmsg("column \"%s\" is of type %s"
                             " but expression is of type %s",
                          colname,
                          format_type_be(attrtype),
                          format_type_be(type_id)),
                      errhint("You will need to rewrite or cast the expression."),
                      parser_errposition(pstate, exprLocation(orig_expr))));
  		}
      }
  
      ELOG_FIELD_NAME_END;
  
      return expr;
  }
  
  /*
   * updateTargetListEntry()
   *	This is used in UPDATE (and DUPLICATE KEY UPDATE) statements only.
   *	It prepares an UPDATE TargetEntry for assignment to a column of
   *	the target table. This includes coercing the given value to the
   *	target column's type (if necessary), and dealing with any subfield
   *	names or subscripts attached to the target column itself.
   *
   * pstate		parse state
   * tle			target list entry to be modified
   * colname		target column name (ie, name of attribute to be assigned to)
   * attrno		target attribute number
   * indirection	subscripts/field names for target column, if any
   * location		error cursor position (should point at column name), or -1
   */
  void updateTargetListEntry(
      ParseState* pstate, TargetEntry* tle, char* colname, int attrno, List* indirection, int location)
  {
      /* Fix up expression as needed */
      tle->expr = transformAssignedExpr(pstate, tle->expr, colname, attrno, indirection, location);
  
      /*
       * Set the resno to identify the target column --- the rewriter and
       * planner depend on this.	We also set the resname to identify the target
       * column, but this is only for debugging purposes; it should not be
       * relied on.  (In particular, it might be out of date in a stored rule.)
       */
      tle->resno = (AttrNumber)attrno;
      tle->resname = colname;
  }
  
  /*
   * Process indirection (field selection or subscripting) of the target
   * column in INSERT/UPDATE.  This routine recurses for multiple levels
   * of indirection --- but note that several adjacent A_Indices nodes in
   * the indirection list are treated as a single multidimensional subscript
   * operation.
   *
   * In the initial call, basenode is a Var for the target column in UPDATE,
   * or a null Const of the target's type in INSERT.  In recursive calls,
   * basenode is NULL, indicating that a substitute node should be consed up if
   * needed.
   *
   * targetName is the name of the field or subfield we're assigning to, and
   * targetIsArray is true if we're subscripting it.  These are just for
   * error reporting.
   *
   * targetTypeId, targetTypMod, targetCollation indicate the datatype and
   * collation of the object to be assigned to (initially the target column,
   * later some subobject).
   *
   * indirection is the sublist remaining to process.  When it's NULL, we're
   * done recursing and can just coerce and return the RHS.
   *
   * rhs is the already-transformed value to be assigned; note it has not been
   * coerced to any particular type.
   *
   * location is the cursor error position for any errors.  (Note: this points
   * to the head of the target clause, eg "foo" in "foo.bar[baz]".  Later we
   * might want to decorate indirection cells with their own location info,
   * in which case the location argument could probably be dropped.)
   */
  static Node* transformAssignmentIndirection(ParseState* pstate, Node* basenode, const char* targetName,
      bool targetIsArray, Oid targetTypeId, int32 targetTypMod, Oid targetCollation, ListCell* indirection, Node* rhs,
      int location)
  {
      Node* result = NULL;
      List* subscripts = NIL;
      bool isSlice = false;
      ListCell* i = NULL;
  
      if (indirection != NULL && basenode == NULL) {
          /* Set up a substitution.  We reuse CaseTestExpr for this. */
          CaseTestExpr* ctest = makeNode(CaseTestExpr);
  
          ctest->typeId = targetTypeId;
          ctest->typeMod = targetTypMod;
          ctest->collation = targetCollation;
          basenode = (Node*)ctest;
      }
  
      /*
       * We have to split any field-selection operations apart from
       * subscripting.  Adjacent A_Indices nodes have to be treated as a single
       * multidimensional subscript operation.
       */
      for_each_cell(i, indirection)
      {
          Node* n = (Node*)lfirst(i);
  
          if (IsA(n, A_Indices)) {
              subscripts = lappend(subscripts, n);
              if (((A_Indices*)n)->lidx != NULL) {
                  isSlice = true;
              }
          } else if (IsA(n, A_Star)) {
              ereport(ERROR,
                  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                      errmsg("row expansion via \"*\" is not supported here"),
                      parser_errposition(pstate, location)));
          } else {
              FieldStore* fstore = NULL;
              Oid typrelid;
              AttrNumber attnum;
              Oid fieldTypeId;
              int32 fieldTypMod;
              Oid fieldCollation;
  
              AssertEreport(IsA(n, String), MOD_OPT, "");
  
              /* process subscripts before this field selection */
              if (subscripts != NULL) {
                  /* recurse, and then return because we're done */
                  return transformAssignmentSubscripts(pstate,
                      basenode,
                      targetName,
                      targetTypeId,
                      targetTypMod,
                      targetCollation,
                      subscripts,
                      isSlice,
                      i,
                      rhs,
                      location);
              }
  
              /* No subscripts, so can process field selection here */
              typrelid = typeidTypeRelid(targetTypeId);
              if (!typrelid) {
                  ereport(ERROR,
                      (errcode(ERRCODE_DATATYPE_MISMATCH),
                          errmsg("cannot assign to field \"%s\" of column \"%s\" because its type %s is not a composite "
                                 "type",
                              strVal(n),
                              targetName,
                              format_type_be(targetTypeId)),
                          parser_errposition(pstate, location)));
              }
  
              attnum = get_attnum(typrelid, strVal(n));
              if (attnum == InvalidAttrNumber) {
                  ereport(ERROR,
                      (errcode(ERRCODE_UNDEFINED_COLUMN),
                          errmsg("cannot assign to field \"%s\" of column \"%s\" because there is no such column in data "
                                 "type %s",
                              strVal(n),
                              targetName,
                              format_type_be(targetTypeId)),
                          parser_errposition(pstate, location)));
              }
              if (attnum < 0) {
                  ereport(ERROR,
                      (errcode(ERRCODE_UNDEFINED_COLUMN),
                          errmsg("cannot assign to system column \"%s\"", strVal(n)),
                          parser_errposition(pstate, location)));
              }
              get_atttypetypmodcoll(typrelid, attnum, &fieldTypeId, &fieldTypMod, &fieldCollation);
  
              /* recurse to create appropriate RHS for field assign */
              rhs = transformAssignmentIndirection(
                  pstate, NULL, strVal(n), false, fieldTypeId, fieldTypMod, fieldCollation, lnext(i), rhs, location);
  
              /* and build a FieldStore node */
              fstore = makeNode(FieldStore);
              fstore->arg = (Expr*)basenode;
              fstore->newvals = list_make1(rhs);
              fstore->fieldnums = list_make1_int(attnum);
              fstore->resulttype = targetTypeId;
  
              return (Node*)fstore;
          }
      }
  
      /* process trailing subscripts, if any */
      if (subscripts != NULL) {
          /* recurse, and then return because we're done */
          return transformAssignmentSubscripts(pstate,
              basenode,
              targetName,
              targetTypeId,
              targetTypMod,
              targetCollation,
              subscripts,
              isSlice,
              NULL,
              rhs,
              location);
      }
  
      /* base case: just coerce RHS to match target type ID */
  
      result = coerce_to_target_type(
          pstate, rhs, exprType(rhs), targetTypeId, targetTypMod, COERCION_ASSIGNMENT, COERCE_IMPLICIT_CAST, -1);
      if (result == NULL) {
          if (targetIsArray) {
              ereport(ERROR,
                  (errcode(ERRCODE_DATATYPE_MISMATCH),
                      errmsg("array assignment to \"%s\" requires type %s"
                             " but expression is of type %s",
                          targetName,
                          format_type_be(targetTypeId),
                          format_type_be(exprType(rhs))),
                      errhint("You will need to rewrite or cast the expression."),
                      parser_errposition(pstate, location)));
          } else {
              ereport(ERROR,
                  (errcode(ERRCODE_DATATYPE_MISMATCH),
                      errmsg("subfield \"%s\" is of type %s"
                             " but expression is of type %s",
                          targetName,
                          format_type_be(targetTypeId),
                          format_type_be(exprType(rhs))),
                      errhint("You will need to rewrite or cast the expression."),
                      parser_errposition(pstate, location)));
          }
      }
  
      return result;
  }
  
  /*
   * helper for transformAssignmentIndirection: process array assignment
   */
  static Node* transformAssignmentSubscripts(ParseState* pstate, Node* basenode, const char* targetName, Oid targetTypeId,
      int32 targetTypMod, Oid targetCollation, List* subscripts, bool isSlice, ListCell* next_indirection, Node* rhs,
      int location)
  {
      Node* result = NULL;
      Oid arrayType;
      int32 arrayTypMod;
      Oid elementTypeId;
      Oid typeNeeded;
      Oid collationNeeded;
  
      AssertEreport(subscripts != NIL, MOD_OPT, "");
  
      /* Identify the actual array type and element type involved */
      arrayType = targetTypeId;
      arrayTypMod = targetTypMod;
      elementTypeId = transformArrayType(&arrayType, &arrayTypMod);
  
      /* Identify type that RHS must provide */
      typeNeeded = isSlice ? arrayType : elementTypeId;
  
      /*
       * Array normally has same collation as elements, but there's an
       * exception: we might be subscripting a domain over an array type. In
       * that case use collation of the base type.
       */
      if (arrayType == targetTypeId) {
          collationNeeded = targetCollation;
      } else {
          collationNeeded = get_typcollation(arrayType);
      }
      /* recurse to create appropriate RHS for array assign */
      rhs = transformAssignmentIndirection(
          pstate, NULL, targetName, true, typeNeeded, arrayTypMod, collationNeeded, next_indirection, rhs, location);
  
      /* process subscripts */
      result = (Node*)transformArraySubscripts(pstate, basenode, arrayType, elementTypeId, arrayTypMod, subscripts, rhs);
  
      /* If target was a domain over array, need to coerce up to the domain */
      if (arrayType != targetTypeId) {
          result = coerce_to_target_type(pstate,
              result,
              exprType(result),
              targetTypeId,
              targetTypMod,
              COERCION_ASSIGNMENT,
              COERCE_IMPLICIT_CAST,
              -1);
          /* probably shouldn't fail, but check */
          if (result == NULL) {
              ereport(ERROR,
                  (errcode(ERRCODE_CANNOT_COERCE),
                      errmsg("cannot cast type %s to %s", format_type_be(exprType(result)), format_type_be(targetTypeId)),
                      parser_errposition(pstate, location)));
          }
      }
  
      return result;
  }
  
  /*
   * checkInsertTargets -
   *	  generate a list of INSERT column targets if not supplied, or
   *	  test supplied column names to make sure they are in target table.
   *	  Also return an integer list of the columns' attribute numbers.
   */
  List* checkInsertTargets(ParseState* pstate, List* cols, List** attrnos)
  {
      *attrnos = NIL;
  
      if (cols == NIL) {
          /*
           * Generate default column list for INSERT.
           */
          if (pstate->p_target_relation == NULL) {
              ereport(ERROR,
                  (errmodule(MOD_OPT),
                      errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
                      errmsg("pstate->p_target_relation is NULL unexpectedly")));
          }
  
          Form_pg_attribute* attr = pstate->p_target_relation->rd_att->attrs;
          int numcol = RelationGetNumberOfAttributes(pstate->p_target_relation);
          int i;
  
          for (i = 0; i < numcol; i++) {
              ResTarget* col = NULL;
  
              if (attr[i]->attisdropped) {
                  continue;
              }
              /* If the hidden column in timeseries relation, skip it */
              if (TsRelWithImplDistColumn(attr, i) && RelationIsTsStore(pstate->p_target_relation)) {
                  continue;
              }
  
              col = makeNode(ResTarget);
              col->name = pstrdup(NameStr(attr[i]->attname));
              col->indirection = NIL;
              col->val = NULL;
              col->location = -1;
              cols = lappend(cols, col);
              *attrnos = lappend_int(*attrnos, i + 1);
          }
      } else {
          /*
           * Do initial validation of user-supplied INSERT column list.
           */
          Bitmapset* wholecols = NULL;
          Bitmapset* partialcols = NULL;
          ListCell* tl = NULL;
  
          foreach (tl, cols) {
              ResTarget* col = (ResTarget*)lfirst(tl);
              char* name = col->name;
              int attrno;
  
              /* Lookup column name, ereport on failure */
              attrno = attnameAttNum(pstate->p_target_relation, name, false);
              if (attrno == InvalidAttrNumber) {
                  ereport(ERROR,
                      (errcode(ERRCODE_UNDEFINED_COLUMN),
                          errmsg("column \"%s\" of relation \"%s\" does not exist",
                              name,
                              RelationGetRelationName(pstate->p_target_relation)),
                          parser_errposition(pstate, col->location)));
              }
              /*
               * Check for duplicates, but only of whole columns --- we allow
               * INSERT INTO foo (col.subcol1, col.subcol2)
               */
              if (col->indirection == NIL) {
                  /* whole column; must not have any other assignment */
                  if (bms_is_member(attrno, wholecols) || bms_is_member(attrno, partialcols)) {
                      ereport(ERROR,
                          (errcode(ERRCODE_DUPLICATE_COLUMN),
                              errmsg("column \"%s\" specified more than once", name),
                              parser_errposition(pstate, col->location)));
                  }
                  wholecols = bms_add_member(wholecols, attrno);
              } else {
                  /* partial column; must not have any whole assignment */
                  if (bms_is_member(attrno, wholecols)) {
                      ereport(ERROR,
                          (errcode(ERRCODE_DUPLICATE_COLUMN),
                              errmsg("column \"%s\" specified more than once", name),
                              parser_errposition(pstate, col->location)));
                  }
                  partialcols = bms_add_member(partialcols, attrno);
              }
  
              *attrnos = lappend_int(*attrnos, attrno);
          }
      }
  
      return cols;
  }
  
  /*
   * ExpandColumnRefStar()
   *		Transforms foo.* into a list of expressions or targetlist entries.
   *
   * This handles the case where '*' appears as the last or only item in a
   * ColumnRef.  The code is shared between the case of foo.* at the top level
   * in a SELECT target list (where we want TargetEntry nodes in the result)
   * and foo.* in a ROW() or VALUES() construct (where we want just bare
   * expressions).
   *
   * The referenced columns are marked as requiring SELECT access.
   */
  static List* ExpandColumnRefStar(ParseState* pstate, ColumnRef* cref, bool targetlist)
  {
      List* fields = cref->fields;
      int numnames = list_length(fields);
  
      if (numnames == 1) {
          /*
           * Target item is a bare '*', expand all tables
           *
           * (e.g., SELECT * FROM emp, dept)
           *
           * Since the grammar only accepts bare '*' at top level of SELECT, we
           * need not handle the targetlist==false case here.
           */
          AssertEreport(targetlist, MOD_OPT, "");
          return ExpandAllTables(pstate, cref->location);
      } else {
          /*
           * Target item is relation.*, expand that table
           *
           * (e.g., SELECT emp.*, dname FROM emp, dept)
           *
           * Note: this code is a lot like transformColumnRef; it's tempting to
           * call that instead and then replace the resulting whole-row Var with
           * a list of Vars.	However, that would leave us with the RTE's
           * selectedCols bitmap showing the whole row as needing select
           * permission, as well as the individual columns.  That would be
           * incorrect (since columns added later shouldn't need select
           * permissions).  We could try to remove the whole-row permission bit
           * after the fact, but duplicating code is less messy.
           */
          char* nspname = NULL;
          char* relname = NULL;
          RangeTblEntry* rte = NULL;
          int levels_up;
          enum { CRSERR_NO_RTE, CRSERR_WRONG_DB, CRSERR_TOO_MANY } crserr = CRSERR_NO_RTE;
  
          /*
           * Give the PreParseColumnRefHook, if any, first shot.	If it returns
           * non-null then we should use that expression.
           */
          if (pstate->p_pre_columnref_hook != NULL) {
              Node* node = NULL;
  
              node = (*pstate->p_pre_columnref_hook)(pstate, cref);
              if (node != NULL) {
                  return ExpandRowReference(pstate, node, targetlist);
              }
          }
  
          switch (numnames) {
              case 2:
                  relname = strVal(linitial(fields));
                  rte = refnameRangeTblEntry(pstate, nspname, relname, cref->location, &levels_up);
                  break;
              case 3:
                  nspname = strVal(linitial(fields));
                  relname = strVal(lsecond(fields));
                  rte = refnameRangeTblEntry(pstate, nspname, relname, cref->location, &levels_up);
                  break;
              case 4: {
                  char* catname = strVal(linitial(fields));
  
                  /*
                   * We check the catalog name and then ignore it.
                   */
                  if (strcmp(catname, get_and_check_db_name(u_sess->proc_cxt.MyDatabaseId, false)) != 0) {
                      crserr = CRSERR_WRONG_DB;
                      break;
                  }
                  nspname = strVal(lsecond(fields));
                  relname = strVal(lthird(fields));
                  rte = refnameRangeTblEntry(pstate, nspname, relname, cref->location, &levels_up);
                  break;
              }
              default:
                  crserr = CRSERR_TOO_MANY;
                  break;
          }
  
          /*
           * Now give the PostParseColumnRefHook, if any, a chance. We cheat a
           * bit by passing the RangeTblEntry, not a Var, as the planned
           * translation.  (A single Var wouldn't be strictly correct anyway.
           * This convention allows hooks that really care to know what is
           * happening.)
           */
          if (pstate->p_post_columnref_hook != NULL) {
              Node* node = NULL;
  
              node = (*pstate->p_post_columnref_hook)(pstate, cref, (Node*)rte);
              if (node != NULL) {
                  if (rte != NULL) {
                      ereport(ERROR,
                          (errcode(ERRCODE_AMBIGUOUS_COLUMN),
                              errmsg("column reference \"%s\" is ambiguous", NameListToString(cref->fields)),
                              parser_errposition(pstate, cref->location)));
                  }
                  return ExpandRowReference(pstate, node, targetlist);
              }
          }
  
          /*
           * Throw error if no translation found.
           */
          if (rte == NULL) {
              switch (crserr) {
                  case CRSERR_NO_RTE:
                      errorMissingRTE(pstate, makeRangeVar(nspname, relname, cref->location));
                      break;
                  case CRSERR_WRONG_DB:
                      ereport(ERROR,
                          (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                              errmsg("cross-database references are not implemented: %s", NameListToString(cref->fields)),
                              parser_errposition(pstate, cref->location)));
                      break;
                  case CRSERR_TOO_MANY:
                      ereport(ERROR,
                          (errcode(ERRCODE_SYNTAX_ERROR),
                              errmsg(
                                  "improper qualified name (too many dotted names): %s", NameListToString(cref->fields)),
                              parser_errposition(pstate, cref->location)));
                      break;
                  default:
                      break;
              }
          }
  
          /*
           * OK, expand the RTE into fields.
           */
          return ExpandSingleTable(pstate, rte, cref->location, targetlist);
      }
  }
  
  /*
   * ExpandAllTables()
   *		Transforms '*' (in the target list) into a list of targetlist entries.
   *
   * tlist entries are generated for each relation appearing in the query's
   * varnamespace.  We do not consider relnamespace because that would include
   * input tables of aliasless JOINs, NEW/OLD pseudo-entries, etc.
   *
   * The referenced relations/columns are marked as requiring SELECT access.
   */
  static List* ExpandAllTables(ParseState* pstate, int location)
  {
      List* target = NIL;
      ListCell* l = NULL;
  
      /* Check for SELECT *; */
      if (!pstate->p_varnamespace) {
          ereport(ERROR,
              (errcode(ERRCODE_SYNTAX_ERROR),
                  errmsg("SELECT * with no tables specified is not valid"),
                  parser_errposition(pstate, location)));
      }
      /* add star info(start, end, only) */
      pstate->p_star_start = lappend_int(pstate->p_star_start, pstate->p_next_resno);
      pstate->p_star_only = lappend_int(pstate->p_star_only, 1);
      foreach (l, pstate->p_varnamespace) {
          ParseNamespaceItem *nsitem = (ParseNamespaceItem *)lfirst(l);
          RangeTblEntry *rte = nsitem->p_rte;
          int rtindex = RTERangeTablePosn(pstate, rte, NULL);
  
          /* Should not have any lateral-only items when parsing targetlist */
          Assert(!nsitem->p_lateral_only);
  
          target = list_concat(target, expandRelAttrs(pstate, rte, rtindex, 0, location));
      }
      pstate->p_star_end = lappend_int(pstate->p_star_end, pstate->p_next_resno);
  
      return target;
  }
  
  /*
   * ExpandIndirectionStar()
   *		Transforms foo.* into a list of expressions or targetlist entries.
   *
   * This handles the case where '*' appears as the last item in A_Indirection.
   * The code is shared between the case of foo.* at the top level in a SELECT
   * target list (where we want TargetEntry nodes in the result) and foo.* in
   * a ROW() or VALUES() construct (where we want just bare expressions).
   */
  static List* ExpandIndirectionStar(ParseState* pstate, A_Indirection* ind, bool targetlist)
  {
      Node* expr = NULL;
  
      /* Strip off the '*' to create a reference to the rowtype object */
      ind = (A_Indirection*)copyObject(ind);
      ind->indirection = list_truncate(ind->indirection, list_length(ind->indirection) - 1);
  
      /* And transform that */
      expr = transformExpr(pstate, (Node*)ind);
  
      /* Expand the rowtype expression into individual fields */
      return ExpandRowReference(pstate, expr, targetlist);
  }
  
  /*
   * ExpandSingleTable()
   *		Transforms foo.* into a list of expressions or targetlist entries.
   *
   * This handles the case where foo has been determined to be a simple
   * reference to an RTE, so we can just generate Vars for the expressions.
   *
   * The referenced columns are marked as requiring SELECT access.
   */
  static List* ExpandSingleTable(ParseState* pstate, RangeTblEntry* rte, int location, bool targetlist)
  {
      int sublevels_up;
      int rtindex;
  
      rtindex = RTERangeTablePosn(pstate, rte, &sublevels_up);
  
      if (targetlist) {
          List* te_list = NIL;
  
          /*mark flags for single table just like  foo.* */
          pstate->p_star_start = lappend_int(pstate->p_star_start, pstate->p_next_resno);
          pstate->p_star_only = lappend_int(pstate->p_star_only, -1);
  
          /* expandRelAttrs handles permissions marking */
          te_list = expandRelAttrs(pstate, rte, rtindex, sublevels_up, location);
          pstate->p_star_end = lappend_int(pstate->p_star_end, pstate->p_next_resno);
  
          return te_list;
      } else {
          List* vars = NIL;
          ListCell* l = NULL;
  
          expandRTE(rte, rtindex, sublevels_up, location, false, NULL, &vars);
  
          /*
           * Require read access to the table.  This is normally redundant with
           * the markVarForSelectPriv calls below, but not if the table has zero
           * columns.
           */
          rte->requiredPerms |= ACL_SELECT;
  
          /* Require read access to each column */
          foreach (l, vars) {
              Var* var = (Var*)lfirst(l);
  
              markVarForSelectPriv(pstate, var, rte);
          }
  
          return vars;
      }
  }
  
  /*
   * ExpandRowReference()
   *		Transforms foo.* into a list of expressions or targetlist entries.
   *
   * This handles the case where foo is an arbitrary expression of composite
   * type.
   */
  static List* ExpandRowReference(ParseState* pstate, Node* expr, bool targetlist)
  {
      List* result = NIL;
      TupleDesc tupleDesc;
      int numAttrs;
      int i;
  
      /*
       * If the rowtype expression is a whole-row Var, we can expand the fields
       * as simple Vars.	Note: if the RTE is a relation, this case leaves us
       * with the RTE's selectedCols bitmap showing the whole row as needing
       * select permission, as well as the individual columns.  However, we can
       * only get here for weird notations like (table.*).*, so it's not worth
       * trying to clean up --- arguably, the permissions marking is correct
       * anyway for such cases.
       */
      if (IsA(expr, Var) && ((Var*)expr)->varattno == InvalidAttrNumber) {
          Var* var = (Var*)expr;
          RangeTblEntry* rte = NULL;
  
          rte = GetRTEByRangeTablePosn(pstate, var->varno, var->varlevelsup);
          return ExpandSingleTable(pstate, rte, var->location, targetlist);
      }
  
      /*
       * Otherwise we have to do it the hard way.  Our current implementation is
       * to generate multiple copies of the expression and do FieldSelects.
       * (This can be pretty inefficient if the expression involves nontrivial
       * computation :-(.)
       *
       * Verify it's a composite type, and get the tupdesc.  We use
       * get_expr_result_type() because that can handle references to functions
       * returning anonymous record types.  If that fails, use
       * lookup_rowtype_tupdesc(), which will almost certainly fail as well, but
       * it will give an appropriate error message.
       *
       * If it's a Var of type RECORD, we have to work even harder: we have to
       * find what the Var refers to, and pass that to get_expr_result_type.
       * That task is handled by expandRecordVariable().
       */
      if (IsA(expr, Var) && ((Var*)expr)->vartype == RECORDOID) {
          tupleDesc = expandRecordVariable(pstate, (Var*)expr, 0);
      } else if (get_expr_result_type(expr, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE) {
          tupleDesc = lookup_rowtype_tupdesc_copy(exprType(expr), exprTypmod(expr));
  	}
  
      if (unlikely(tupleDesc == NULL)) {
          ereport(ERROR, 
              (errcode(ERRCODE_UNEXPECTED_NULL_VALUE), 
                  errmsg("tupleDesc should not be null")));
      }
  
      /* Generate a list of references to the individual fields */
      numAttrs = tupleDesc->natts;
      for (i = 0; i < numAttrs; i++) {
          Form_pg_attribute att = tupleDesc->attrs[i];
          FieldSelect* fselect = NULL;
  
          if (att->attisdropped) {
              continue;
          }
          fselect = makeNode(FieldSelect);
          fselect->arg = (Expr*)copyObject(expr);
          fselect->fieldnum = i + 1;
          fselect->resulttype = att->atttypid;
          fselect->resulttypmod = att->atttypmod;
          /* save attribute's collation for parse_collate.c */
          fselect->resultcollid = att->attcollation;
  
          if (targetlist) {
              /* add TargetEntry decoration */
              TargetEntry* te = NULL;
  
              te = makeTargetEntry(
                  (Expr*)fselect, (AttrNumber)pstate->p_next_resno++, pstrdup(NameStr(att->attname)), false);
              result = lappend(result, te);
          } else {
              result = lappend(result, fselect);
          }
      }
  
      return result;
  }
  
  /*
   * expandRecordVariable
   *		Get the tuple descriptor for a Var of type RECORD, if possible.
   *
   * Since no actual table or view column is allowed to have type RECORD, such
   * a Var must refer to a JOIN or FUNCTION RTE or to a subquery output.	We
   * drill down to find the ultimate defining expression and attempt to infer
   * the tupdesc from it.  We ereport if we can't determine the tupdesc.
   *
   * levelsup is an extra offset to interpret the Var's varlevelsup correctly.
   */
  TupleDesc expandRecordVariable(ParseState* pstate, Var* var, int levelsup)
  {
      TupleDesc tupleDesc;
      int netlevelsup;
      RangeTblEntry* rte = NULL;
      AttrNumber attnum;
      Node* expr = NULL;
  
      /* Check my caller didn't mess up */
      AssertEreport(IsA(var, Var), MOD_OPT, "");
      AssertEreport(var->vartype == RECORDOID, MOD_OPT, "");
  
      netlevelsup = var->varlevelsup + levelsup;
      rte = GetRTEByRangeTablePosn(pstate, var->varno, netlevelsup);
      attnum = var->varattno;
  
      if (attnum == InvalidAttrNumber) {
          /* Whole-row reference to an RTE, so expand the known fields */
          List *names = NIL; 
          List *vars = NIL;
          ListCell *lname = NULL; 
          ListCell *lvar = NULL;
          int i;
  
          expandRTE(rte, var->varno, 0, var->location, false, &names, &vars);
  
          tupleDesc = CreateTemplateTupleDesc(list_length(vars), false);
          i = 1;
          forboth(lname, names, lvar, vars) {
              char* label = strVal(lfirst(lname));
              Node* varnode = (Node*)lfirst(lvar);
  
              TupleDescInitEntry(tupleDesc, i, label, exprType(varnode), exprTypmod(varnode), 0);
              TupleDescInitEntryCollation(tupleDesc, i, exprCollation(varnode));
              i++;
          }
          AssertEreport(lname == NULL && lvar == NULL, MOD_OPT, ""); /* lists same length? */
  
          return tupleDesc;
      }
  
      expr = (Node*)var; /* default if we can't drill down */
  
      switch (rte->rtekind) {
          case RTE_RELATION:
          case RTE_VALUES:
  
              /*
               * This case should not occur: a column of a table or values list
               * shouldn't have type RECORD.  Fall through and fail (most
               * likely) at the bottom.
               */
              break;
          case RTE_SUBQUERY: {
              /* Subselect-in-FROM: examine sub-select's output expr */
              TargetEntry* ste = get_tle_by_resno(rte->subquery->targetList, attnum);
  
              if (ste == NULL || ste->resjunk) {
                  ereport(ERROR,
                      (errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE),
                          errmsg("subquery %s does not have attribute %d", rte->eref->aliasname, attnum)));
              }    
              expr = (Node*)ste->expr;
              if (IsA(expr, Var)) {
                  /*
                   * Recurse into the sub-select to see what its Var refers
                   * to.	We have to build an additional level of ParseState
                   * to keep in step with varlevelsup in the subselect.
                   */
                  ParseState mypstate;
                  errno_t rc;
  
                  rc = memset_s(&mypstate, sizeof(mypstate), 0, sizeof(mypstate));
                  securec_check(rc, "\0", "\0");
                  mypstate.parentParseState = pstate;
                  mypstate.p_rtable = rte->subquery->rtable;
                  /* don't bother filling the rest of the fake pstate */
                  return expandRecordVariable(&mypstate, (Var*)expr, 0);
              }
              /* else fall through to inspect the expression */
          } break;
          case RTE_JOIN:
              /* Join RTE --- recursively inspect the alias variable */
              AssertEreport(attnum > 0 && attnum <= list_length(rte->joinaliasvars), MOD_OPT, "");
              expr = (Node*)list_nth(rte->joinaliasvars, attnum - 1);
              if (IsA(expr, Var)) {
                  return expandRecordVariable(pstate, (Var*)expr, netlevelsup);
              }
              /* else fall through to inspect the expression */
              break;
          case RTE_FUNCTION:
  
              /*
               * We couldn't get here unless a function is declared with one of
               * its result columns as RECORD, which is not allowed.
               */
              break;
          case RTE_CTE:
              /* CTE reference: examine subquery's output expr */
              if (!rte->self_reference) {
                  CommonTableExpr* cte = GetCTEForRTE(pstate, rte, netlevelsup);
                  TargetEntry* ste = NULL;
  
                  ste = get_tle_by_resno(GetCTETargetList(cte), attnum);
                  if (ste == NULL || ste->resjunk) {
                      ereport(ERROR,
                          (errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE),
                              errmsg("subquery %s does not have attribute %d", rte->eref->aliasname, attnum)));
                  }    
                  expr = (Node*)ste->expr;
                  if (IsA(expr, Var)) {
                      /*
                       * Recurse into the CTE to see what its Var refers to. We
                       * have to build an additional level of ParseState to keep
                       * in step with varlevelsup in the CTE; furthermore it
                       * could be an outer CTE.
                       */
                      ParseState mypstate;
                      Index levelsup;
                      errno_t rc = EOK;
  
                      rc = memset_s(&mypstate, sizeof(mypstate), 0, sizeof(mypstate));
                      securec_check(rc, "\0", "\0");
                      /* this loop must work, since GetCTEForRTE did */
                      for (levelsup = 0; levelsup < rte->ctelevelsup + netlevelsup; levelsup++) {
                          pstate = pstate->parentParseState;
                      }
                      mypstate.parentParseState = pstate;
                      mypstate.p_rtable = ((Query*)cte->ctequery)->rtable;
                      /* don't bother filling the rest of the fake pstate */
  
                      return expandRecordVariable(&mypstate, (Var*)expr, 0);
                  }
                  /* else fall through to inspect the expression */
              }
              break;
  #ifdef PGXC
          case RTE_REMOTE_DUMMY:
              ereport(ERROR, (errcode(ERRCODE_INVALID_OPERATION), errmsg("Invalid RTE found")));
              break;
  #endif /* PGXC */
          default:
              break;
      }
  
      /*
       * We now have an expression we can't expand any more, so see if
       * get_expr_result_type() can do anything with it.	If not, pass to
       * lookup_rowtype_tupdesc() which will probably fail, but will give an
       * appropriate error message while failing.
       */
      if (get_expr_result_type(expr, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE) {
          tupleDesc = lookup_rowtype_tupdesc_copy(exprType(expr), exprTypmod(expr));
      }
  
      return tupleDesc;
  }
  
  /*
   * FigureColname -
   *	  if the name of the resulting column is not specified in the target
   *	  list, we have to guess a suitable name.  The SQL spec provides some
   *	  guidance, but not much...
   *
   * Note that the argument is the *untransformed* parse tree for the target
   * item.  This is a shade easier to work with than the transformed tree.
   */
  char* FigureColname(Node* node)
  {
      char* name = NULL;
  
      (void)FigureColnameInternal(node, &name);
      if (name != NULL) {
          return name;
      }
      /* default result if we can't guess anything */
      return (char*)"?column?";
  }
  
  /*
   * FigureIndexColname -
   *	  choose the name for an expression column in an index
   *
   * This is actually just like FigureColname, except we return NULL if
   * we can't pick a good name.
   */
  char* FigureIndexColname(Node* node)
  {
      char* name = NULL;
  
      (void)FigureColnameInternal(node, &name);
      return name;
  }
  
  /*
   * FigureColnameInternal -
   *	  internal workhorse for FigureColname
   *
   * Return value indicates strength of confidence in result:
   *		0 - no information
   *		1 - second-best name choice
   *		2 - good name choice
   * The return value is actually only used internally.
   * If the result isn't zero, *name is set to the chosen name.
   */
  static int FigureColnameInternal(Node* node, char** name)
  {
      int strength = 0;
  
      if (node == NULL) {
          return strength;
      }
      switch (nodeTag(node)) {
          case T_ColumnRef: {
              char* fname = find_last_field_name(((ColumnRef*)node)->fields);
              if (fname != NULL) {
                  *name = fname;
                  return 2;
              }
          } break;
          case T_A_Indirection: {
              A_Indirection* ind = (A_Indirection*)node;
              char* fname = find_last_field_name(ind->indirection);
              if (fname != NULL) {
                  *name = fname;
                  return 2;
              }
              return FigureColnameInternal(ind->arg, name);
          } break;
          case T_FuncCall:
              if (((FuncCall*)node)->colname != NULL) {
                  *name = ((FuncCall*)node)->colname;
              } else {
                  *name = strVal(llast(((FuncCall*)node)->funcname));
              }
              return 2;
          case T_A_Expr:
              /* make nullif() act like a regular function */
              if (((A_Expr*)node)->kind == AEXPR_NULLIF) {
                  *name = "nullif";
                  return 2;
              }
              break;
          case T_TypeCast:
              strength = FigureColnameInternal(((TypeCast*)node)->arg, name);
              if (strength <= 1) {
                  if (((TypeCast*)node)->typname != NULL) {
                      *name = strVal(llast(((TypeCast*)node)->typname->names));
                      return 1;
                  }
              }
              break;
          case T_CollateClause:
              return FigureColnameInternal(((CollateClause*)node)->arg, name);
          case T_GroupingFunc:
              /* make GROUPING() act like a regular function */
              *name = "grouping";
              return 2;
          case T_Rownum:
              *name = "rownum";
              return 2;
          case T_SubLink:
              switch (((SubLink*)node)->subLinkType) {
                  case EXISTS_SUBLINK:
                      *name = "exists";
                      return 2;
                  case ARRAY_SUBLINK:
                      *name = "array";
                      return 2;
                  case EXPR_SUBLINK: {
                      /* Get column name of the subquery's single target */
                      SubLink* sublink = (SubLink*)node;
                      Query* query = (Query*)sublink->subselect;
  
                      /*
                       * The subquery has probably already been transformed,
                       * but let's be careful and check that.  (The reason
                       * we can see a transformed subquery here is that
                       * transformSubLink is lazy and modifies the SubLink
                       * node in-place.)
                       */
                      if (IsA(query, Query)) {
                          TargetEntry* te = (TargetEntry*)linitial(query->targetList);
  
                          if (te->resname) {
                              *name = te->resname;
                              return 2;
                          }
                      }
                  } break;
                      /* As with other operator-like nodes, these have no names */
                  case ALL_SUBLINK:
                  case ANY_SUBLINK:
                  case ROWCOMPARE_SUBLINK:
                  case CTE_SUBLINK:
                      break;
              }
              break;
          case T_CaseExpr:
              strength = FigureColnameInternal((Node*)((CaseExpr*)node)->defresult, name);
              if (strength <= 1) {
                  *name = "case";
                  return 1;
              }
              break;
          case T_A_ArrayExpr:
              /* make ARRAY[] act like a function */
              *name = "array";
              return 2;
          case T_RowExpr:
              /* make ROW() act like a function */
              *name = "row";
              return 2;
          case T_CoalesceExpr:
              /* make coalesce() act like a regular function */
              // modify NVL display to A db's style "NVL" instead of "COALESCE"
              if (((CoalesceExpr*)node)->isnvl) {
                  *name = "nvl";
              } else {
                  *name = "coalesce";
              }
              return 2;
          case T_MinMaxExpr:
              /* make greatest/least act like a regular function */
              switch (((MinMaxExpr*)node)->op) {
                  case IS_GREATEST:
                      *name = "greatest";
                      return 2;
                  case IS_LEAST:
                      *name = "least";
                      return 2;
              }
              break;
          case T_XmlExpr:
              /* make SQL/XML functions act like a regular function */
              switch (((XmlExpr*)node)->op) {
                  case IS_XMLCONCAT:
                      *name = "xmlconcat";
                      return 2;
                  case IS_XMLELEMENT:
                      *name = "xmlelement";
                      return 2;
                  case IS_XMLFOREST:
                      *name = "xmlforest";
                      return 2;
                  case IS_XMLPARSE:
                      *name = "xmlparse";
                      return 2;
                  case IS_XMLPI:
                      *name = "xmlpi";
                      return 2;
                  case IS_XMLROOT:
                      *name = "xmlroot";
                      return 2;
                  case IS_XMLSERIALIZE:
                      *name = "xmlserialize";
                      return 2;
                  case IS_DOCUMENT:
                      /* nothing */
                      break;
              }
              break;
          case T_XmlSerialize:
              *name = "xmlserialize";
              return 2;
          default:
              break;
      }
  
      return strength;
  }
  