aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser/parse_relation.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/parser/parse_relation.c')
-rw-r--r--src/backend/parser/parse_relation.c357
1 files changed, 224 insertions, 133 deletions
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index 9440914a776..1609c89ce07 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.62 2002/03/06 06:09:55 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.63 2002/03/12 00:51:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -36,38 +36,41 @@ static Node *scanNameSpaceForRefname(ParseState *pstate, Node *nsnode,
char *refname);
static Node *scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte,
char *colname);
-static Node *scanJoinForColumn(JoinExpr *join, char *colname,
- int sublevels_up);
static bool isForUpdate(ParseState *pstate, char *relname);
-static List *expandNamesVars(ParseState *pstate, List *names, List *vars);
static int specialAttNum(char *a);
static void warnAutoRange(ParseState *pstate, char *refname);
/*
- * refnameRangeOrJoinEntry
- * Given a refname, look to see if it matches any RTE or join table.
- * If so, return a pointer to the RangeTblEntry or JoinExpr.
+ * refnameRangeTblEntry
+ * Given a refname, look to see if it matches any RTE.
+ * If so, return a pointer to the RangeTblEntry.
* Optionally get its nesting depth (0 = current). If sublevels_up
* is NULL, only consider items at the current nesting level.
*/
-Node *
-refnameRangeOrJoinEntry(ParseState *pstate,
- char *refname,
- int *sublevels_up)
+RangeTblEntry *
+refnameRangeTblEntry(ParseState *pstate,
+ char *refname,
+ int *sublevels_up)
{
if (sublevels_up)
*sublevels_up = 0;
while (pstate != NULL)
{
- Node *rte;
+ Node *nsnode;
- rte = scanNameSpaceForRefname(pstate,
- (Node *) pstate->p_namespace,
- refname);
- if (rte)
- return rte;
+ nsnode = scanNameSpaceForRefname(pstate,
+ (Node *) pstate->p_namespace,
+ refname);
+ if (nsnode)
+ {
+ /* should get an RTE or JoinExpr */
+ if (IsA(nsnode, RangeTblEntry))
+ return (RangeTblEntry *) nsnode;
+ Assert(IsA(nsnode, JoinExpr));
+ return rt_fetch(((JoinExpr *) nsnode)->rtindex, pstate->p_rtable);
+ }
pstate = pstate->parentParseState;
if (sublevels_up)
@@ -247,6 +250,12 @@ RTERangeTablePosn(ParseState *pstate, RangeTblEntry *rte, int *sublevels_up)
*
* 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 checkForRead from the beginning.
*/
static Node *
scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
@@ -279,10 +288,9 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
return result;
/*
- * If the RTE represents a table (not a sub-select), consider system
- * column names.
+ * If the RTE represents a real table, consider system column names.
*/
- if (rte->relid != InvalidOid)
+ if (rte->rtekind == RTE_RELATION)
{
/* quick check to see if name could be a system column */
attnum = specialAttNum(colname);
@@ -304,44 +312,6 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
}
/*
- * scanJoinForColumn
- * Search the column names of a single join table for the given name.
- * If found, return an appropriate Var node or expression, else return NULL.
- * If the name proves ambiguous within this jointable, raise error.
- *
- * NOTE: unlike scanRTEForColumn, there's no need to worry about forcing
- * checkForRead true for the referenced tables. This is so because a join
- * expression can only appear in a FROM clause, and any table named in
- * FROM will be marked checkForRead from the beginning.
- */
-static Node *
-scanJoinForColumn(JoinExpr *join, char *colname, int sublevels_up)
-{
- Node *result = NULL;
- int attnum = 0;
- List *c;
-
- foreach(c, join->colnames)
- {
- attnum++;
- if (strcmp(strVal(lfirst(c)), colname) == 0)
- {
- if (result)
- elog(ERROR, "Column reference \"%s\" is ambiguous", colname);
- result = copyObject(nth(attnum - 1, join->colvars));
-
- /*
- * If referencing an uplevel join item, we must adjust
- * sublevels settings in the copied expression.
- */
- if (sublevels_up > 0)
- IncrementVarSublevelsUp(result, sublevels_up, 0);
- }
- }
- return result;
-}
-
-/*
* colnameToVar
* Search for an unqualified column name.
* If found, return the appropriate Var node (or expression).
@@ -382,9 +352,13 @@ colnameToVar(ParseState *pstate, char *colname)
}
else if (IsA(nsnode, JoinExpr))
{
- JoinExpr *j = (JoinExpr *) nsnode;
+ int varno = ((JoinExpr *) nsnode)->rtindex;
+ RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable);
- newresult = scanJoinForColumn(j, colname, levels_up);
+ /* joins are always inFromCl, so no need to check */
+
+ /* use orig_pstate here to get the right sublevels_up */
+ newresult = scanRTEForColumn(orig_pstate, rte, colname);
}
else
elog(ERROR, "colnameToVar: unexpected node type %d",
@@ -412,41 +386,26 @@ colnameToVar(ParseState *pstate, char *colname)
/*
* qualifiedNameToVar
* Search for a qualified column name (refname + column name).
- * If found, return the appropriate Var node (or expression).
+ * If found, return the appropriate Var node.
* If not found, return NULL. If the name proves ambiguous, raise error.
*/
Node *
qualifiedNameToVar(ParseState *pstate, char *refname, char *colname,
bool implicitRTEOK)
{
- Node *result;
- Node *rteorjoin;
+ RangeTblEntry *rte;
int sublevels_up;
- rteorjoin = refnameRangeOrJoinEntry(pstate, refname, &sublevels_up);
+ rte = refnameRangeTblEntry(pstate, refname, &sublevels_up);
- if (rteorjoin == NULL)
+ if (rte == NULL)
{
if (!implicitRTEOK)
return NULL;
- rteorjoin = (Node *) addImplicitRTE(pstate, refname);
- sublevels_up = 0;
+ rte = addImplicitRTE(pstate, refname);
}
- if (IsA(rteorjoin, RangeTblEntry))
- result = scanRTEForColumn(pstate, (RangeTblEntry *) rteorjoin,
- colname);
- else if (IsA(rteorjoin, JoinExpr))
- result = scanJoinForColumn((JoinExpr *) rteorjoin,
- colname, sublevels_up);
- else
- {
- elog(ERROR, "qualifiedNameToVar: unexpected node type %d",
- nodeTag(rteorjoin));
- result = NULL; /* keep compiler quiet */
- }
-
- return result;
+ return scanRTEForColumn(pstate, rte, colname);
}
/*
@@ -474,9 +433,9 @@ addRangeTableEntry(ParseState *pstate,
int numaliases;
int varattno;
+ rte->rtekind = RTE_RELATION;
rte->relname = relname;
rte->alias = alias;
- rte->subquery = NULL;
/*
* Get the rel's OID. This access also ensures that we have an
@@ -563,6 +522,7 @@ addRangeTableEntryForSubquery(ParseState *pstate,
int varattno;
List *tlistitem;
+ rte->rtekind = RTE_SUBQUERY;
rte->relname = NULL;
rte->relid = InvalidOid;
rte->subquery = subquery;
@@ -622,6 +582,76 @@ addRangeTableEntryForSubquery(ParseState *pstate,
}
/*
+ * 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 *coltypes,
+ List *coltypmods,
+ List *leftcols,
+ List *rightcols,
+ Attr *alias,
+ bool inFromCl)
+{
+ RangeTblEntry *rte = makeNode(RangeTblEntry);
+ Attr *eref;
+ int numaliases;
+
+ rte->rtekind = RTE_JOIN;
+ rte->relname = NULL;
+ rte->relid = InvalidOid;
+ rte->subquery = NULL;
+ rte->jointype = jointype;
+ rte->joincoltypes = coltypes;
+ rte->joincoltypmods = coltypmods;
+ rte->joinleftcols = leftcols;
+ rte->joinrightcols = rightcols;
+ rte->alias = alias;
+
+ eref = alias ? (Attr *) copyObject(alias) : makeAttr("unnamed_join", NULL);
+ numaliases = length(eref->attrs);
+
+ /* fill in any unspecified alias columns */
+ if (numaliases < length(colnames))
+ {
+ while (numaliases-- > 0)
+ colnames = lnext(colnames);
+ eref->attrs = nconc(eref->attrs, colnames);
+ }
+
+ 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 read/write access rights.
+ *
+ * Joins are never checked for access rights.
+ *----------
+ */
+ rte->inh = false; /* never true for joins */
+ rte->inFromCl = inFromCl;
+ rte->checkForRead = false;
+ rte->checkForWrite = false;
+
+ 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 relname been selected FOR UPDATE?
*/
static bool
@@ -720,15 +750,16 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
/* Need the RT index of the entry for creating Vars */
rtindex = RTERangeTablePosn(pstate, rte, &sublevels_up);
- if (rte->relname)
+ if (rte->rtekind == RTE_RELATION)
{
/* Ordinary relation RTE */
Relation rel;
int maxattrs;
+ int numaliases;
rel = heap_openr(rte->relname, AccessShareLock);
-
maxattrs = RelationGetNumberOfAttributes(rel);
+ numaliases = length(rte->eref->attrs);
for (varattno = 0; varattno < maxattrs; varattno++)
{
@@ -743,7 +774,7 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
{
char *label;
- if (varattno < length(rte->eref->attrs))
+ if (varattno < numaliases)
label = strVal(nth(varattno, rte->eref->attrs));
else
label = NameStr(attr->attname);
@@ -764,7 +795,7 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
heap_close(rel, AccessShareLock);
}
- else
+ else if (rte->rtekind == RTE_SUBQUERY)
{
/* Subquery RTE */
List *aliasp = rte->eref->attrs;
@@ -802,56 +833,63 @@ expandRTE(ParseState *pstate, RangeTblEntry *rte,
}
}
}
-}
+ else if (rte->rtekind == RTE_JOIN)
+ {
+ /* Join RTE */
+ List *aliasp = rte->eref->attrs;
+ List *coltypes = rte->joincoltypes;
+ List *coltypmods = rte->joincoltypmods;
-/*
- * expandRelAttrs -
- * makes a list of TargetEntry nodes for the attributes of the rel
- */
-List *
-expandRelAttrs(ParseState *pstate, RangeTblEntry *rte)
-{
- List *name_list,
- *var_list;
+ varattno = 0;
+ while (aliasp)
+ {
+ Assert(coltypes && coltypmods);
+ varattno++;
- expandRTE(pstate, rte, &name_list, &var_list);
+ if (colnames)
+ {
+ char *label = strVal(lfirst(aliasp));
- return expandNamesVars(pstate, name_list, var_list);
-}
+ *colnames = lappend(*colnames, makeString(pstrdup(label)));
+ }
-/*
- * expandJoinAttrs -
- * makes a list of TargetEntry nodes for the attributes of the join
- */
-List *
-expandJoinAttrs(ParseState *pstate, JoinExpr *join, int sublevels_up)
-{
- List *vars;
+ if (colvars)
+ {
+ Var *varnode;
- vars = copyObject(join->colvars);
+ varnode = makeVar(rtindex, varattno,
+ (Oid) lfirsti(coltypes),
+ (int32) lfirsti(coltypmods),
+ sublevels_up);
- /*
- * If referencing an uplevel join item, we must adjust sublevels
- * settings in the copied expression.
- */
- if (sublevels_up > 0)
- IncrementVarSublevelsUp((Node *) vars, sublevels_up, 0);
+ *colvars = lappend(*colvars, varnode);
+ }
- return expandNamesVars(pstate,
- copyObject(join->colnames),
- vars);
+ aliasp = lnext(aliasp);
+ coltypes = lnext(coltypes);
+ coltypmods = lnext(coltypmods);
+ }
+ Assert(coltypes == NIL && coltypmods == NIL);
+ }
+ else
+ elog(ERROR, "expandRTE: unsupported RTE kind %d",
+ (int) rte->rtekind);
}
/*
- * expandNamesVars -
- * Workhorse for "*" expansion: produce a list of targetentries
- * given lists of column names (as String nodes) and var references.
+ * expandRelAttrs -
+ * Workhorse for "*" expansion: produce a list of targetentries
+ * for the attributes of the rte
*/
-static List *
-expandNamesVars(ParseState *pstate, List *names, List *vars)
+List *
+expandRelAttrs(ParseState *pstate, RangeTblEntry *rte)
{
+ List *names,
+ *vars;
List *te_list = NIL;
+ expandRTE(pstate, rte, &names, &vars);
+
while (names)
{
char *label = strVal(lfirst(names));
@@ -875,22 +913,16 @@ expandNamesVars(ParseState *pstate, List *names, List *vars)
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, whereas
+ * 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.
- *
- * XXX Actually, this is completely bogus, because refnames of RTEs are
- * not guaranteed unique, and may not even have scope across the whole
- * query. Cleanest fix would be to add refname/attname to Var nodes and
- * just print those, rather than indulging in this hack.
- * ----------
*/
char *
get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum)
@@ -901,7 +933,8 @@ get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum)
return "*";
/*
- * If there is an alias, use it
+ * If there is an alias, use it. (This path should always be taken
+ * for non-relation RTEs.)
*/
if (attnum > 0 && attnum <= length(rte->eref->attrs))
return strVal(nth(attnum - 1, rte->eref->attrs));
@@ -909,9 +942,9 @@ get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum)
/*
* Can get here for a system attribute (which never has an alias), or
* if alias name list is too short (which probably can't happen
- * anymore). Neither of these cases is valid for a subselect RTE.
+ * anymore). Neither of these cases is valid for a non-relation RTE.
*/
- if (rte->relid == InvalidOid)
+ if (rte->rtekind != RTE_RELATION)
elog(ERROR, "Invalid attnum %d for rangetable entry %s",
attnum, rte->eref->relname);
@@ -926,6 +959,64 @@ get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum)
}
/*
+ * get_rte_attribute_type
+ * Get attribute type information from a RangeTblEntry
+ */
+void
+get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
+ Oid *vartype, int32 *vartypmod)
+{
+ if (rte->rtekind == 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);
+ /* this shouldn't happen... */
+ if (!HeapTupleIsValid(tp))
+ elog(ERROR, "Relation %s does not have attribute %d",
+ rte->relname, attnum);
+ att_tup = (Form_pg_attribute) GETSTRUCT(tp);
+ *vartype = att_tup->atttypid;
+ *vartypmod = att_tup->atttypmod;
+ ReleaseSysCache(tp);
+ }
+ else if (rte->rtekind == RTE_SUBQUERY)
+ {
+ /* Subselect RTE --- get type info from subselect's tlist */
+ List *tlistitem;
+
+ foreach(tlistitem, rte->subquery->targetList)
+ {
+ TargetEntry *te = (TargetEntry *) lfirst(tlistitem);
+
+ if (te->resdom->resjunk || te->resdom->resno != attnum)
+ continue;
+ *vartype = te->resdom->restype;
+ *vartypmod = te->resdom->restypmod;
+ return;
+ }
+ /* falling off end of list shouldn't happen... */
+ elog(ERROR, "Subquery %s does not have attribute %d",
+ rte->eref->relname, attnum);
+ }
+ else if (rte->rtekind == RTE_JOIN)
+ {
+ /* Join RTE --- get type info directly from join RTE */
+ Assert(attnum > 0 && attnum <= length(rte->joincoltypes));
+ *vartype = (Oid) nthi(attnum-1, rte->joincoltypes);
+ *vartypmod = nthi(attnum-1, rte->joincoltypmods);
+ }
+ else
+ elog(ERROR, "get_rte_attribute_type: unsupported RTE kind %d",
+ (int) rte->rtekind);
+}
+
+/*
* given relation and att name, return id of variable
*
* This should only be used if the relation is already