Logo Search packages:      
Sourcecode: postgresql-8.4 version File versions

parse_relation.c

/*-------------------------------------------------------------------------
 *
 * parse_relation.c
 *      parser support routines dealing with relations
 *
 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *      $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.142 2009/06/11 14:49:00 momjian Exp $
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include <ctype.h>

#include "access/heapam.h"
#include "access/sysattr.h"
#include "catalog/heap.h"
#include "catalog/namespace.h"
#include "catalog/pg_type.h"
#include "funcapi.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "parser/parsetree.h"
#include "parser/parse_relation.h"
#include "parser/parse_type.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"


/* GUC parameter */
bool        add_missing_from;

static RangeTblEntry *scanNameSpaceForRefname(ParseState *pstate,
                                    const char *refname, int location);
static RangeTblEntry *scanNameSpaceForRelid(ParseState *pstate, Oid relid,
                                int location);
static void markRTEForSelectPriv(ParseState *pstate, RangeTblEntry *rte,
                               int rtindex, AttrNumber col);
static bool isLockedRel(ParseState *pstate, char *refname);
static void expandRelation(Oid relid, Alias *eref,
                     int rtindex, int sublevels_up,
                     int location, bool include_dropped,
                     List **colnames, List **colvars);
static void expandTupleDesc(TupleDesc tupdesc, Alias *eref,
                        int rtindex, int sublevels_up,
                        int location, bool include_dropped,
                        List **colnames, List **colvars);
static int  specialAttNum(const char *attname);
static void warnAutoRange(ParseState *pstate, RangeVar *relation);


/*
 * refnameRangeTblEntry
 *      Given a possibly-qualified refname, look to see if it matches any RTE.
 *      If so, return a pointer to the RangeTblEntry; else return NULL.
 *
 *      Optionally get RTE's nesting depth (0 = current) into *sublevels_up.
 *      If sublevels_up is NULL, only consider items at the current nesting
 *      level.
 *
 * An unqualified refname (schemaname == NULL) can match any RTE with matching
 * alias, or matching unqualified relname in the case of alias-less relation
 * RTEs.  It is possible that such a refname matches multiple RTEs in the
 * nearest nesting level that has a match; if so, we report an error via
 * ereport().
 *
 * A qualified refname (schemaname != NULL) can only match a relation RTE
 * that (a) has no alias and (b) is for the same relation identified by
 * schemaname.refname.  In this case we convert schemaname.refname to a
 * relation OID and search by relid, rather than by alias name.  This is
 * peculiar, but it's what SQL92 says to do.
 */
RangeTblEntry *
refnameRangeTblEntry(ParseState *pstate,
                               const char *schemaname,
                               const char *refname,
                               int location,
                               int *sublevels_up)
{
      Oid               relId = InvalidOid;

      if (sublevels_up)
            *sublevels_up = 0;

      if (schemaname != NULL)
      {
            Oid               namespaceId;

            namespaceId = LookupExplicitNamespace(schemaname);
            relId = get_relname_relid(refname, namespaceId);
            if (!OidIsValid(relId))
                  return NULL;
      }

      while (pstate != NULL)
      {
            RangeTblEntry *result;

            if (OidIsValid(relId))
                  result = scanNameSpaceForRelid(pstate, relId, location);
            else
                  result = scanNameSpaceForRefname(pstate, refname, location);

            if (result)
                  return result;

            if (sublevels_up)
                  (*sublevels_up)++;
            else
                  break;

            pstate = pstate->parentParseState;
      }
      return NULL;
}

/*
 * Search the query's table namespace for an RTE matching the
 * given unqualified refname.  Return the RTE if a unique match, or NULL
 * if no match.  Raise error if multiple matches.
 */
static RangeTblEntry *
scanNameSpaceForRefname(ParseState *pstate, const char *refname, int location)
{
      RangeTblEntry *result = NULL;
      ListCell   *l;

      foreach(l, pstate->p_relnamespace)
      {
            RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);

            if (strcmp(rte->eref->aliasname, refname) == 0)
            {
                  if (result)
                        ereport(ERROR,
                                    (errcode(ERRCODE_AMBIGUOUS_ALIAS),
                                     errmsg("table reference \"%s\" is ambiguous",
                                                refname),
                                     parser_errposition(pstate, location)));
                  result = rte;
            }
      }
      return result;
}

/*
 * Search the query's table namespace for a relation RTE matching the
 * given relation OID.  Return the RTE if a unique match, or NULL
 * if no match.  Raise error if multiple matches (which shouldn't
 * happen if the namespace was checked correctly when it was created).
 *
 * See the comments for refnameRangeTblEntry to understand why this
 * acts the way it does.
 */
static RangeTblEntry *
scanNameSpaceForRelid(ParseState *pstate, Oid relid, int location)
{
      RangeTblEntry *result = NULL;
      ListCell   *l;

      foreach(l, pstate->p_relnamespace)
      {
            RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);

            /* yes, the test for alias == NULL should be there... */
            if (rte->rtekind == RTE_RELATION &&
                  rte->relid == relid &&
                  rte->alias == NULL)
            {
                  if (result)
                        ereport(ERROR,
                                    (errcode(ERRCODE_AMBIGUOUS_ALIAS),
                                     errmsg("table reference %u is ambiguous",
                                                relid),
                                     parser_errposition(pstate, location)));
                  result = rte;
            }
      }
      return result;
}

/*
 * Search the query's CTE namespace for a CTE matching the given unqualified
 * refname.  Return the CTE (and its levelsup count) if a match, or NULL
 * if no match.  We need not worry about multiple matches, since parse_cte.c
 * rejects WITH lists containing duplicate CTE names.
 */
CommonTableExpr *
scanNameSpaceForCTE(ParseState *pstate, const char *refname,
                              Index *ctelevelsup)
{
      Index       levelsup;

      for (levelsup = 0;
             pstate != NULL;
             pstate = pstate->parentParseState, levelsup++)
      {
            ListCell   *lc;

            foreach(lc, pstate->p_ctenamespace)
            {
                  CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);

                  if (strcmp(cte->ctename, refname) == 0)
                  {
                        *ctelevelsup = levelsup;
                        return cte;
                  }
            }
      }
      return NULL;
}

/*
 * Search for a possible "future CTE", that is one that is not yet in scope
 * according to the WITH scoping rules.  This has nothing to do with valid
 * SQL semantics, but it's important for error reporting purposes.
 */
static bool
isFutureCTE(ParseState *pstate, const char *refname)
{
      for (; pstate != NULL; pstate = pstate->parentParseState)
      {
            ListCell   *lc;

            foreach(lc, pstate->p_future_ctes)
            {
                  CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);

                  if (strcmp(cte->ctename, refname) == 0)
                        return true;
            }
      }
      return false;
}

/*
 * searchRangeTable
 *      See if any RangeTblEntry could possibly match the RangeVar.
 *      If so, return a pointer to the RangeTblEntry; else return NULL.
 *
 * This is different from refnameRangeTblEntry in that it considers every
 * entry in the ParseState's rangetable(s), not only those that are currently
 * visible in the p_relnamespace lists.  This behavior is invalid per the SQL
 * spec, and it may give ambiguous results (there might be multiple equally
 * valid matches, but only one will be returned).  This must be used ONLY
 * as a heuristic in giving suitable error messages.  See warnAutoRange.
 *
 * Notice that we consider both matches on actual relation (or CTE) name
 * and matches on alias.
 */
static RangeTblEntry *
searchRangeTable(ParseState *pstate, RangeVar *relation)
{
      const char *refname = relation->relname;
      Oid               relId = InvalidOid;
      CommonTableExpr *cte = NULL;
      Index       ctelevelsup = 0;
      Index       levelsup;

      /*
       * If it's an unqualified name, check for possible CTE matches. A CTE
       * hides any real relation matches.  If no CTE, look for a matching
       * relation.
       */
      if (!relation->schemaname)
            cte = scanNameSpaceForCTE(pstate, refname, &ctelevelsup);
      if (!cte)
            relId = RangeVarGetRelid(relation, true);

      /* Now look for RTEs matching either the relation/CTE or the alias */
      for (levelsup = 0;
             pstate != NULL;
             pstate = pstate->parentParseState, levelsup++)
      {
            ListCell   *l;

            foreach(l, pstate->p_rtable)
            {
                  RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);

                  if (rte->rtekind == RTE_RELATION &&
                        OidIsValid(relId) &&
                        rte->relid == relId)
                        return rte;
                  if (rte->rtekind == RTE_CTE &&
                        cte != NULL &&
                        rte->ctelevelsup + levelsup == ctelevelsup &&
                        strcmp(rte->ctename, refname) == 0)
                        return rte;
                  if (strcmp(rte->eref->aliasname, refname) == 0)
                        return rte;
            }
      }
      return NULL;
}

/*
 * Check for relation-name conflicts between two relnamespace lists.
 * Raise an error if any is found.
 *
 * Note: we assume that each given argument does not contain conflicts
 * itself; we just want to know if the two can be merged together.
 *
 * Per SQL92, two alias-less plain relation RTEs do not conflict even if
 * they have the same eref->aliasname (ie, same relation name), if they
 * are for different relation OIDs (implying they are in different schemas).
 */
