blob: e1be9519c75602339ecef930feb8f27634bddd74 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*-------------------------------------------------------------------------
*
* parse_relation.c
* parser support routines dealing with relations
*
* Portions Copyright (c) 2006-2008, Greenplum inc
* Portions Copyright (c) 1996-2008, 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.125 2006/10/04 00:29:56 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include <ctype.h>
#include "catalog/catquery.h"
#include "access/genam.h"
#include "catalog/heap.h"
#include "access/heapam.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/pg_exttable.h"
#include "catalog/pg_proc_callback.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_database.h"
#include "catalog/pg_type.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/relation.h" /* CdbRelColumnInfo */
#include "optimizer/pathnode.h" /* cdb_rte_find_pseudo_column() */
#include "parser/parsetree.h"
#include "parser/parse_coerce.h"
#include "parser/parse_expr.h"
#include "parser/parse_relation.h"
#include "parser/parse_type.h"
#include "utils/acl.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/guc.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
#include "utils/rangerrest.h"
/* GUC parameter */
bool add_missing_from;
static RangeTblEntry *scanNameSpaceForRefname(ParseState *pstate,
const char *refname);
static RangeTblEntry *scanNameSpaceForRelid(ParseState *pstate, Oid relid);
static LockingClause *getLockingClause(ParseState *pstate, char *refname);
static void expandRelation(Oid relid, Alias *eref,
int rtindex, int sublevels_up,
bool include_dropped,
List **colnames, List **colvars);
static void expandTupleDesc(TupleDesc tupdesc, Alias *eref,
int rtindex, int sublevels_up,
bool include_dropped,
List **colnames, List **colvars);
static int specialAttNum(const char *attname);
static void warnAutoRange(ParseState *pstate, RangeVar *relation,
int location);
static bool get_attisdropped(Oid relid, int attnum);
static RangeTblEntry *refnameRangeTblEntryHelper(ParseState *pstate, const char *refname, Oid relid, int *sublevels_up);
static RangeTblEntry *refnameRangeTblEntryHelperSchemaQualified(ParseState *pstate, Oid dboid, const char *schemaname, const char *refname, int *sublevels_up);
/*
* 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 *catalogname,
const char *schemaname,
const char *refname,
int location,
int *sublevels_up)
{
Oid relId = InvalidOid;
if (sublevels_up)
{
*sublevels_up = 0;
}
if (NULL == catalogname && NULL == schemaname)
{
/* simple unqualified name: search the rangevars of the query */
return refnameRangeTblEntryHelper(pstate, refname, InvalidOid /*relId*/, sublevels_up);
}
if (NULL != catalogname && NULL != schemaname)
{
/* fully qualified table name: catalog.schema.refname */
Oid dboid = GetCatalogId(catalogname);
Oid namespaceId = LookupExplicitNamespace(schemaname, dboid);
relId = get_relname_relid(refname, namespaceId);
if (!OidIsValid(relId))
{
return NULL;
}
return refnameRangeTblEntryHelper(pstate, refname, relId, sublevels_up);
}
/* namespace-qualified name: schema.refname: consider both the current database and HCatalog */
Assert(NULL != schemaname);
Oid dboidCurrent = GetCatalogId(NULL /*catalogname*/);
int slevelsUpHcatalog = 0;
int slevelsUpCurrent = 0;
int *pslevelsUpCurrent = NULL;
int *pslevelsUpHcatalog = NULL;
if (NULL != sublevels_up)
{
pslevelsUpCurrent = &slevelsUpCurrent;
pslevelsUpHcatalog = &slevelsUpHcatalog;
}
RangeTblEntry *rteCurrent = refnameRangeTblEntryHelperSchemaQualified(pstate, dboidCurrent, schemaname, refname, pslevelsUpCurrent);
RangeTblEntry *rteHcatalog = refnameRangeTblEntryHelperSchemaQualified(pstate, HcatalogDbOid, schemaname, refname, pslevelsUpHcatalog);
if (NULL == rteCurrent && NULL == rteHcatalog)
{
return NULL;
}
if (NULL != rteCurrent && NULL != rteHcatalog)
{
ereport(ERROR,
(errcode(ERRCODE_AMBIGUOUS_ALIAS),
errmsg("table reference \"%s.%s\" is ambiguous",
schemaname, refname)));
}
if (NULL != rteCurrent)
{
if (NULL != sublevels_up)
{
*sublevels_up = *pslevelsUpCurrent;
}
return rteCurrent;
}
Assert(NULL != rteHcatalog);
if (NULL != sublevels_up)
{
*sublevels_up = *pslevelsUpHcatalog;
}
return rteHcatalog;
}
/*
* refnameRangeTblEntryHelper
* Given a refname an possibly resolved relid, 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.
*
*
* The relid may have been resolved by the caller, in case a qualified refname (schemaname != NULL)
* was specified.
*/
RangeTblEntry *
refnameRangeTblEntryHelper(ParseState *pstate, const char *refname, Oid relId, int *sublevels_up)
{
while (pstate != NULL)
{
RangeTblEntry *result = NULL;
if (OidIsValid(relId))
{
result = scanNameSpaceForRelid(pstate, relId);
}
else
{
result = scanNameSpaceForRefname(pstate, refname);
}
if (result)
{
return result;
}
if (NULL == sublevels_up)
{
break;
}
(*sublevels_up)++;
pstate = pstate->parentParseState;
}
return NULL;
}
/*
* refnameRangeTblEntryHelperSchemaQualified
* Given a schema-qualified refname and a dboid, 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.
*
*/
RangeTblEntry *
refnameRangeTblEntryHelperSchemaQualified(ParseState *pstate, Oid dboid, const char *nspname, const char *refname, int *sublevels_up)
{
Oid nspid = LookupNamespaceId(nspname, dboid);
if (!OidIsValid(nspid))
{
return NULL;
}
Oid relid = get_relname_relid(refname, nspid);
if (!OidIsValid(relid))
{
return NULL;
}
return refnameRangeTblEntryHelper(pstate, refname, relid, sublevels_up);
}
/*
* 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)
{
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)));
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)
{
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)));
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)
{
Assert(refname != NULL);
Index levelsup;
for (levelsup = 0;
pstate != NULL;
pstate = pstate->parentParseState, levelsup++)
{
ListCell *lc;
foreach(lc, pstate->p_ctenamespace)
{
CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);
Assert(cte != NULL && cte->ctename != NULL);
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 name (or CTE) and matches
* on alias.
*/
static RangeTblEntry *
searchRangeTable(ParseState *pstate, RangeVar *relation)
{
Oid relId = InvalidOid;
const char *refname = relation->relname;
CommonTableExpr *cte = NULL;
Index ctelevelsup = 0;
/*
* 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, true /*allowHcatalog*/);
Index levelsup = 0;
while (pstate != NULL)
{
ListCell *l;
foreach(l, pstate->p_rtable)
{
RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
if (OidIsValid(relId) &&
rte->rtekind == RTE_RELATION &&
rte->relid == relId)
return rte;
if (rte->rtekind == RTE_CTE &&
cte != NULL &&
rte->ctelevelsup + levelsup == ctelevelsup &&
strcmp(rte->ctename, refname) == 0)
return rte;
if (rte->eref != NULL &&
rte->eref->aliasname != NULL &&
strcmp(rte->eref->aliasname, refname) == 0)
return rte;
}
pstate = pstate->parentParseState;
levelsup++;
}
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);
}
/*
* 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.
* See comments in setTargetTable().
*
* NOTE: if the RTE is for a join, marking it as requiring read access does
* nothing. It might seem that we need to propagate the mark to all the
* contained RTEs, but that is not necessary. This is so because a join
* expression can only appear in a FROM clause, and any table named in
* FROM will be marked as requiring read access from the beginning.
*/
Node *
scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname,
int location)
{
Node *result = NULL;
int attnum = 0;
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),
errOmitLocation(true),
parser_errposition(pstate, location)));
result = (Node *) make_var(pstate, rte, attnum, location);
/* Require read access */
rte->requiredPerms |= ACL_SELECT;
}
}
/*
* 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 (caql_getcount(
NULL,
cql("SELECT COUNT(*) FROM pg_attribute "
" WHERE attrelid = :1 "
" AND attnum = :2 ",
ObjectIdGetDatum(rte->relid),
Int16GetDatum(attnum))))
{
result = (Node *) make_var(pstate, rte, attnum, location);
/* Require read access */
rte->requiredPerms |= ACL_SELECT;
}
}
}
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),
errOmitLocation(true),
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 *catalogname,
char *schemaname,
char *refname,
char *colname,
bool implicitRTEOK,
int location)
{
RangeTblEntry *rte;
int sublevels_up;
rte = refnameRangeTblEntry(pstate, catalogname, schemaname, refname, location, &sublevels_up);
if (rte == NULL)
{
if (!implicitRTEOK)
return NULL;
rte = addImplicitRTE(pstate, makeRangeVar(catalogname, schemaname, refname, location),
location);
}
return scanRTEForColumn(pstate, rte, colname, location);
}
/*
* 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 the same as CdbOpenRelationRv, except that it caters
* to some parser-specific error reporting needs.
*/
static Relation
parserOpenTable(ParseState *pstate, const RangeVar *relation,
int lockmode, bool nowait, bool *lockUpgraded)
{
Relation rel = NULL;
PG_TRY();
{
rel = CdbOpenRelationRv(relation, lockmode, nowait, NULL);
}
PG_CATCH();
{
if (relation->schemaname == NULL &&
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("Re-order the WITH items to remove forward references.")));
}
PG_RE_THROW();
}
PG_END_TRY();
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 = AccessShareLock;
bool nowait = false;
LockingClause *locking;
Relation rel;
/*
* CDB: lock promotion around the locking clause is a little different
* from postgres to allow for required lock promotion for distributed
* tables.
*/
locking = getLockingClause(pstate, refname);
if (locking)
{
lockmode = locking->forUpdate ? RowExclusiveLock : RowShareLock;
nowait = locking->noWait;
}
rel = parserOpenTable(pstate, relation, lockmode, nowait, NULL);
/*
* 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.
*/
rte->relid = RelationGetRelid(rel);
rte->alias = alias;
rte->rtekind = RTE_RELATION;
/* external tables don't allow inheritance */
if(RelationIsExternal(rel))
inh = false;
/*
* 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 */
/*
* 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 */
/*
* 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;
/*
* 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;
Oid funcDescribe = InvalidOid;
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;
/*
* If the function has TABLE value expressions in its arguments then it must
* be planned as a TableFunctionScan instead of a normal FunctionScan. We
* mark this here because this is where we know that the function is being
* used as a RangeTableEntry.
*/
if (funcexpr && IsA(funcexpr, FuncExpr))
{
FuncExpr *func = (FuncExpr *) funcexpr;
if (func->args && IsA(func->args, List))
{
ListCell *arg;
foreach(arg, (List*) func->args)
{
Node *n = (Node *) lfirst(arg);
if (IsA(n, TableValueExpr))
{
TableValueExpr *input = (TableValueExpr *) n;
/*
* Currently only support single TABLE value expression.
*
* Note: this shouldn't be possible given that we don't
* allow it at function creation so the function parser
* should have already errored due to type mismatch.
*/
Assert(IsA(input->subquery, Query));
if (rte->subquery != NULL)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("functions over multiple TABLE value "
"expressions not supported")));
/*
* Convert RTE to a TableFunctionScan over the specified
* input
*/
rte->rtekind = RTE_TABLEFUNCTION;
rte->subquery = (Query *) input->subquery;
/*
* Mark function as a table function so that the second pass
* check, parseCheckTableFunctions(), can correctly detect
* that it is a valid TABLE value expression.
*/
func->is_tablefunc = true;
/*
* We do not break from the loop here because we want to
* keep looping to guard against multiple TableValueExpr
* arguments.
*/
}
}
}
}
/*
* Now determine if the function returns a simple or composite type.
*/
functypclass = get_expr_result_type(funcexpr,
&funcrettype,
&tupdesc);
/*
* Handle dynamic type resolution for functions with DESCRIBE callbacks.
*/
if (functypclass == TYPEFUNC_RECORD && IsA(funcexpr, FuncExpr))
{
FuncExpr *func = (FuncExpr *) funcexpr;
Datum d;
int i;
Insist(TypeSupportsDescribe(funcrettype));
funcDescribe = lookupProcCallback(func->funcid, PROMETHOD_DESCRIBE);
if (OidIsValid(funcDescribe))
{
FmgrInfo flinfo;
FunctionCallInfoData fcinfo;
/*
* Describe functions have the signature d(internal) => internal
* where the parameter is the untransformed FuncExpr node and the result
* is a tuple descriptor. Its context is RangeTblEntry which has
* funcuserdata field to store arbitrary binary data to transport
* to executor.
*/
rte->funcuserdata = NULL;
fmgr_info(funcDescribe, &flinfo);
InitFunctionCallInfoData(fcinfo, &flinfo, 1, (Node *) rte, NULL);
fcinfo.arg[0] = PointerGetDatum(funcexpr);
fcinfo.argnull[0] = false;
d = FunctionCallInvoke(&fcinfo);
if (fcinfo.isnull)
elog(ERROR, "function %u returned NULL", flinfo.fn_oid);
tupdesc = (TupleDesc) DatumGetPointer(d);
/*
* Might want to improve this API so the describe method return
* value is somehow verifiable
*/
if (tupdesc != NULL)
{
functypclass = TYPEFUNC_COMPOSITE;
for (i = 0; i < tupdesc->natts; i++)
{
Form_pg_attribute attr = tupdesc->attrs[i];
rte->funccoltypes = lappend_oid(rte->funccoltypes,
attr->atttypid);
rte->funccoltypmods = lappend_int(rte->funccoltypmods,
attr->atttypmod);
}
}
}
}
/*
* 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\""),
errOmitLocation(true)));
}
else
{
if (functypclass == TYPEFUNC_RECORD)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("a column definition list is required for functions returning \"record\""),
errOmitLocation(true)));
}
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->typname->setof)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
errmsg("column \"%s\" cannot be declared SETOF",
attrname),
errOmitLocation(true)));
eref->colnames = lappend(eref->colnames, makeString(attrname));
attrtype = typenameTypeId(pstate, n->typname);
attrtypmod = n->typname->typmod;
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)),
errOmitLocation(true)));
/*----------
* 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;
/*
* 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),
errOmitLocation(true)));
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;
/*
* 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;
/* transform any Vars of type UNKNOWNOID if we can */
fixup_unknown_vars_in_exprlist(pstate, rte->joinaliasvars);
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;
/*
* 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 to the pstate's range table (p_rtable).
*
* This is just like addRangeTableEntry() except that it makes a CTE RTE.
*/
RangeTblEntry *
addRangeTableEntryForCTE(ParseState *pstate,
CommonTableExpr *cte,
Index levelsup,
RangeVar *rangeVar,
bool inFromCl)
{
RangeTblEntry *rte = makeNode(RangeTblEntry);
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++;
/* Currently, we only support SELECT in WITH query */
AssertImply(IsA(cte->ctequery, Query),
((Query *) cte->ctequery)->commandType == CMD_SELECT);
rte->ctecoltypes = cte->ctecoltypes;
rte->ctecoltypmods = cte->ctecoltypmods;
rte->alias = rangeVar->alias;
char *refname = rte->alias ? rte->alias->aliasname : cte->ctename;
Alias *eref;
if (rte->alias)
eref = copyObject(rte->alias);
else
eref = makeAlias(refname, NIL);
int numaliases = list_length(eref->colnames);
/* fill in any unspecified alias columns */
int varattno = 0;
ListCell *lc;
foreach(lc, cte->ctecolnames)
{
varattno++;
if (varattno > numaliases)
eref->colnames = lappend(eref->colnames, lfirst(lc));
}
if (varattno < numaliases)
{
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg(ERRMSG_GP_WITH_COLUMNS_MISMATCH, refname)));
}
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;
/*
* 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 LockingClause*
getLockingClause(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)
{
return lc; /* all tables used in query */
}
else
{
/* just the named tables */
ListCell *l2;
foreach(l2, lc->lockedRels)
{
char *rname = strVal(lfirst(l2));
if (strcmp(refname, rname) == 0)
return lc; /* refname matched */
}
}
}
pstate = pstate->parentParseState;
}
return NULL;
}
/*
* isSimplyUpdatableRelation
*
* The oid must reference a normal, heap relation. This disallows
* AO, AO/CO, external tables, views, etc.
*/
bool
isSimplyUpdatableRelation(Oid relid)
{
if (OidIsValid(relid))
{
cqContext *pcqCtx;
HeapTuple tuple;
pcqCtx = caql_beginscan(
NULL,
cql("SELECT * FROM pg_class "
" WHERE oid = :1 ",
ObjectIdGetDatum(relid)));
tuple = caql_getnext(pcqCtx);
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for relation %u", relid);
Form_pg_class rel = (Form_pg_class) GETSTRUCT(tuple);
bool is_heap_tuple = rel->relkind == RELKIND_RELATION &&
rel->relstorage == RELSTORAGE_HEAP;
caql_endscan(pcqCtx);
return is_heap_tuple;
}
return false;
}
/*
* extractSimplyUpdatableRTEIndex
* given a range table associated with a simply updatable range table,
* return the desired RangeTblEntry
*
* NOTE: The range table *MUST* belong to a simply updatable query.
*
* The semantics of a simply updatable query demand a range table consisting
* of exactly one logical table. Thus, for the simple case of a one-element
* range table, we quickly return the index for the lone RTE.
* However, we must also cope with inheritance, where an RTE requesting
* inheritance may have been expanded out into its child relations. In this
* case, we seek to return the parent RTE.
*/
Index
extractSimplyUpdatableRTEIndex(List *rtable)
{
Assert(list_length(rtable) > 0);
if (list_length(rtable) == 1)
return 1;
/*
* This better be an inheritance case.
* Find the RTE with inh = true.
* Furthermore, we Insist that no other RTEs have inh = true.
*/
Index temp, ret = 0;
ListCell *lc;
foreach_with_count (lc, rtable, temp)
{
RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
if (rte->inh)
{
Insist(ret == 0); /* to be simply updatable, there cannot be more than 1 parent table */
ret = temp + 1; /* the temp counter is zero indexed */
}
}
Insist(ret != 0);
return ret;
}
/*
* 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, int location)
{
RangeTblEntry *rte;
/* issue warning or error as needed */
warnAutoRange(pstate, relation, location);
/*
* 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.
*/
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 and sublevels_up are the varno and varlevelsup 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,
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);
*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 */
Assert(IsA(lfirst(aliasp_item), String));
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;
case RTE_TABLEFUNCTION:
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,
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);
*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);
*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);
*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);
*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,
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, include_dropped,
colnames, colvars);
relation_close(rel, AccessShareLock);
}
/*
* expandTupleDesc -- expandRTE subroutine
*/
static void
expandTupleDesc(TupleDesc tupdesc, Alias *eref,
int rtindex, int sublevels_up,
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);
*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. pstate->p_next_resno determines the resnos
* assigned to the TLEs.
*/
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);
forboth(name, names, var, vars)
{
char *label = strVal(lfirst(name));
Node *varnode = (Node *) lfirst(var);
TargetEntry *te;
te = makeTargetEntry((Expr *) varnode,
(AttrNumber) pstate->p_next_resno++,
label,
false);
te_list = lappend(te_list, te);
}
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.
*/
const char *
get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum)
{
const char *name;
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));
/*
* CDB: Pseudo columns have negative attribute numbers below the
* lowest system attribute number.
*/
if (attnum <= FirstLowInvalidHeapAttributeNumber)
{
CdbRelColumnInfo *rci = cdb_rte_find_pseudo_column(rte, attnum);
if (!rci)
goto bogus;
return rci->colname;
}
/*
* 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 (rte->eref != NULL &&
attnum > 0 &&
attnum <= list_length(rte->eref->colnames))
return strVal(list_nth(rte->eref->colnames, attnum - 1));
/* CDB: Get name of sysattr even if relid is no good (e.g. SubqueryScan) */
if (attnum < 0 &&
attnum > FirstLowInvalidHeapAttributeNumber)
{
Form_pg_attribute att_tup = SystemAttributeDefinition(attnum, true);
return NameStr(att_tup->attname);
}
bogus:
/* else caller gave us a bogus attnum */
name = (rte->eref && rte->eref->aliasname) ? rte->eref->aliasname
: "*BOGUS*";
ereport(WARNING, (errcode(ERRCODE_INTERNAL_ERROR),
errmsg_internal("invalid attnum %d for rangetable entry %s",
attnum, name) ));
return "*BOGUS*";
}
static bool get_attisdropped(Oid relid, int attnum)
{
HeapTuple tp;
Form_pg_attribute att_tup;
bool result = false;
cqContext *pcqCtx;
/* SELECT attisdropped FROM pg_attribute */
pcqCtx = caql_beginscan(
NULL,
cql("SELECT * FROM pg_attribute "
" WHERE attrelid = :1 "
" AND attnum = :2 ",
ObjectIdGetDatum(relid),
Int16GetDatum(attnum)));
tp = caql_getnext(pcqCtx);
if (!HeapTupleIsValid(tp)) /* shouldn't happen */
elog(ERROR, "cache lookup failed for attribute %d of relation %u",
attnum, relid);
att_tup = (Form_pg_attribute) GETSTRUCT(tp);
result = att_tup->attisdropped;
caql_endscan(pcqCtx);
return (result);
}
/*
* 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;
cqContext *pcqCtx;
pcqCtx = caql_beginscan(
NULL,
cql("SELECT * FROM pg_attribute "
" WHERE attrelid = :1 "
" AND attnum = :2 ",
ObjectIdGetDatum(rte->relid),
Int16GetDatum(attnum)));
tp = caql_getnext(pcqCtx);
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)),
errOmitLocation(true)));
*vartype = att_tup->atttypid;
*vartypmod = att_tup->atttypmod;
caql_endscan(pcqCtx);
}
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_TABLEFUNCTION:
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:
{
result = get_attisdropped(rte->relid, attnum);
}
break;
case RTE_SUBQUERY:
case RTE_VALUES:
case RTE_CTE:
/* Subselect and Values 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_TABLEFUNCTION:
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
*/
result = get_attisdropped(funcrelid, attnum);
}
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, int location)
{
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 /*catalogname*/, NULL, rte->eref->aliasname, 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)),
errOmitLocation(true),
parser_errposition(pstate, location)));
else
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_TABLE),
(pstate->parentParseState ?
errmsg("missing FROM-clause entry in subquery for table \"%s\"",
relation->relname) :
errmsg("missing FROM-clause entry for table \"%s\"",
relation->relname)),
errOmitLocation(true),
parser_errposition(pstate, location)));
}
else
{
/* just issue a warning */
ereport(NOTICE,
(errcode(ERRCODE_UNDEFINED_TABLE),
(pstate->parentParseState ?
errmsg("adding missing FROM-clause entry in subquery for table \"%s\"",
relation->relname) :
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)),
errOmitLocation(true),
parser_errposition(pstate, location)));
}
}
/*
* ExecCheckRTPerms
* Check access permissions for all relations listed in a range table.
*/
void
ExecCheckRTPerms(List *rangeTable)
{
if (aclType == HAWQ_ACL_RANGER)
{
if(rangeTable!=NULL)
ExecCheckRTPermsWithRanger(rangeTable);
return;
}
ListCell *l;
foreach(l, rangeTable)
{
ExecCheckRTEPerms((RangeTblEntry *) lfirst(l));
}
}
/*
* ExecCheckRTPerms
* Batch implementation: Check access permissions for all relations
* listed in a range table with acl_type is ranger.
*/
void
ExecCheckRTPermsWithRanger(List *rangeTable)
{
List *ranger_check_args = NIL;
ListCell *l;
foreach(l, rangeTable)
{
AclMode requiredPerms;
Oid relOid;
Oid userid;
RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
if (rte->rtekind != RTE_RELATION)
continue;
requiredPerms = rte->requiredPerms;
if (requiredPerms == 0)
continue;
bool ret = fallBackToNativeCheck(ACL_KIND_CLASS, rte->relid, GetUserId(), ACL_NO_RIGHTS);
if (ret) {
ExecCheckRTEPerms((RangeTblEntry *) lfirst(l));
continue;
}
relOid = rte->relid;
userid = rte->checkAsUser ? rte->checkAsUser : GetUserId();
RangerPrivilegeArgs *ranger_check_arg = (RangerPrivilegeArgs *) palloc(sizeof(RangerPrivilegeArgs));
ranger_check_arg->objkind = ACL_KIND_CLASS;
ranger_check_arg->object_oid = relOid;
ranger_check_arg->roleid = userid;
ranger_check_arg->mask = requiredPerms;
ranger_check_arg->how = ACLMASK_ALL;
ranger_check_args = lappend(ranger_check_args, ranger_check_arg);
}
if (ranger_check_args == NIL)
return;
/* ranger ACL check with package Oids */
List *aclresults = NIL;
aclresults = pg_rangercheck_batch(ranger_check_args);
if (aclresults == NIL)
{
elog(ERROR, "ACL check failed\n");
return;
}
/* check result */
StringInfoData acl_fail_msg;
bool acl_allok = true;
ListCell *result;
foreach(result, aclresults)
{
RangerPrivilegeResults *result_ptr = (RangerPrivilegeResults *) lfirst(result);
if(result_ptr->result != RANGERCHECK_OK)
{
if (acl_allok)
{
initStringInfo(&acl_fail_msg);
appendStringInfo(&acl_fail_msg, "permission denied for relation(s): ");
}
else
{
appendStringInfo(&acl_fail_msg, ", ");
}
acl_allok = false;
/* collect all acl fail relations */
Oid relOid = result_ptr->relOid;
const char *rel_name = get_rel_name_partition(relOid);
const char *namespace_name = get_namespace_name(get_rel_namespace(relOid));
appendStringInfo(&acl_fail_msg, "%s.%s", namespace_name, rel_name);
}
}
if (ranger_check_args)
{
list_free_deep(ranger_check_args);
ranger_check_args = NIL;
}
if (aclresults)
{
list_free_deep(aclresults);
aclresults = NIL;
}
if (!acl_allok)
{
errstart(ERROR, __FILE__, __LINE__, PG_FUNCNAME_MACRO, TEXTDOMAIN);
errmsg("%s", acl_fail_msg.data),
pfree(acl_fail_msg.data);
errfinish(errOmitLocation(true));
}
}
/*
* ExecCheckRTEPerms
* Check access permissions for a single RTE.
*/
void
ExecCheckRTEPerms(RangeTblEntry *rte)
{
AclMode requiredPerms;
Oid relOid;
Oid userid;
/*
* Only plain-relation RTEs need to be checked here. Function RTEs are
* checked by init_fcache when the function is prepared for execution.
* Join, subquery, and special RTEs need no checks.
*/
if (rte->rtekind != RTE_RELATION)
return;
/*
* No work if requiredPerms is empty.
*/
requiredPerms = rte->requiredPerms;
if (requiredPerms == 0)
return;
relOid = rte->relid;
/*
* userid to check as: current user unless we have a setuid indication.
*
* Note: GetUserId() is presently fast enough that there's no harm in
* calling it separately for each RTE. If that stops being true, we could
* call it once in ExecCheckRTPerms and pass the userid down from there.
* But for now, no need for the extra clutter.
*/
userid = rte->checkAsUser ? rte->checkAsUser : GetUserId();
/*
* We must have *all* the requiredPerms bits, so use aclmask not aclcheck.
*/
if (pg_class_aclmask(relOid, userid, requiredPerms, ACLMASK_ALL)
!= requiredPerms) {
/*
* If the table is a partition, return an error message that includes
* the name of the parent table.
*/
const char *rel_name = get_rel_name_partition(relOid);
aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_CLASS, rel_name);
}
}