void
checkNameSpaceConflicts(ParseState *pstate, List *namespace1,
                                    List *namespace2)
{
      ListCell   *l1;

      foreach(l1, namespace1)
      {
            RangeTblEntry *rte1 = (RangeTblEntry *) lfirst(l1);
            const char *aliasname1 = rte1->eref->aliasname;
            ListCell   *l2;

            foreach(l2, namespace2)
            {
                  RangeTblEntry *rte2 = (RangeTblEntry *) lfirst(l2);

                  if (strcmp(rte2->eref->aliasname, aliasname1) != 0)
                        continue;         /* definitely no conflict */
                  if (rte1->rtekind == RTE_RELATION && rte1->alias == NULL &&
                        rte2->rtekind == RTE_RELATION && rte2->alias == NULL &&
                        rte1->relid != rte2->relid)
                        continue;         /* no conflict per SQL92 rule */
                  ereport(ERROR,
                              (errcode(ERRCODE_DUPLICATE_ALIAS),
                               errmsg("table name \"%s\" specified more than once",
                                          aliasname1)));
            }
      }
}

/*
 * given an RTE, return RT index (starting with 1) of the entry,
 * and optionally get its nesting depth (0 = current).      If sublevels_up
 * is NULL, only consider rels at the current nesting level.
 * Raises error if RTE not found.
 */
int
RTERangeTablePosn(ParseState *pstate, RangeTblEntry *rte, int *sublevels_up)
{
      int               index;
      ListCell   *l;

      if (sublevels_up)
            *sublevels_up = 0;

      while (pstate != NULL)
      {
            index = 1;
            foreach(l, pstate->p_rtable)
            {
                  if (rte == (RangeTblEntry *) lfirst(l))
                        return index;
                  index++;
            }
            pstate = pstate->parentParseState;
            if (sublevels_up)
                  (*sublevels_up)++;
            else
                  break;
      }

      elog(ERROR, "RTE not found (internal error)");
      return 0;                           /* keep compiler quiet */
}

/*
 * Given an RT index and nesting depth, find the corresponding RTE.
 * This is the inverse of RTERangeTablePosn.
 */
RangeTblEntry *
GetRTEByRangeTablePosn(ParseState *pstate,
                                 int varno,
                                 int sublevels_up)
{
      while (sublevels_up-- > 0)
      {
            pstate = pstate->parentParseState;
            Assert(pstate != NULL);
      }
      Assert(varno > 0 && varno <= list_length(pstate->p_rtable));
      return rt_fetch(varno, pstate->p_rtable);
}

/*
 * Fetch the CTE for a CTE-reference RTE.
 *
 * rtelevelsup is the number of query levels above the given pstate that the
 * RTE came from.  Callers that don't have this information readily available
 * may pass -1 instead.
 */
CommonTableExpr *
GetCTEForRTE(ParseState *pstate, RangeTblEntry *rte, int rtelevelsup)
{
      Index       levelsup;
      ListCell   *lc;

      /* Determine RTE's levelsup if caller didn't know it */
      if (rtelevelsup < 0)
            (void) RTERangeTablePosn(pstate, rte, &rtelevelsup);

      Assert(rte->rtekind == RTE_CTE);
      levelsup = rte->ctelevelsup + rtelevelsup;
      while (levelsup-- > 0)
      {
            pstate = pstate->parentParseState;
            if (!pstate)                  /* shouldn't happen */
                  elog(ERROR, "bad levelsup for CTE \"%s\"", rte->ctename);
      }
      foreach(lc, pstate->p_ctenamespace)
      {
            CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);

            if (strcmp(cte->ctename, rte->ctename) == 0)
                  return cte;
      }
      /* shouldn't happen */
      elog(ERROR, "could not find CTE \"%s\"", rte->ctename);
      return NULL;                        /* keep compiler quiet */
}

/*
 * scanRTEForColumn
 *      Search the column names of a single RTE for the given name.
 *      If found, return an appropriate Var node, else return NULL.
 *      If the name proves ambiguous within this RTE, raise error.
 *
 * Side effect: if we find a match, mark the RTE as requiring read access
 * for the column.
 */
Node *
scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname,
                         int location)
{
      Node     *result = NULL;
      int               attnum = 0;
      Var            *var;
      ListCell   *c;

      /*
       * Scan the user column names (or aliases) for a match. Complain if
       * multiple matches.
       *
       * Note: eref->colnames may include entries for dropped columns, but those
       * will be empty strings that cannot match any legal SQL identifier, so we
       * don't bother to test for that case here.
       *
       * Should this somehow go wrong and we try to access a dropped column,
       * we'll still catch it by virtue of the checks in
       * get_rte_attribute_type(), which is called by make_var().  That routine
       * has to do a cache lookup anyway, so the check there is cheap.
       */
      foreach(c, rte->eref->colnames)
      {
            attnum++;
            if (strcmp(strVal(lfirst(c)), colname) == 0)
            {
                  if (result)
                        ereport(ERROR,
                                    (errcode(ERRCODE_AMBIGUOUS_COLUMN),
                                     errmsg("column reference \"%s\" is ambiguous",
                                                colname),
                                     parser_errposition(pstate, location)));
                  var = make_var(pstate, rte, attnum, location);
                  /* Require read access to the column */
                  markVarForSelectPriv(pstate, var, rte);
                  result = (Node *) var;
            }
      }

      /*
       * If we have a unique match, return it.  Note that this allows a user
       * alias to override a system column name (such as OID) without error.
       */
      if (result)
            return result;

      /*
       * If the RTE represents a real table, consider system column names.
       */
      if (rte->rtekind == RTE_RELATION)
      {
            /* quick check to see if name could be a system column */
            attnum = specialAttNum(colname);
            if (attnum != InvalidAttrNumber)
            {
                  /* now check to see if column actually is defined */
                  if (SearchSysCacheExists(ATTNUM,
                                                       ObjectIdGetDatum(rte->relid),
                                                       Int16GetDatum(attnum),
                                                       0, 0))
                  {
                        var = make_var(pstate, rte, attnum, location);
                        /* Require read access to the column */
                        markVarForSelectPriv(pstate, var, rte);
                        result = (Node *) var;
                  }
            }
      }

      return result;
}

/*
 * colNameToVar
 *      Search for an unqualified column name.
 *      If found, return the appropriate Var node (or expression).
 *      If not found, return NULL.  If the name proves ambiguous, raise error.
 *      If localonly is true, only names in the innermost query are considered.
 */
Node *
colNameToVar(ParseState *pstate, char *colname, bool localonly,
                   int location)
{
      Node     *result = NULL;
      ParseState *orig_pstate = pstate;

      while (pstate != NULL)
      {
            ListCell   *l;

            foreach(l, pstate->p_varnamespace)
            {
                  RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
                  Node     *newresult;

                  /* use orig_pstate here to get the right sublevels_up */
                  newresult = scanRTEForColumn(orig_pstate, rte, colname, location);

                  if (newresult)
                  {
                        if (result)
                              ereport(ERROR,
                                          (errcode(ERRCODE_AMBIGUOUS_COLUMN),
                                           errmsg("column reference \"%s\" is ambiguous",
                                                      colname),
                                           parser_errposition(orig_pstate, location)));
                        result = newresult;
                  }
            }

            if (result != NULL || localonly)
                  break;                        /* found, or don't want to look at parent */

            pstate = pstate->parentParseState;
      }

      return result;
}

/*
 * qualifiedNameToVar
 *      Search for a qualified column name: either refname.colname or
 *      schemaname.relname.colname.
 *
 *      If found, return the appropriate Var node.
 *      If not found, return NULL.  If the name proves ambiguous, raise error.
 */
Node *
qualifiedNameToVar(ParseState *pstate,
                           char *schemaname,
                           char *refname,
                           char *colname,
                           bool implicitRTEOK,
                           int location)
{
      RangeTblEntry *rte;
      int               sublevels_up;

      rte = refnameRangeTblEntry(pstate, schemaname, refname, location,
                                             &sublevels_up);

      if (rte == NULL)
      {
            if (!implicitRTEOK)
                  return NULL;
            rte = addImplicitRTE(pstate,
                                           makeRangeVar(schemaname, refname, location));
      }

      return scanRTEForColumn(pstate, rte, colname, location);
}

/*
 * markRTEForSelectPriv
 *       Mark the specified column of an RTE as requiring SELECT privilege
 *
 * col == InvalidAttrNumber means a "whole row" reference
 *
 * The caller should pass the actual RTE if it has it handy; otherwise pass
 * NULL, and we'll look it up here.  (This uglification of the API is
 * worthwhile because nearly all external callers have the RTE at hand.)
 */
static void
markRTEForSelectPriv(ParseState *pstate, RangeTblEntry *rte,
                               int rtindex, AttrNumber col)
{
      if (rte == NULL)
            rte = rt_fetch(rtindex, pstate->p_rtable);

      if (rte->rtekind == RTE_RELATION)
      {
            /* Make sure the rel as a whole is marked for SELECT access */
            rte->requiredPerms |= ACL_SELECT;
            /* Must offset the attnum to fit in a bitmapset */
            rte->selectedCols = bms_add_member(rte->selectedCols,
                                                   col - FirstLowInvalidHeapAttributeNumber);
      }
      else if (rte->rtekind == RTE_JOIN)
      {
            if (col == InvalidAttrNumber)
            {
                  /*
                   * A whole-row reference to a join has to be treated as whole-row
                   * references to the two inputs.
                   */
                  JoinExpr   *j;

                  if (rtindex > 0 && rtindex <= list_length(pstate->p_joinexprs))
                        j = (JoinExpr *) list_nth(pstate->p_joinexprs, rtindex - 1);
                  else
                        j = NULL;
                  if (j == NULL)
                        elog(ERROR, "could not find JoinExpr for whole-row reference");
                  Assert(IsA(j, JoinExpr));

                  /* Note: we can't see FromExpr here */
                  if (IsA(j->larg, RangeTblRef))
                  {
                        int               varno = ((RangeTblRef *) j->larg)->rtindex;

                        markRTEForSelectPriv(pstate, NULL, varno, InvalidAttrNumber);
                  }
                  else if (IsA(j->larg, JoinExpr))
                  {
                        int               varno = ((JoinExpr *) j->larg)->rtindex;

                        markRTEForSelectPriv(pstate, NULL, varno, InvalidAttrNumber);
                  }
                  else
                        elog(ERROR, "unrecognized node type: %d",
                               (int) nodeTag(j->larg));
                  if (IsA(j->rarg, RangeTblRef))
                  {
                        int               varno = ((RangeTblRef *) j->rarg)->rtindex;

                        markRTEForSelectPriv(pstate, NULL, varno, InvalidAttrNumber);
                  }
                  else if (IsA(j->rarg, JoinExpr))
                  {
                        int               varno = ((JoinExpr *) j->rarg)->rtindex;

                        markRTEForSelectPriv(pstate, NULL, varno, InvalidAttrNumber);
                  }
                  else
                        elog(ERROR, "unrecognized node type: %d",
                               (int) nodeTag(j->rarg));
            }
            else
            {
                  /*
                   * Regular join attribute, look at the alias-variable list.
                   *
                   * The aliasvar could be either a Var or a COALESCE expression,
                   * but in the latter case we should already have marked the two
                   * referent variables as being selected, due to their use in the
                   * JOIN clause.  So we need only be concerned with the simple Var
                   * case.
                   */
                  Var            *aliasvar;

                  Assert(col > 0 && col <= list_length(rte->joinaliasvars));
                  aliasvar = (Var *) list_nth(rte->joinaliasvars, col - 1);
                  if (IsA(aliasvar, Var))
                        markVarForSelectPriv(pstate, aliasvar, NULL);
            }
      }
      /* other RTE types don't require privilege marking */
}

/*
 * markVarForSelectPriv
 *       Mark the RTE referenced by a Var as requiring SELECT privilege
 *
 * The caller should pass the Var's referenced RTE if it has it handy
 * (nearly all do); otherwise pass NULL.
 */
void
markVarForSelectPriv(ParseState *pstate, Var *var, RangeTblEntry *rte)
{
      Index       lv;

      Assert(IsA(var, Var));
      /* Find the appropriate pstate if it's an uplevel Var */
      for (lv = 0; lv < var->varlevelsup; lv++)
            pstate = pstate->parentParseState;
      markRTEForSelectPriv(pstate, rte, var->varno, var->varattno);
}

/*
 * buildRelationAliases
 *          Construct the eref column name list for a relation RTE.
 *          This code is also used for the case of a function RTE returning
 *          a named composite type.
 *
 * tupdesc: the physical column information
 * alias: the user-supplied alias, or NULL if none
 * eref: the eref Alias to store column names in
 *
 * eref->colnames is filled in.  Also, alias->colnames is rebuilt to insert
 * empty strings for any dropped columns, so that it will be one-to-one with
 * physical column numbers.
 */
static void
buildRelationAliases(TupleDesc tupdesc, Alias *alias, Alias *eref)
{
      int               maxattrs = tupdesc->natts;
      ListCell   *aliaslc;
      int               numaliases;
      int               varattno;
      int               numdropped = 0;

      Assert(eref->colnames == NIL);

      if (alias)
      {
            aliaslc = list_head(alias->colnames);
            numaliases = list_length(alias->colnames);
            /* We'll rebuild the alias colname list */
            alias->colnames = NIL;
      }
      else
      {
            aliaslc = NULL;
            numaliases = 0;
      }

      for (varattno = 0; varattno < maxattrs; varattno++)
      {
            Form_pg_attribute attr = tupdesc->attrs[varattno];
            Value    *attrname;

            if (attr->attisdropped)
            {
                  /* Always insert an empty string for a dropped column */
                  attrname = makeString(pstrdup(""));
                  if (aliaslc)
                        alias->colnames = lappend(alias->colnames, attrname);
                  numdropped++;
            }
            else if (aliaslc)
            {
                  /* Use the next user-supplied alias */
                  attrname = (Value *) lfirst(aliaslc);
                  aliaslc = lnext(aliaslc);
                  alias->colnames = lappend(alias->colnames, attrname);
            }
            else
            {
                  attrname = makeString(pstrdup(NameStr(attr->attname)));
                  /* we're done with the alias if any */
            }

            eref->colnames = lappend(eref->colnames, attrname);
      }

      /* Too many user-supplied aliases? */
      if (aliaslc)
            ereport(ERROR,
                        (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
                         errmsg("table \"%s\" has %d columns available but %d columns specified",
                                    eref->aliasname, maxattrs - numdropped, numaliases)));
}

/*
 * buildScalarFunctionAlias
 *          Construct the eref column name list for a function RTE,
 *          when the function returns a scalar type (not composite or RECORD).
 *
 * funcexpr: transformed expression tree for the function call
 * funcname: function name (used only for error message)
 * alias: the user-supplied alias, or NULL if none
 * eref: the eref Alias to store column names in
 *
 * eref->colnames is filled in.
 */
static void
buildScalarFunctionAlias(Node *funcexpr, char *funcname,
                                     Alias *alias, Alias *eref)
{
      char     *pname;

      Assert(eref->colnames == NIL);

      /* Use user-specified column alias if there is one. */
      if (alias && alias->colnames != NIL)
      {
            if (list_length(alias->colnames) != 1)
                  ereport(ERROR,
                              (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
                          errmsg("too many column aliases specified for function %s",
                                     funcname)));
            eref->colnames = copyObject(alias->colnames);
            return;
      }

      /*
       * If the expression is a simple function call, and the function has a
       * single OUT parameter that is named, use the parameter's name.
       */
      if (funcexpr && IsA(funcexpr, FuncExpr))
      {
            pname = get_func_result_name(((FuncExpr *) funcexpr)->funcid);
            if (pname)
            {
                  eref->colnames = list_make1(makeString(pname));
                  return;
            }
      }

      /*
       * Otherwise use the previously-determined alias (not necessarily the
       * function name!)
       */
      eref->colnames = list_make1(makeString(eref->aliasname));
}

/*
 * Open a table during parse analysis
 *
 * This is essentially just the same as heap_openrv(), except that it caters
 * to some parser-specific error reporting needs, notably that it arranges
 * to include the RangeVar's parse location in any resulting error.
 *
 * Note: properly, lockmode should be declared LOCKMODE not int, but that
 * would require importing storage/lock.h into parse_relation.h.  Since
 * LOCKMODE is typedef'd as int anyway, that seems like overkill.
 */
Relation
parserOpenTable(ParseState *pstate, const RangeVar *relation, int lockmode)
{
      Relation    rel;
      ParseCallbackState pcbstate;

      setup_parser_errposition_callback(&pcbstate, pstate, relation->location);
      rel = try_heap_openrv(relation, lockmode);
      if (rel == NULL)
      {
            if (relation->schemaname)
                  ereport(ERROR,
                              (errcode(ERRCODE_UNDEFINED_TABLE),
                               errmsg("relation \"%s.%s\" does not exist",
                                          relation->schemaname, relation->relname)));
            else
            {
                  /*
                   * An unqualified name might have been meant as a reference to
                   * some not-yet-in-scope CTE.  The bare "does not exist" message
                   * has proven remarkably unhelpful for figuring out such problems,
                   * so we take pains to offer a specific hint.
                   */
                  if (isFutureCTE(pstate, relation->relname))
                        ereport(ERROR,
                                    (errcode(ERRCODE_UNDEFINED_TABLE),
                                     errmsg("relation \"%s\" does not exist",
                                                relation->relname),
                                     errdetail("There is a WITH item named \"%s\", but it cannot be referenced from this part of the query.",
                                                   relation->relname),
                                     errhint("Use WITH RECURSIVE, or re-order the WITH items to remove forward references.")));
                  else
                        ereport(ERROR,
                                    (errcode(ERRCODE_UNDEFINED_TABLE),
                                     errmsg("relation \"%s\" does not exist",
                                                relation->relname)));
            }
      }
      cancel_parser_errposition_callback(&pcbstate);
      return rel;
}

/*
 * Add an entry for a relation to the pstate's range table (p_rtable).
 *
 * If pstate is NULL, we just build an RTE and return it without adding it
 * to an rtable list.
 *
 * Note: formerly this checked for refname conflicts, but that's wrong.
 * Caller is responsible for checking for conflicts in the appropriate scope.
 */
RangeTblEntry *
addRangeTableEntry(ParseState *pstate,
                           RangeVar *relation,
                           Alias *alias,
                           bool inh,
                           bool inFromCl)
{
      RangeTblEntry *rte = makeNode(RangeTblEntry);
      char     *refname = alias ? alias->aliasname : relation->relname;
      LOCKMODE    lockmode;
      Relation    rel;

      rte->rtekind = RTE_RELATION;
      rte->alias = alias;

      /*
       * Get the rel's OID.  This access also ensures that we have an up-to-date
       * relcache entry for the rel.      Since this is typically the first access
       * to a rel in a statement, be careful to get the right access level
       * depending on whether we're doing SELECT FOR UPDATE/SHARE.
       */
      lockmode = isLockedRel(pstate, refname) ? RowShareLock : AccessShareLock;
      rel = parserOpenTable(pstate, relation, lockmode);
      rte->relid = RelationGetRelid(rel);

      /*
       * Build the list of effective column names using user-supplied aliases
       * and/or actual column names.
       */
      rte->eref = makeAlias(refname, NIL);
      buildRelationAliases(rel->rd_att, alias, rte->eref);

      /*
       * Drop the rel refcount, but keep the access lock till end of transaction
       * so that the table can't be deleted or have its schema modified
       * underneath us.
       */
      heap_close(rel, NoLock);

      /*----------
       * Flags:
       * - this RTE should be expanded to include descendant tables,
       * - this RTE is in the FROM clause,
       * - this RTE should be checked for appropriate access rights.
       *
       * The initial default on access checks is always check-for-READ-access,
       * which is the right thing for all except target tables.
       *----------
       */
      rte->inh = inh;
      rte->inFromCl = inFromCl;

      rte->requiredPerms = ACL_SELECT;
      rte->checkAsUser = InvalidOid;            /* not set-uid by default, either */
      rte->selectedCols = NULL;
      rte->modifiedCols = NULL;

      /*
       * Add completed RTE to pstate's range table list, but not to join list
       * nor namespace --- caller must do that if appropriate.
       */
      if (pstate != NULL)
            pstate->p_rtable = lappend(pstate->p_rtable, rte);

      return rte;
}

/*
 * Add an entry for a relation to the pstate's range table (p_rtable).
 *
 * This is just like addRangeTableEntry() except that it makes an RTE
 * given an already-open relation instead of a RangeVar reference.
 */
RangeTblEntry *
addRangeTableEntryForRelation(ParseState *pstate,
                                            Relation rel,
                                            Alias *alias,
                                            bool inh,
                                            bool inFromCl)
{
      RangeTblEntry *rte = makeNode(RangeTblEntry);
      char     *refname = alias ? alias->aliasname : RelationGetRelationName(rel);

      rte->rtekind = RTE_RELATION;
      rte->alias = alias;
      rte->relid = RelationGetRelid(rel);

      /*
       * Build the list of effective column names using user-supplied aliases
       * and/or actual column names.
       */
      rte->eref = makeAlias(refname, NIL);
      buildRelationAliases(rel->rd_att, alias, rte->eref);

      /*----------
       * Flags:
       * - this RTE should be expanded to include descendant tables,
       * - this RTE is in the FROM clause,
       * - this RTE should be checked for appropriate access rights.
       *
       * The initial default on access checks is always check-for-READ-access,
       * which is the right thing for all except target tables.
       *----------
       */
      rte->inh = inh;
      rte->inFromCl = inFromCl;

      rte->requiredPerms = ACL_SELECT;
      rte->checkAsUser = InvalidOid;            /* not set-uid by default, either */
      rte->selectedCols = NULL;
      rte->modifiedCols = NULL;

      /*
       * Add completed RTE to pstate's range table list, but not to join list
       * nor namespace --- caller must do that if appropriate.
       */
      if (pstate != NULL)
            pstate->p_rtable = lappend(pstate->p_rtable, rte);

      return rte;
}

/*
 * Add an entry for a subquery to the pstate's range table (p_rtable).
 *
 * This is just like addRangeTableEntry() except that it makes a subquery RTE.
 * Note that an alias clause *must* be supplied.
 */
RangeTblEntry *
addRangeTableEntryForSubquery(ParseState *pstate,
                                            Query *subquery,
                                            Alias *alias,
                                            bool inFromCl)
{
      RangeTblEntry *rte = makeNode(RangeTblEntry);
      char     *refname = alias->aliasname;
      Alias    *eref;
      int               numaliases;
      int               varattno;
      ListCell   *tlistitem;

      rte->rtekind = RTE_SUBQUERY;
      rte->relid = InvalidOid;
      rte->subquery = subquery;
      rte->alias = alias;

      eref = copyObject(alias);
      numaliases = list_length(eref->colnames);

      /* fill in any unspecified alias columns */
      varattno = 0;
      foreach(tlistitem, subquery->targetList)
      {
            TargetEntry *te = (TargetEntry *) lfirst(tlistitem);

            if (te->resjunk)
                  continue;
            varattno++;
            Assert(varattno == te->resno);
            if (varattno > numaliases)
            {
                  char     *attrname;

                  attrname = pstrdup(te->resname);
                  eref->colnames = lappend(eref->colnames, makeString(attrname));
            }
      }
      if (varattno < numaliases)
            ereport(ERROR,
                        (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
                         errmsg("table \"%s\" has %d columns available but %d columns specified",
                                    refname, varattno, numaliases)));

      rte->eref = eref;

      /*----------
       * Flags:
       * - this RTE should be expanded to include descendant tables,
       * - this RTE is in the FROM clause,
       * - this RTE should be checked for appropriate access rights.
       *
       * Subqueries are never checked for access rights.
       *----------
       */
      rte->inh = false;             /* never true for subqueries */
      rte->inFromCl = inFromCl;

      rte->requiredPerms = 0;
      rte->checkAsUser = InvalidOid;
      rte->selectedCols = NULL;
      rte->modifiedCols = NULL;

      /*
       * Add completed RTE to pstate's range table list, but not to join list
       * nor namespace --- caller must do that if appropriate.
       */
      if (pstate != NULL)
            pstate->p_rtable = lappend(pstate->p_rtable, rte);

      return rte;
}

/*
 * Add an entry for a function to the pstate's range table (p_rtable).
 *
 * This is just like addRangeTableEntry() except that it makes a function RTE.
 */
RangeTblEntry *
addRangeTableEntryForFunction(ParseState *pstate,
                                            char *funcname,
                                            Node *funcexpr,
                                            RangeFunction *rangefunc,
                                            bool inFromCl)
{
      RangeTblEntry *rte = makeNode(RangeTblEntry);
      TypeFuncClass functypclass;
      Oid               funcrettype;
      TupleDesc   tupdesc;
      Alias    *alias = rangefunc->alias;
      List     *coldeflist = rangefunc->coldeflist;
      Alias    *eref;

      rte->rtekind = RTE_FUNCTION;
      rte->relid = InvalidOid;
      rte->subquery = NULL;
      rte->funcexpr = funcexpr;
      rte->funccoltypes = NIL;
      rte->funccoltypmods = NIL;
      rte->alias = alias;

      eref = makeAlias(alias ? alias->aliasname : funcname, NIL);
      rte->eref = eref;

      /*
       * Now determine if the function returns a simple or composite type.
       */
      functypclass = get_expr_result_type(funcexpr,
                                                            &funcrettype,
                                                            &tupdesc);

      /*
       * A coldeflist is required if the function returns RECORD and hasn't got
       * a predetermined record type, and is prohibited otherwise.
       */
      if (coldeflist != NIL)
      {
            if (functypclass != TYPEFUNC_RECORD)
                  ereport(ERROR,
                              (errcode(ERRCODE_SYNTAX_ERROR),
                               errmsg("a column definition list is only allowed for functions returning \"record\""),
                               parser_errposition(pstate, exprLocation(funcexpr))));
      }
      else
      {
            if (functypclass == TYPEFUNC_RECORD)
                  ereport(ERROR,
                              (errcode(ERRCODE_SYNTAX_ERROR),
                               errmsg("a column definition list is required for functions returning \"record\""),
                               parser_errposition(pstate, exprLocation(funcexpr))));
      }

      if (functypclass == TYPEFUNC_COMPOSITE)
      {
            /* Composite data type, e.g. a table's row type */
            Assert(tupdesc);
            /* Build the column alias list */
            buildRelationAliases(tupdesc, alias, eref);
      }
      else if (functypclass == TYPEFUNC_SCALAR)
      {
            /* Base data type, i.e. scalar */
            buildScalarFunctionAlias(funcexpr, funcname, alias, eref);
      }
      else if (functypclass == TYPEFUNC_RECORD)
      {
            ListCell   *col;

            /*
             * Use the column definition list to form the alias list and
             * funccoltypes/funccoltypmods lists.
             */
            foreach(col, coldeflist)
            {
                  ColumnDef  *n = (ColumnDef *) lfirst(col);
                  char     *attrname;
                  Oid               attrtype;
                  int32       attrtypmod;

                  attrname = pstrdup(n->colname);
                  if (n->typename->setof)
                        ereport(ERROR,
                                    (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                                     errmsg("column \"%s\" cannot be declared SETOF",
                                                attrname),
                                     parser_errposition(pstate, n->typename->location)));
                  attrtype = typenameTypeId(pstate, n->typename, &attrtypmod);
                  eref->colnames = lappend(eref->colnames, makeString(attrname));
                  rte->funccoltypes = lappend_oid(rte->funccoltypes, attrtype);
                  rte->funccoltypmods = lappend_int(rte->funccoltypmods, attrtypmod);
            }
      }
      else
            ereport(ERROR,
                        (errcode(ERRCODE_DATATYPE_MISMATCH),
                   errmsg("function \"%s\" in FROM has unsupported return type %s",
                              funcname, format_type_be(funcrettype)),
                         parser_errposition(pstate, exprLocation(funcexpr))));

      /*----------
       * Flags:
       * - this RTE should be expanded to include descendant tables,
       * - this RTE is in the FROM clause,
       * - this RTE should be checked for appropriate access rights.
       *
       * Functions are never checked for access rights (at least, not by
       * the RTE permissions mechanism).
       *----------
       */
      rte->inh = false;             /* never true for functions */
      rte->inFromCl = inFromCl;

      rte->requiredPerms = 0;
      rte->checkAsUser = InvalidOid;
      rte->selectedCols = NULL;
      rte->modifiedCols = NULL;

      /*
       * Add completed RTE to pstate's range table list, but not to join list
       * nor namespace --- caller must do that if appropriate.
       */
      if (pstate != NULL)
            pstate->p_rtable = lappend(pstate->p_rtable, rte);

      return rte;
}

/*
 * Add an entry for a VALUES list to the pstate's range table (p_rtable).
 *
 * This is much like addRangeTableEntry() except that it makes a values RTE.
 */
RangeTblEntry *
addRangeTableEntryForValues(ParseState *pstate,
                                          List *exprs,
                                          Alias *alias,
                                          bool inFromCl)
{
      RangeTblEntry *rte = makeNode(RangeTblEntry);
      char     *refname = alias ? alias->aliasname : pstrdup("*VALUES*");
      Alias    *eref;
      int               numaliases;
      int               numcolumns;

      rte->rtekind = RTE_VALUES;
      rte->relid = InvalidOid;
      rte->subquery = NULL;
      rte->values_lists = exprs;
      rte->alias = alias;

      eref = alias ? copyObject(alias) : makeAlias(refname, NIL);

      /* fill in any unspecified alias columns */
      numcolumns = list_length((List *) linitial(exprs));
      numaliases = list_length(eref->colnames);
      while (numaliases < numcolumns)
      {
            char        attrname[64];

            numaliases++;
            snprintf(attrname, sizeof(attrname), "column%d", numaliases);
            eref->colnames = lappend(eref->colnames,
                                                 makeString(pstrdup(attrname)));
      }
      if (numcolumns < numaliases)
            ereport(ERROR,
                        (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
                         errmsg("VALUES lists \"%s\" have %d columns available but %d columns specified",
                                    refname, numcolumns, numaliases)));

      rte->eref = eref;

      /*----------
       * Flags:
       * - this RTE should be expanded to include descendant tables,
       * - this RTE is in the FROM clause,
       * - this RTE should be checked for appropriate access rights.
       *
       * Subqueries are never checked for access rights.
       *----------
       */
      rte->inh = false;             /* never true for values RTEs */
      rte->inFromCl = inFromCl;

      rte->requiredPerms = 0;
      rte->checkAsUser = InvalidOid;
      rte->selectedCols = NULL;
      rte->modifiedCols = NULL;

      /*
       * Add completed RTE to pstate's range table list, but not to join list
       * nor namespace --- caller must do that if appropriate.
       */
      if (pstate != NULL)
            pstate->p_rtable = lappend(pstate->p_rtable, rte);

      return rte;
}

/*
 * Add an entry for a join to the pstate's range table (p_rtable).
 *
 * This is much like addRangeTableEntry() except that it makes a join RTE.
 */
RangeTblEntry *
addRangeTableEntryForJoin(ParseState *pstate,
                                      List *colnames,
                                      JoinType jointype,
                                      List *aliasvars,
                                      Alias *alias,
                                      bool inFromCl)
{
      RangeTblEntry *rte = makeNode(RangeTblEntry);
      Alias    *eref;
      int               numaliases;

      /*
       * Fail if join has too many columns --- we must be able to reference any
       * of the columns with an AttrNumber.
       */
      if (list_length(aliasvars) > MaxAttrNumber)
            ereport(ERROR,
                        (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
                         errmsg("joins can have at most %d columns",
                                    MaxAttrNumber)));

      rte->rtekind = RTE_JOIN;
      rte->relid = InvalidOid;
      rte->subquery = NULL;
      rte->jointype = jointype;
      rte->joinaliasvars = aliasvars;
      rte->alias = alias;

      eref = alias ? (Alias *) copyObject(alias) : makeAlias("unnamed_join", NIL);
      numaliases = list_length(eref->colnames);

      /* fill in any unspecified alias columns */
      if (numaliases < list_length(colnames))
            eref->colnames = list_concat(eref->colnames,
                                                       list_copy_tail(colnames, numaliases));

      rte->eref = eref;

      /*----------
       * Flags:
       * - this RTE should be expanded to include descendant tables,
       * - this RTE is in the FROM clause,
       * - this RTE should be checked for appropriate access rights.
       *
       * Joins are never checked for access rights.
       *----------
       */
      rte->inh = false;             /* never true for joins */
      rte->inFromCl = inFromCl;

      rte->requiredPerms = 0;
      rte->checkAsUser = InvalidOid;
      rte->selectedCols = NULL;
      rte->modifiedCols = NULL;

      /*
       * Add completed RTE to pstate's range table list, but not to join list
       * nor namespace --- caller must do that if appropriate.
       */
      if (pstate != NULL)
            pstate->p_rtable = lappend(pstate->p_rtable, rte);

      return rte;
}

/*
 * Add an entry for a CTE reference to the pstate's range table (p_rtable).
 *
 * This is much like addRangeTableEntry() except that it makes a CTE RTE.
 */
RangeTblEntry *
addRangeTableEntryForCTE(ParseState *pstate,
                                     CommonTableExpr *cte,
                                     Index levelsup,
                                     Alias *alias,
                                     bool inFromCl)
{
      RangeTblEntry *rte = makeNode(RangeTblEntry);
      char     *refname = alias ? alias->aliasname : cte->ctename;
      Alias    *eref;
      int               numaliases;
      int               varattno;
      ListCell   *lc;

      rte->rtekind = RTE_CTE;
      rte->ctename = cte->ctename;
      rte->ctelevelsup = levelsup;

      /* Self-reference if and only if CTE's parse analysis isn't completed */
      rte->self_reference = !IsA(cte->ctequery, Query);
      Assert(cte->cterecursive || !rte->self_reference);
      /* Bump the CTE's refcount if this isn't a self-reference */
      if (!rte->self_reference)
            cte->cterefcount++;

      rte->ctecoltypes = cte->ctecoltypes;
      rte->ctecoltypmods = cte->ctecoltypmods;

      rte->alias = alias;
      if (alias)
            eref = copyObject(alias);
      else
            eref = makeAlias(refname, NIL);
      numaliases = list_length(eref->colnames);

      /* fill in any unspecified alias columns */
      varattno = 0;
      foreach(lc, cte->ctecolnames)
      {
            varattno++;
            if (varattno > numaliases)
                  eref->colnames = lappend(eref->colnames, lfirst(lc));
      }
      if (varattno < numaliases)
            ereport(ERROR,
                        (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
                         errmsg("table \"%s\" has %d columns available but %d columns specified",
                                    refname, varattno, numaliases)));

      rte->eref = eref;

      /*----------
       * Flags:
       * - this RTE should be expanded to include descendant tables,
       * - this RTE is in the FROM clause,
       * - this RTE should be checked for appropriate access rights.
       *
       * Subqueries are never checked for access rights.
       *----------
       */
      rte->inh = false;             /* never true for subqueries */
      rte->inFromCl = inFromCl;

      rte->requiredPerms = 0;
      rte->checkAsUser = InvalidOid;
      rte->selectedCols = NULL;
      rte->modifiedCols = NULL;

      /*
       * Add completed RTE to pstate's range table list, but not to join list
       * nor namespace --- caller must do that if appropriate.
       */
      if (pstate != NULL)
            pstate->p_rtable = lappend(pstate->p_rtable, rte);

      return rte;
}


/*
 * Has the specified refname been selected FOR UPDATE/FOR SHARE?
 *
 * Note: we pay no attention to whether it's FOR UPDATE vs FOR SHARE.
 */
static bool
isLockedRel(ParseState *pstate, char *refname)
{
      /* Outer loop to check parent query levels as well as this one */
      while (pstate != NULL)
      {
            ListCell   *l;

            foreach(l, pstate->p_locking_clause)
            {
                  LockingClause *lc = (LockingClause *) lfirst(l);

                  if (lc->lockedRels == NIL)
                  {
                        /* all tables used in query */
                        return true;
                  }
                  else
                  {
                        /* just the named tables */
                        ListCell   *l2;

                        foreach(l2, lc->lockedRels)
                        {
                              RangeVar   *thisrel = (RangeVar *) lfirst(l2);

                              if (strcmp(refname, thisrel->relname) == 0)
                                    return true;
                        }
                  }
            }
            pstate = pstate->parentParseState;
      }
      return false;
}

/*
 * Add the given RTE as a top-level entry in the pstate's join list
 * and/or name space lists.  (We assume caller has checked for any
 * namespace conflicts.)
 */
void
addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte,
                    bool addToJoinList,
                    bool addToRelNameSpace, bool addToVarNameSpace)
{
      if (addToJoinList)
      {
            int               rtindex = RTERangeTablePosn(pstate, rte, NULL);
            RangeTblRef *rtr = makeNode(RangeTblRef);

            rtr->rtindex = rtindex;
            pstate->p_joinlist = lappend(pstate->p_joinlist, rtr);
      }
      if (addToRelNameSpace)
            pstate->p_relnamespace = lappend(pstate->p_relnamespace, rte);
      if (addToVarNameSpace)
            pstate->p_varnamespace = lappend(pstate->p_varnamespace, rte);
}

/*
 * Add a POSTQUEL-style implicit RTE.
 *
 * We assume caller has already checked that there is no RTE or join with
 * a conflicting name.
 */
RangeTblEntry *
addImplicitRTE(ParseState *pstate, RangeVar *relation)
{
      CommonTableExpr *cte = NULL;
      Index       levelsup = 0;
      RangeTblEntry *rte;

      /* issue warning or error as needed */
      warnAutoRange(pstate, relation);

      /* if it is an unqualified name, it might be a CTE reference */
      if (!relation->schemaname)
            cte = scanNameSpaceForCTE(pstate, relation->relname, &levelsup);

      /*
       * Note that we set inFromCl true, so that the RTE will be listed
       * explicitly if the parsetree is ever decompiled by ruleutils.c. This
       * provides a migration path for views/rules that were originally written
       * with implicit-RTE syntax.
       */
      if (cte)
            rte = addRangeTableEntryForCTE(pstate, cte, levelsup, NULL, true);
      else
            rte = addRangeTableEntry(pstate, relation, NULL, false, true);
      /* Add to joinlist and relnamespace, but not varnamespace */
      addRTEtoQuery(pstate, rte, true, true, false);

      return rte;
}

/*
 * expandRTE -- expand the columns of a rangetable entry
 *
 * This creates lists of an RTE's column names (aliases if provided, else
 * real names) and Vars for each column.  Only user columns are considered.
 * If include_dropped is FALSE then dropped columns are omitted from the
 * results.  If include_dropped is TRUE then empty strings and NULL constants
 * (not Vars!) are returned for dropped columns.
 *
 * rtindex, sublevels_up, and location are the varno, varlevelsup, and location
 * values to use in the created Vars.  Ordinarily rtindex should match the
 * actual position of the RTE in its rangetable.
 *
 * The output lists go into *colnames and *colvars.
 * If only one of the two kinds of output list is needed, pass NULL for the
 * output pointer for the unwanted one.
 */
void
expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
              int location, bool include_dropped,
              List **colnames, List **colvars)
{
      int               varattno;

      if (colnames)
            *colnames = NIL;
      if (colvars)
            *colvars = NIL;

      switch (rte->rtekind)
      {
            case RTE_RELATION:
                  /* Ordinary relation RTE */
                  expandRelation(rte->relid, rte->eref,
                                       rtindex, sublevels_up, location,
                                       include_dropped, colnames, colvars);
                  break;
            case RTE_SUBQUERY:
                  {
                        /* Subquery RTE */
                        ListCell   *aliasp_item = list_head(rte->eref->colnames);
                        ListCell   *tlistitem;

                        varattno = 0;
                        foreach(tlistitem, rte->subquery->targetList)
                        {
                              TargetEntry *te = (TargetEntry *) lfirst(tlistitem);

                              if (te->resjunk)
                                    continue;
                              varattno++;
                              Assert(varattno == te->resno);

                              if (colnames)
                              {
                                    /* Assume there is one alias per target item */
                                    char     *label = strVal(lfirst(aliasp_item));

                                    *colnames = lappend(*colnames, makeString(pstrdup(label)));
                                    aliasp_item = lnext(aliasp_item);
                              }

                              if (colvars)
                              {
                                    Var            *varnode;

                                    varnode = makeVar(rtindex, varattno,
                                                              exprType((Node *) te->expr),
                                                              exprTypmod((Node *) te->expr),
                                                              sublevels_up);
                                    varnode->location = location;

                                    *colvars = lappend(*colvars, varnode);
                              }
                        }
                  }
                  break;
            case RTE_FUNCTION:
                  {
                        /* Function RTE */
                        TypeFuncClass functypclass;
                        Oid               funcrettype;
                        TupleDesc   tupdesc;

                        functypclass = get_expr_result_type(rte->funcexpr,
                                                                              &funcrettype,
                                                                              &tupdesc);
                        if (functypclass == TYPEFUNC_COMPOSITE)
                        {
                              /* Composite data type, e.g. a table's row type */
                              Assert(tupdesc);
                              expandTupleDesc(tupdesc, rte->eref,
                                                      rtindex, sublevels_up, location,
                                                      include_dropped, colnames, colvars);
                        }
                        else if (functypclass == TYPEFUNC_SCALAR)
                        {
                              /* Base data type, i.e. scalar */
                              if (colnames)
                                    *colnames = lappend(*colnames,
                                                                  linitial(rte->eref->colnames));

                              if (colvars)
                              {
                                    Var            *varnode;

                                    varnode = makeVar(rtindex, 1,
                                                              funcrettype, -1,
                                                              sublevels_up);
                                    varnode->location = location;

                                    *colvars = lappend(*colvars, varnode);
                              }
                        }
                        else if (functypclass == TYPEFUNC_RECORD)
                        {
                              if (colnames)
                                    *colnames = copyObject(rte->eref->colnames);
                              if (colvars)
                              {
                                    ListCell   *l1;
                                    ListCell   *l2;
                                    int               attnum = 0;

                                    forboth(l1, rte->funccoltypes, l2, rte->funccoltypmods)
                                    {
                                          Oid               attrtype = lfirst_oid(l1);
                                          int32       attrtypmod = lfirst_int(l2);
                                          Var            *varnode;

                                          attnum++;
                                          varnode = makeVar(rtindex,
                                                                    attnum,
                                                                    attrtype,
                                                                    attrtypmod,
                                                                    sublevels_up);
                                          varnode->location = location;
                                          *colvars = lappend(*colvars, varnode);
                                    }
                              }
                        }
                        else
                        {
                              /* addRangeTableEntryForFunction should've caught this */
                              elog(ERROR, "function in FROM has unsupported return type");
                        }
                  }
                  break;
            case RTE_VALUES:
                  {
                        /* Values RTE */
                        ListCell   *aliasp_item = list_head(rte->eref->colnames);
                        ListCell   *lc;

                        varattno = 0;
                        foreach(lc, (List *) linitial(rte->values_lists))
                        {
                              Node     *col = (Node *) lfirst(lc);

                              varattno++;
                              if (colnames)
                              {
                                    /* Assume there is one alias per column */
                                    char     *label = strVal(lfirst(aliasp_item));

                                    *colnames = lappend(*colnames,
                                                                  makeString(pstrdup(label)));
                                    aliasp_item = lnext(aliasp_item);
                              }

                              if (colvars)
                              {
                                    Var            *varnode;

                                    varnode = makeVar(rtindex, varattno,
                                                              exprType(col),
                                                              exprTypmod(col),
                                                              sublevels_up);
                                    varnode->location = location;
                                    *colvars = lappend(*colvars, varnode);
                              }
                        }
                  }
                  break;
            case RTE_JOIN:
                  {
                        /* Join RTE */
                        ListCell   *colname;
                        ListCell   *aliasvar;

                        Assert(list_length(rte->eref->colnames) == list_length(rte->joinaliasvars));

                        varattno = 0;
                        forboth(colname, rte->eref->colnames, aliasvar, rte->joinaliasvars)
                        {
                              Node     *avar = (Node *) lfirst(aliasvar);

                              varattno++;

                              /*
                               * During ordinary parsing, there will never be any
                               * deleted columns in the join; but we have to check since
                               * this routine is also used by the rewriter, and joins
                               * found in stored rules might have join columns for
                               * since-deleted columns.  This will be signaled by a NULL
                               * Const in the alias-vars list.
                               */
                              if (IsA(avar, Const))
                              {
                                    if (include_dropped)
                                    {
                                          if (colnames)
                                                *colnames = lappend(*colnames,
                                                                              makeString(pstrdup("")));
                                          if (colvars)
                                                *colvars = lappend(*colvars,
                                                                           copyObject(avar));
                                    }
                                    continue;
                              }

                              if (colnames)
                              {
                                    char     *label = strVal(lfirst(colname));

                                    *colnames = lappend(*colnames,
                                                                  makeString(pstrdup(label)));
                              }

                              if (colvars)
                              {
                                    Var            *varnode;

                                    varnode = makeVar(rtindex, varattno,
                                                              exprType(avar),
                                                              exprTypmod(avar),
                                                              sublevels_up);
                                    varnode->location = location;

                                    *colvars = lappend(*colvars, varnode);
                              }
                        }
                  }
                  break;
            case RTE_CTE:
                  {
                        ListCell   *aliasp_item = list_head(rte->eref->colnames);
                        ListCell   *lct;
                        ListCell   *lcm;

                        varattno = 0;
                        forboth(lct, rte->ctecoltypes, lcm, rte->ctecoltypmods)
                        {
                              Oid               coltype = lfirst_oid(lct);
                              int32       coltypmod = lfirst_int(lcm);

                              varattno++;

                              if (colnames)
                              {
                                    /* Assume there is one alias per output column */
                                    char     *label = strVal(lfirst(aliasp_item));

                                    *colnames = lappend(*colnames, makeString(pstrdup(label)));
                                    aliasp_item = lnext(aliasp_item);
                              }

                              if (colvars)
                              {
                                    Var            *varnode;

                                    varnode = makeVar(rtindex, varattno,
                                                              coltype, coltypmod,
                                                              sublevels_up);
                                    *colvars = lappend(*colvars, varnode);
                              }
                        }
                  }
                  break;
            default:
                  elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind);
      }
}

/*
 * expandRelation -- expandRTE subroutine
 */
static void
expandRelation(Oid relid, Alias *eref, int rtindex, int sublevels_up,
                     int location, bool include_dropped,
                     List **colnames, List **colvars)
{
      Relation    rel;

      /* Get the tupledesc and turn it over to expandTupleDesc */
      rel = relation_open(relid, AccessShareLock);
      expandTupleDesc(rel->rd_att, eref, rtindex, sublevels_up,
                              location, include_dropped,
                              colnames, colvars);
      relation_close(rel, AccessShareLock);
}

/*
 * expandTupleDesc -- expandRTE subroutine
 */
static void
expandTupleDesc(TupleDesc tupdesc, Alias *eref,
                        int rtindex, int sublevels_up,
                        int location, bool include_dropped,
                        List **colnames, List **colvars)
{
      int               maxattrs = tupdesc->natts;
      int               numaliases = list_length(eref->colnames);
      int               varattno;

      for (varattno = 0; varattno < maxattrs; varattno++)
      {
            Form_pg_attribute attr = tupdesc->attrs[varattno];

            if (attr->attisdropped)
            {
                  if (include_dropped)
                  {
                        if (colnames)
                              *colnames = lappend(*colnames, makeString(pstrdup("")));
                        if (colvars)
                        {
                              /*
                               * can't use atttypid here, but it doesn't really matter
                               * what type the Const claims to be.
                               */
                              *colvars = lappend(*colvars, makeNullConst(INT4OID, -1));
                        }
                  }
                  continue;
            }

            if (colnames)
            {
                  char     *label;

                  if (varattno < numaliases)
                        label = strVal(list_nth(eref->colnames, varattno));
                  else
                        label = NameStr(attr->attname);
                  *colnames = lappend(*colnames, makeString(pstrdup(label)));
            }

            if (colvars)
            {
                  Var            *varnode;

                  varnode = makeVar(rtindex, attr->attnum,
                                            attr->atttypid, attr->atttypmod,
                                            sublevels_up);
                  varnode->location = location;

                  *colvars = lappend(*colvars, varnode);
            }
      }
}

/*
 * expandRelAttrs -
 *      Workhorse for "*" expansion: produce a list of targetentries
 *      for the attributes of the RTE
 *
 * As with expandRTE, rtindex/sublevels_up determine the varno/varlevelsup
 * fields of the Vars produced, and location sets their location.
 * pstate->p_next_resno determines the resnos assigned to the TLEs.
 * The referenced columns are marked as requiring SELECT access.
 */
List *
expandRelAttrs(ParseState *pstate, RangeTblEntry *rte,
                     int rtindex, int sublevels_up, int location)
{
      List     *names,
                     *vars;
      ListCell   *name,
                     *var;
      List     *te_list = NIL;

      expandRTE(rte, rtindex, sublevels_up, location, false,
                    &names, &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;

      forboth(name, names, var, vars)
      {
            char     *label = strVal(lfirst(name));
            Var            *varnode = (Var *) lfirst(var);
            TargetEntry *te;

            te = makeTargetEntry((Expr *) varnode,
                                           (AttrNumber) pstate->p_next_resno++,
                                           label,
                                           false);
            te_list = lappend(te_list, te);

            /* Require read access to each column */
            markVarForSelectPriv(pstate, varnode, rte);
      }

      Assert(name == NULL && var == NULL);            /* lists not the same length? */

      return te_list;
}

/*
 * get_rte_attribute_name
 *          Get an attribute name from a RangeTblEntry
 *
 * This is unlike get_attname() because we use aliases if available.
 * In particular, it will work on an RTE for a subselect or join, whereas
 * get_attname() only works on real relations.
 *
 * "*" is returned if the given attnum is InvalidAttrNumber --- this case
 * occurs when a Var represents a whole tuple of a relation.
 */
char *
get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum)
{
      if (attnum == InvalidAttrNumber)
            return "*";

      /*
       * If there is a user-written column alias, use it.
       */
      if (rte->alias &&
            attnum > 0 && attnum <= list_length(rte->alias->colnames))
            return strVal(list_nth(rte->alias->colnames, attnum - 1));

      /*
       * If the RTE is a relation, go to the system catalogs not the
       * eref->colnames list.  This is a little slower but it will give the
       * right answer if the column has been renamed since the eref list was
       * built (which can easily happen for rules).
       */
      if (rte->rtekind == RTE_RELATION)
            return get_relid_attribute_name(rte->relid, attnum);

      /*
       * Otherwise use the column name from eref.  There should always be one.
       */
      if (attnum > 0 && attnum <= list_length(rte->eref->colnames))
            return strVal(list_nth(rte->eref->colnames, attnum - 1));

      /* else caller gave us a bogus attnum */
      elog(ERROR, "invalid attnum %d for rangetable entry %s",
             attnum, rte->eref->aliasname);
      return NULL;                        /* keep compiler quiet */
}

/*
 * get_rte_attribute_type
 *          Get attribute type information from a RangeTblEntry
 */
void
get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
                                 Oid *vartype, int32 *vartypmod)
{
      switch (rte->rtekind)
      {
            case RTE_RELATION:
                  {
                        /* Plain relation RTE --- get the attribute's type info */
                        HeapTuple   tp;
                        Form_pg_attribute att_tup;

                        tp = SearchSysCache(ATTNUM,
                                                      ObjectIdGetDatum(rte->relid),
                                                      Int16GetDatum(attnum),
                                                      0, 0);
                        if (!HeapTupleIsValid(tp))          /* shouldn't happen */
                              elog(ERROR, "cache lookup failed for attribute %d of relation %u",
                                     attnum, rte->relid);
                        att_tup = (Form_pg_attribute) GETSTRUCT(tp);

                        /*
                         * If dropped column, pretend it ain't there.  See notes in
                         * scanRTEForColumn.
                         */
                        if (att_tup->attisdropped)
                              ereport(ERROR,
                                          (errcode(ERRCODE_UNDEFINED_COLUMN),
                              errmsg("column \"%s\" of relation \"%s\" does not exist",
                                       NameStr(att_tup->attname),
                                       get_rel_name(rte->relid))));
                        *vartype = att_tup->atttypid;
                        *vartypmod = att_tup->atttypmod;
                        ReleaseSysCache(tp);
                  }
                  break;
            case RTE_SUBQUERY:
                  {
                        /* Subselect RTE --- get type info from subselect's tlist */
                        TargetEntry *te = get_tle_by_resno(rte->subquery->targetList,
                                                                           attnum);

                        if (te == NULL || te->resjunk)
                              elog(ERROR, "subquery %s does not have attribute %d",
                                     rte->eref->aliasname, attnum);
                        *vartype = exprType((Node *) te->expr);
                        *vartypmod = exprTypmod((Node *) te->expr);
                  }
                  break;
            case RTE_FUNCTION:
                  {
                        /* Function RTE */
                        TypeFuncClass functypclass;
                        Oid               funcrettype;
                        TupleDesc   tupdesc;

                        functypclass = get_expr_result_type(rte->funcexpr,
                                                                              &funcrettype,
                                                                              &tupdesc);

                        if (functypclass == TYPEFUNC_COMPOSITE)
                        {
                              /* Composite data type, e.g. a table's row type */
                              Form_pg_attribute att_tup;

                              Assert(tupdesc);
                              /* this is probably a can't-happen case */
                              if (attnum < 1 || attnum > tupdesc->natts)
                                    ereport(ERROR,
                                                (errcode(ERRCODE_UNDEFINED_COLUMN),
                                    errmsg("column %d of relation \"%s\" does not exist",
                                             attnum,
                                             rte->eref->aliasname)));

                              att_tup = tupdesc->attrs[attnum - 1];

                              /*
                               * If dropped column, pretend it ain't there.  See notes
                               * in scanRTEForColumn.
                               */
                              if (att_tup->attisdropped)
                                    ereport(ERROR,
                                                (errcode(ERRCODE_UNDEFINED_COLUMN),
                                                 errmsg("column \"%s\" of relation \"%s\" does not exist",
                                                            NameStr(att_tup->attname),
                                                            rte->eref->aliasname)));
                              *vartype = att_tup->atttypid;
                              *vartypmod = att_tup->atttypmod;
                        }
                        else if (functypclass == TYPEFUNC_SCALAR)
                        {
                              /* Base data type, i.e. scalar */
                              *vartype = funcrettype;
                              *vartypmod = -1;
                        }
                        else if (functypclass == TYPEFUNC_RECORD)
                        {
                              *vartype = list_nth_oid(rte->funccoltypes, attnum - 1);
                              *vartypmod = list_nth_int(rte->funccoltypmods, attnum - 1);
                        }
                        else
                        {
                              /* addRangeTableEntryForFunction should've caught this */
                              elog(ERROR, "function in FROM has unsupported return type");
                        }
                  }
                  break;
            case RTE_VALUES:
                  {
                        /* Values RTE --- get type info from first sublist */
                        List     *collist = (List *) linitial(rte->values_lists);
                        Node     *col;

                        if (attnum < 1 || attnum > list_length(collist))
                              elog(ERROR, "values list %s does not have attribute %d",
                                     rte->eref->aliasname, attnum);
                        col = (Node *) list_nth(collist, attnum - 1);
                        *vartype = exprType(col);
                        *vartypmod = exprTypmod(col);
                  }
                  break;
            case RTE_JOIN:
                  {
                        /*
                         * Join RTE --- get type info from join RTE's alias variable
                         */
                        Node     *aliasvar;

                        Assert(attnum > 0 && attnum <= list_length(rte->joinaliasvars));
                        aliasvar = (Node *) list_nth(rte->joinaliasvars, attnum - 1);
                        *vartype = exprType(aliasvar);
                        *vartypmod = exprTypmod(aliasvar);
                  }
                  break;
            case RTE_CTE:
                  {
                        /* CTE RTE --- get type info from lists in the RTE */
                        Assert(attnum > 0 && attnum <= list_length(rte->ctecoltypes));
                        *vartype = list_nth_oid(rte->ctecoltypes, attnum - 1);
                        *vartypmod = list_nth_int(rte->ctecoltypmods, attnum - 1);
                  }
                  break;
            default:
                  elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind);
      }
}

/*
 * get_rte_attribute_is_dropped
 *          Check whether attempted attribute ref is to a dropped column
 */
bool
get_rte_attribute_is_dropped(RangeTblEntry *rte, AttrNumber attnum)
{
      bool        result;

      switch (rte->rtekind)
      {
            case RTE_RELATION:
                  {
                        /*
                         * Plain relation RTE --- get the attribute's catalog entry
                         */
                        HeapTuple   tp;
                        Form_pg_attribute att_tup;

                        tp = SearchSysCache(ATTNUM,
                                                      ObjectIdGetDatum(rte->relid),
                                                      Int16GetDatum(attnum),
                                                      0, 0);
                        if (!HeapTupleIsValid(tp))          /* shouldn't happen */
                              elog(ERROR, "cache lookup failed for attribute %d of relation %u",
                                     attnum, rte->relid);
                        att_tup = (Form_pg_attribute) GETSTRUCT(tp);
                        result = att_tup->attisdropped;
                        ReleaseSysCache(tp);
                  }
                  break;
            case RTE_SUBQUERY:
            case RTE_VALUES:
            case RTE_CTE:
                  /* Subselect, Values, CTE RTEs never have dropped columns */
                  result = false;
                  break;
            case RTE_JOIN:
                  {
                        /*
                         * A join RTE would not have dropped columns when constructed,
                         * but one in a stored rule might contain columns that were
                         * dropped from the underlying tables, if said columns are
                         * nowhere explicitly referenced in the rule.  This will be
                         * signaled to us by a NULL Const in the joinaliasvars list.
                         */
                        Var            *aliasvar;

                        if (attnum <= 0 ||
                              attnum > list_length(rte->joinaliasvars))
                              elog(ERROR, "invalid varattno %d", attnum);
                        aliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1);

                        result = IsA(aliasvar, Const);
                  }
                  break;
            case RTE_FUNCTION:
                  {
                        /* Function RTE */
                        Oid               funcrettype = exprType(rte->funcexpr);
                        Oid               funcrelid = typeidTypeRelid(funcrettype);

                        if (OidIsValid(funcrelid))
                        {
                              /*
                               * Composite data type, i.e. a table's row type
                               *
                               * Same as ordinary relation RTE
                               */
                              HeapTuple   tp;
                              Form_pg_attribute att_tup;

                              tp = SearchSysCache(ATTNUM,
                                                            ObjectIdGetDatum(funcrelid),
                                                            Int16GetDatum(attnum),
                                                            0, 0);
                              if (!HeapTupleIsValid(tp))    /* shouldn't happen */
                                    elog(ERROR, "cache lookup failed for attribute %d of relation %u",
                                           attnum, funcrelid);
                              att_tup = (Form_pg_attribute) GETSTRUCT(tp);
                              result = att_tup->attisdropped;
                              ReleaseSysCache(tp);
                        }
                        else
                        {
                              /*
                               * Must be a base data type, i.e. scalar
                               */
                              result = false;
                        }
                  }
                  break;
            default:
                  elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind);
                  result = false;         /* keep compiler quiet */
      }

      return result;
}

/*
 * Given a targetlist and a resno, return the matching TargetEntry
 *
 * Returns NULL if resno is not present in list.
 *
 * Note: we need to search, rather than just indexing with list_nth(),
 * because not all tlists are sorted by resno.
 */
TargetEntry *
get_tle_by_resno(List *tlist, AttrNumber resno)
{
      ListCell   *l;

      foreach(l, tlist)
      {
            TargetEntry *tle = (TargetEntry *) lfirst(l);

            if (tle->resno == resno)
                  return tle;
      }
      return NULL;
}

/*
 * Given a Query and rangetable index, return relation's RowMarkClause if any
 *
 * Returns NULL if relation is not selected FOR UPDATE/SHARE
 */
RowMarkClause *
get_rowmark(Query *qry, Index rtindex)
{
      ListCell   *l;

      foreach(l, qry->rowMarks)
      {
            RowMarkClause *rc = (RowMarkClause *) lfirst(l);

            if (rc->rti == rtindex)
                  return rc;
      }
      return NULL;
}

/*
 *    given relation and att name, return attnum of variable
 *
 *    Returns InvalidAttrNumber if the attr doesn't exist (or is dropped).
 *
 *    This should only be used if the relation is already
 *    heap_open()'ed.  Use the cache version get_attnum()
 *    for access to non-opened relations.
 */
int
attnameAttNum(Relation rd, const char *attname, bool sysColOK)
{
      int               i;

      for (i = 0; i < rd->rd_rel->relnatts; i++)
      {
            Form_pg_attribute att = rd->rd_att->attrs[i];

            if (namestrcmp(&(att->attname), attname) == 0 && !att->attisdropped)
                  return i + 1;
      }

      if (sysColOK)
      {
            if ((i = specialAttNum(attname)) != InvalidAttrNumber)
            {
                  if (i != ObjectIdAttributeNumber || rd->rd_rel->relhasoids)
                        return i;
            }
      }

      /* on failure */
      return InvalidAttrNumber;
}

/* specialAttNum()
 *
 * Check attribute name to see if it is "special", e.g. "oid".
 * - thomas 2000-02-07
 *
 * Note: this only discovers whether the name could be a system attribute.
 * Caller needs to verify that it really is an attribute of the rel,
 * at least in the case of "oid", which is now optional.
 */
static int
specialAttNum(const char *attname)
{
      Form_pg_attribute sysatt;

      sysatt = SystemAttributeByName(attname,
                                                   true /* "oid" will be accepted */ );
      if (sysatt != NULL)
            return sysatt->attnum;
      return InvalidAttrNumber;
}


/*
 * given attribute id, return name of that attribute
 *
 *    This should only be used if the relation is already
 *    heap_open()'ed.  Use the cache version get_atttype()
 *    for access to non-opened relations.
 */
Name
attnumAttName(Relation rd, int attid)
{
      if (attid <= 0)
      {
            Form_pg_attribute sysatt;

            sysatt = SystemAttributeDefinition(attid, rd->rd_rel->relhasoids);
            return &sysatt->attname;
      }
      if (attid > rd->rd_att->natts)
            elog(ERROR, "invalid attribute number %d", attid);
      return &rd->rd_att->attrs[attid - 1]->attname;
}

/*
 * given attribute id, return type of that attribute
 *
 *    This should only be used if the relation is already
 *    heap_open()'ed.  Use the cache version get_atttype()
 *    for access to non-opened relations.
 */
Oid
attnumTypeId(Relation rd, int attid)
{
      if (attid <= 0)
      {
            Form_pg_attribute sysatt;

            sysatt = SystemAttributeDefinition(attid, rd->rd_rel->relhasoids);
            return sysatt->atttypid;
      }
      if (attid > rd->rd_att->natts)
            elog(ERROR, "invalid attribute number %d", attid);
      return rd->rd_att->attrs[attid - 1]->atttypid;
}

/*
 * Generate a warning or error about an implicit RTE, if appropriate.
 *
 * If ADD_MISSING_FROM is not enabled, raise an error. Otherwise, emit
 * a warning.
 */
static void
warnAutoRange(ParseState *pstate, RangeVar *relation)
{
      RangeTblEntry *rte;
      int               sublevels_up;
      const char *badAlias = NULL;

      /*
       * Check to see if there are any potential matches in the query's
       * rangetable.    This affects the message we provide.
       */
      rte = searchRangeTable(pstate, relation);

      /*
       * If we found a match that has an alias and the alias is visible in the
       * namespace, then the problem is probably use of the relation's real name
       * instead of its alias, ie "SELECT foo.* FROM foo f". This mistake is
       * common enough to justify a specific hint.
       *
       * If we found a match that doesn't meet those criteria, assume the
       * problem is illegal use of a relation outside its scope, as in the
       * MySQL-ism "SELECT ... FROM a, b LEFT JOIN c ON (a.x = c.y)".
       */
      if (rte && rte->alias &&
            strcmp(rte->eref->aliasname, relation->relname) != 0 &&
            refnameRangeTblEntry(pstate, NULL, rte->eref->aliasname,
                                           relation->location,
                                           &sublevels_up) == rte)
            badAlias = rte->eref->aliasname;

      if (!add_missing_from)
      {
            if (rte)
                  ereport(ERROR,
                              (errcode(ERRCODE_UNDEFINED_TABLE),
                  errmsg("invalid reference to FROM-clause entry for table \"%s\"",
                           relation->relname),
                               (badAlias ?
                  errhint("Perhaps you meant to reference the table alias \"%s\".",
                              badAlias) :
                                errhint("There is an entry for table \"%s\", but it cannot be referenced from this part of the query.",
                                            rte->eref->aliasname)),
                               parser_errposition(pstate, relation->location)));
            else
                  ereport(ERROR,
                              (errcode(ERRCODE_UNDEFINED_TABLE),
                               errmsg("missing FROM-clause entry for table \"%s\"",
                                          relation->relname),
                               parser_errposition(pstate, relation->location)));
      }
      else
      {
            /* just issue a warning */
            ereport(NOTICE,
                        (errcode(ERRCODE_UNDEFINED_TABLE),
                         errmsg("adding missing FROM-clause entry for table \"%s\"",
                                    relation->relname),
                         (badAlias ?
                  errhint("Perhaps you meant to reference the table alias \"%s\".",
                              badAlias) :
                          (rte ?
                           errhint("There is an entry for table \"%s\", but it cannot be referenced from this part of the query.",
                                       rte->eref->aliasname) : 0)),
                         parser_errposition(pstate, relation->location)));
      }
}

Generated by  Doxygen 1.6.0   Back to index