*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.159 2001/02/12 20:07:21 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.160 2001/02/14 21:34:59 tgl Exp $
*
*
* INTERFACE ROUTINES
bool updatePgAttribute)
{
Node *expr;
- RangeTblEntry *rte;
char *adsrc;
Relation adrel;
Relation idescs[Num_pg_attrdef_indices];
expr = stringToNode(adbin);
/*
- * deparse_expression needs a RangeTblEntry list, so make one
+ * deparse it
*/
- rte = makeNode(RangeTblEntry);
- rte->relname = RelationGetRelationName(rel);
- rte->relid = RelationGetRelid(rel);
- rte->eref = makeNode(Attr);
- rte->eref->relname = RelationGetRelationName(rel);
- rte->inh = false;
- rte->inFromCl = true;
- adsrc = deparse_expression(expr, makeList1(makeList1(rte)), false);
+ adsrc = deparse_expression(expr,
+ deparse_context_for(RelationGetRelationName(rel),
+ RelationGetRelid(rel)),
+ false);
values[Anum_pg_attrdef_adrelid - 1] = RelationGetRelid(rel);
values[Anum_pg_attrdef_adnum - 1] = attnum;
StoreRelCheck(Relation rel, char *ccname, char *ccbin)
{
Node *expr;
- RangeTblEntry *rte;
char *ccsrc;
Relation rcrel;
Relation idescs[Num_pg_relcheck_indices];
expr = (Node *) make_ands_explicit((List *) expr);
/*
- * deparse_expression needs a RangeTblEntry list, so make one
+ * deparse it
*/
- rte = makeNode(RangeTblEntry);
- rte->relname = RelationGetRelationName(rel);
- rte->relid = RelationGetRelid(rel);
- rte->eref = makeNode(Attr);
- rte->eref->relname = RelationGetRelationName(rel);
- rte->inh = false;
- rte->inFromCl = true;
- ccsrc = deparse_expression(expr, makeList1(makeList1(rte)), false);
+ ccsrc = deparse_expression(expr,
+ deparse_context_for(RelationGetRelationName(rel),
+ RelationGetRelid(rel)),
+ false);
values[Anum_pg_relcheck_rcrelid - 1] = RelationGetRelid(rel);
values[Anum_pg_relcheck_rcname - 1] = DirectFunctionCall1(namein,
* sole rangetable entry. We need a ParseState for transformExpr.
*/
pstate = make_parsestate(NULL);
- makeRangeTable(pstate, NULL);
rte = addRangeTableEntry(pstate, relname, NULL, false, true);
- addRTEtoJoinList(pstate, rte);
+ addRTEtoQuery(pstate, rte, true, true);
/*
* Process column default expressions.
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.120 2001/01/29 00:39:20 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.121 2001/02/14 21:35:00 tgl Exp $
*
* NOTES
* The PerformAddAttribute() code, like most of the relation
* the expression we can pass to ExecQual
*/
pstate = make_parsestate(NULL);
- makeRangeTable(pstate, NULL);
rte = addRangeTableEntry(pstate, relationName, NULL,
false, true);
- addRTEtoJoinList(pstate, rte);
+ addRTEtoQuery(pstate, rte, true, true);
/* Convert the A_EXPR in raw_expr into an EXPR */
expr = transformExpr(pstate, constr->raw_expr,
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: analyze.c,v 1.178 2001/01/27 07:23:48 tgl Exp $
+ * $Id: analyze.c,v 1.179 2001/02/14 21:35:01 tgl Exp $
*
*-------------------------------------------------------------------------
*/
qry->commandType = CMD_DELETE;
- /* set up a range table */
- lockTargetTable(pstate, stmt->relname);
- makeRangeTable(pstate, NIL);
- setTargetTable(pstate, stmt->relname,
- interpretInhOption(stmt->inhOpt), true);
+ /* set up range table with just the result rel */
+ qry->resultRelation = setTargetTable(pstate, stmt->relname,
+ interpretInhOption(stmt->inhOpt),
+ true);
qry->distinctClause = NIL;
/* done building the range table and jointree */
qry->rtable = pstate->p_rtable;
qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
- qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
qry->hasSubLinks = pstate->p_hasSubLinks;
qry->hasAggs = pstate->p_hasAggs;
transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
{
Query *qry = makeNode(Query);
+ List *sub_rtable;
+ List *sub_namespace;
List *icolumns;
List *attrnos;
List *attnos;
pstate->p_is_insert = true;
/*
- * Must get write lock on target table before scanning SELECT,
+ * If a non-nil rangetable/namespace was passed in, and we are doing
+ * INSERT/SELECT, arrange to pass the rangetable/namespace down to the
+ * SELECT. This can only happen if we are inside a CREATE RULE,
+ * and in that case we want the rule's OLD and NEW rtable entries to
+ * appear as part of the SELECT's rtable, not as outer references for
+ * it. (Kluge!) The SELECT's joinlist is not affected however.
+ * We must do this before adding the target table to the INSERT's rtable.
+ */
+ if (stmt->selectStmt)
+ {
+ sub_rtable = pstate->p_rtable;
+ pstate->p_rtable = NIL;
+ sub_namespace = pstate->p_namespace;
+ pstate->p_namespace = NIL;
+ }
+ else
+ {
+ sub_rtable = NIL; /* not used, but keep compiler quiet */
+ sub_namespace = NIL;
+ }
+
+ /*
+ * Must get write lock on INSERT target table before scanning SELECT,
* else we will grab the wrong kind of initial lock if the target
- * table is also mentioned in the SELECT part.
+ * table is also mentioned in the SELECT part. Note that the target
+ * table is not added to the joinlist or namespace.
*/
- lockTargetTable(pstate, stmt->relname);
+ qry->resultRelation = setTargetTable(pstate, stmt->relname,
+ false, false);
/*
* Is it INSERT ... SELECT or INSERT ... VALUES?
* otherwise the behavior of SELECT within INSERT might be different
* from a stand-alone SELECT. (Indeed, Postgres up through 6.5 had
* bugs of just that nature...)
- *
- * If a non-nil rangetable was passed in, pass it down to the SELECT.
- * This can only happen if we are inside a CREATE RULE, and in that
- * case we want the rule's OLD and NEW rtable entries to appear as
- * part of the SELECT's rtable, not as outer references for it.
*/
- sub_pstate->p_rtable = pstate->p_rtable;
- pstate->p_rtable = NIL;
+ sub_pstate->p_rtable = sub_rtable;
+ sub_pstate->p_namespace = sub_namespace;
+
selectQuery = transformStmt(sub_pstate, stmt->selectStmt);
+
release_pstate_resources(sub_pstate);
pfree(sub_pstate);
elog(ERROR, "INSERT ... SELECT may not specify INTO");
/*
* Make the source be a subquery in the INSERT's rangetable,
- * and add it to the joinlist.
+ * and add it to the INSERT's joinlist.
*/
rte = addRangeTableEntryForSubquery(pstate,
selectQuery,
/*
* Now we are done with SELECT-like processing, and can get on with
* transforming the target list to match the INSERT target columns.
- *
- * In particular, it's time to add the INSERT target to the rangetable.
- * (We didn't want it there until now since it shouldn't be visible in
- * the SELECT part.) Note that the INSERT target is NOT added to the
- * joinlist, since we don't want to join over it.
*/
- setTargetTable(pstate, stmt->relname, false, false);
/* Prepare to assign non-conflicting resnos to resjunk attributes */
if (pstate->p_last_resno <= pstate->p_target_relation->rd_rel->relnatts)
/* done building the range table and jointree */
qry->rtable = pstate->p_rtable;
qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
- qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
qry->hasSubLinks = pstate->p_hasSubLinks;
qry->hasAggs = pstate->p_hasAggs;
oldrte->checkForRead = false;
newrte->checkForRead = false;
/*
- * They must be in the joinlist too for lookup purposes, but only add
+ * They must be in the namespace too for lookup purposes, but only add
* the one(s) that are relevant for the current kind of rule. In an
* UPDATE rule, quals must refer to OLD.field or NEW.field to be
* unambiguous, but there's no need to be so picky for INSERT & DELETE.
* (Note we marked the RTEs "inFromCl = true" above to allow unqualified
- * references to their fields.)
+ * references to their fields.) We do not add them to the joinlist.
*/
switch (stmt->event)
{
case CMD_SELECT:
- addRTEtoJoinList(pstate, oldrte);
+ addRTEtoQuery(pstate, oldrte, false, true);
break;
case CMD_UPDATE:
- addRTEtoJoinList(pstate, oldrte);
- addRTEtoJoinList(pstate, newrte);
+ addRTEtoQuery(pstate, oldrte, false, true);
+ addRTEtoQuery(pstate, newrte, false, true);
break;
case CMD_INSERT:
- addRTEtoJoinList(pstate, newrte);
+ addRTEtoQuery(pstate, newrte, false, true);
break;
case CMD_DELETE:
- addRTEtoJoinList(pstate, oldrte);
+ addRTEtoQuery(pstate, oldrte, false, true);
break;
default:
elog(ERROR, "transformRuleStmt: unexpected event type %d",
* Set up OLD/NEW in the rtable for this statement. The entries
* are marked not inFromCl because we don't want them to be
* referred to by unqualified field names nor "*" in the rule
- * actions. We don't need to add them to the joinlist for
- * qualified-name lookup, either (see qualifiedNameToVar()).
+ * actions. We must add them to the namespace, however, or they
+ * won't be accessible at all. We decide later whether to put
+ * them in the joinlist.
*/
oldrte = addRangeTableEntry(sub_pstate, stmt->object->relname,
makeAttr("*OLD*", NULL),
false, false);
oldrte->checkForRead = false;
newrte->checkForRead = false;
+ addRTEtoQuery(sub_pstate, oldrte, false, true);
+ addRTEtoQuery(sub_pstate, newrte, false, true);
/* Transform the rule action statement */
top_subqry = transformStmt(sub_pstate, lfirst(actions));
*/
if (has_old || (has_new && stmt->event == CMD_UPDATE))
{
- /* hack so we can use addRTEtoJoinList() */
+ /* hack so we can use addRTEtoQuery() */
sub_pstate->p_rtable = sub_qry->rtable;
sub_pstate->p_joinlist = sub_qry->jointree->fromlist;
- addRTEtoJoinList(sub_pstate, oldrte);
+ addRTEtoQuery(sub_pstate, oldrte, true, false);
sub_qry->jointree->fromlist = sub_pstate->p_joinlist;
}
/* make FOR UPDATE clause available to addRangeTableEntry */
pstate->p_forUpdate = stmt->forUpdate;
- /* set up a range table */
- makeRangeTable(pstate, stmt->fromClause);
+ /* process the FROM clause */
+ transformFromClause(pstate, stmt->fromClause);
/* transform targetlist and WHERE */
qry->targetList = transformTargetList(pstate, stmt->targetList);
if (isLeaf)
{
/* Process leaf SELECT */
- List *save_rtable;
List *selectList;
Query *selectQuery;
char selectName[32];
RangeTblRef *rtr;
/*
- * Transform SelectStmt into a Query. We do not want any previously
- * transformed leaf queries to be visible in the outer context of
- * this sub-query, so temporarily make the top-level pstate have an
- * empty rtable. (We needn't do the same with the joinlist because
- * we aren't entering anything in the top-level joinlist.)
+ * Transform SelectStmt into a Query.
+ *
+ * Note: previously transformed sub-queries don't affect the parsing
+ * of this sub-query, because they are not in the toplevel pstate's
+ * namespace list.
*/
- save_rtable = pstate->p_rtable;
- pstate->p_rtable = NIL;
selectList = parse_analyze((Node *) stmt, pstate);
- pstate->p_rtable = save_rtable;
Assert(length(selectList) == 1);
selectQuery = (Query *) lfirst(selectList);
qry->commandType = CMD_UPDATE;
pstate->p_is_update = true;
+ qry->resultRelation = setTargetTable(pstate, stmt->relname,
+ interpretInhOption(stmt->inhOpt),
+ true);
+
/*
* the FROM clause is non-standard SQL syntax. We used to be able to
* do this with REPLACE in POSTQUEL so we keep the feature.
- *
- * Note: it's critical here that we process FROM before adding the
- * target table to the rtable --- otherwise, if the target is also
- * used in FROM, we'd fail to notice that it should be marked
- * checkForRead as well as checkForWrite. See setTargetTable().
*/
- lockTargetTable(pstate, stmt->relname);
- makeRangeTable(pstate, stmt->fromClause);
- setTargetTable(pstate, stmt->relname,
- interpretInhOption(stmt->inhOpt), true);
+ transformFromClause(pstate, stmt->fromClause);
qry->targetList = transformTargetList(pstate, stmt->targetList);
qry->rtable = pstate->p_rtable;
qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
- qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
qry->hasSubLinks = pstate->p_hasSubLinks;
qry->hasAggs = pstate->p_hasAggs;
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.75 2001/01/24 19:43:01 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.76 2001/02/14 21:35:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/*
- * makeRangeTable -
- * Build the initial range table from the FROM clause.
+ * transformFromClause -
+ * Process the FROM clause and add items to the query's range table,
+ * joinlist, and namespace.
*
- * The range table constructed here may grow as we transform the expressions
- * in the query's quals and target list. (Note that this happens because in
- * POSTQUEL, we allow references to relations not specified in the
- * from-clause. PostgreSQL keeps this extension to standard SQL.)
+ * Note: we assume that pstate's p_rtable, p_joinlist, and p_namespace lists
+ * were initialized to NIL when the pstate was created. We will add onto
+ * any entries already present --- this is needed for rule processing, as
+ * well as for UPDATE and DELETE.
*
- * Note: we assume that pstate's p_rtable and p_joinlist lists were
- * initialized to NIL when the pstate was created. We will add onto
- * any entries already present --- this is needed for rule processing!
+ * The range table may grow still further when we transform the expressions
+ * in the query's quals and target list. (This is possible because in
+ * POSTQUEL, we allowed references to relations not specified in the
+ * from-clause. PostgreSQL keeps this extension to standard SQL.)
*/
void
-makeRangeTable(ParseState *pstate, List *frmList)
+transformFromClause(ParseState *pstate, List *frmList)
{
List *fl;
/*
* The grammar will have produced a list of RangeVars, RangeSubselects,
- * and/or JoinExprs. Transform each one, and then add it to the joinlist.
+ * and/or JoinExprs. Transform each one (possibly adding entries to the
+ * rtable), check for duplicate refnames, and then add it to the joinlist
+ * and namespace.
*/
foreach(fl, frmList)
{
List *containedRels;
n = transformFromClauseItem(pstate, n, &containedRels);
+ checkNameSpaceConflicts(pstate, (Node *) pstate->p_namespace, n);
pstate->p_joinlist = lappend(pstate->p_joinlist, n);
+ pstate->p_namespace = lappend(pstate->p_namespace, n);
}
}
/*
- * lockTargetTable
- * Find the target relation of INSERT/UPDATE/DELETE and acquire write
- * lock on it. This must be done before building the range table,
- * in case the target is also mentioned as a source relation --- we
- * want to be sure to grab the write lock before any read lock.
+ * setTargetTable
+ * Add the target relation of INSERT/UPDATE/DELETE to the range table,
+ * and make the special links to it in the ParseState.
+ *
+ * We also open the target relation and acquire a write lock on it.
+ * This must be done before processing the FROM list, in case the target
+ * is also mentioned as a source relation --- we want to be sure to grab
+ * the write lock before any read lock.
*
- * The ParseState's link to the target relcache entry is also set here.
+ * If alsoSource is true, add the target to the query's joinlist and
+ * namespace. For INSERT, we don't want the target to be joined to;
+ * it's a destination of tuples, not a source. For UPDATE/DELETE,
+ * we do need to scan or join the target. (NOTE: we do not bother
+ * to check for namespace conflict; we assume that the namespace was
+ * initially empty in these cases.)
+ *
+ * Returns the rangetable index of the target relation.
*/
-void
-lockTargetTable(ParseState *pstate, char *relname)
+int
+setTargetTable(ParseState *pstate, char *relname,
+ bool inh, bool alsoSource)
{
+ RangeTblEntry *rte;
+ int rtindex;
+
/* Close old target; this could only happen for multi-action rules */
if (pstate->p_target_relation != NULL)
heap_close(pstate->p_target_relation, NoLock);
- pstate->p_target_relation = NULL;
- pstate->p_target_rangetblentry = NULL; /* setTargetTable will set this */
/*
* Open target rel and grab suitable lock (which we will hold till
* but *not* release the lock.
*/
pstate->p_target_relation = heap_openr(relname, RowExclusiveLock);
-}
-/*
- * setTargetTable
- * Add the target relation of INSERT/UPDATE/DELETE to the range table,
- * and make the special links to it in the ParseState.
- *
- * inJoinSet says whether to add the target to the join list.
- * For INSERT, we don't want the target to be joined to; it's a
- * destination of tuples, not a source. For UPDATE/DELETE, we do
- * need to scan or join the target.
- */
-void
-setTargetTable(ParseState *pstate, char *relname, bool inh, bool inJoinSet)
-{
- RangeTblEntry *rte;
+ /*
+ * Now build an RTE.
+ */
+ rte = addRangeTableEntry(pstate, relname, NULL, inh, false);
+ pstate->p_target_rangetblentry = rte;
- /* look for relname only at current nesting level... */
- if (refnameRangeTablePosn(pstate, relname, NULL) == 0)
- {
- rte = addRangeTableEntry(pstate, relname, NULL, inh, false);
- /*
- * Since the rel wasn't in the rangetable already, it's not being
- * read; override addRangeTableEntry's default checkForRead.
- *
- * If we find an explicit reference to the rel later during
- * parse analysis, scanRTEForColumn will change checkForRead
- * to 'true' again. That can't happen for INSERT but it is
- * possible for UPDATE and DELETE.
- */
- rte->checkForRead = false;
- }
- else
- {
- rte = refnameRangeTableEntry(pstate, relname);
- /*
- * Since the rel was in the rangetable already, it's being read
- * as well as written. Therefore, leave checkForRead true.
- *
- * Force inh to the desired setting for the target (XXX is this
- * reasonable? It's *necessary* that INSERT target not be marked
- * inheritable, but otherwise not too clear what to do if conflict?)
- */
- rte->inh = inh;
- }
+ /* assume new rte is at end */
+ rtindex = length(pstate->p_rtable);
+ Assert(rte == rt_fetch(rtindex, pstate->p_rtable));
- /* Mark target table as requiring write access. */
+ /*
+ * Override addRangeTableEntry's default checkForRead, and instead
+ * mark target table as requiring write access.
+ *
+ * If we find an explicit reference to the rel later during
+ * parse analysis, scanRTEForColumn will change checkForRead
+ * to 'true' again. That can't happen for INSERT but it is
+ * possible for UPDATE and DELETE.
+ */
+ rte->checkForRead = false;
rte->checkForWrite = true;
- if (inJoinSet)
- addRTEtoJoinList(pstate, rte);
-
- /* lockTargetTable should have been called earlier */
- Assert(pstate->p_target_relation != NULL);
+ /*
+ * If UPDATE/DELETE, add table to joinlist and namespace.
+ */
+ if (alsoSource)
+ addRTEtoQuery(pstate, rte, true, true);
- pstate->p_target_rangetblentry = rte;
+ return rtindex;
}
/*
List *containedRels)
{
Node *result;
- List *sv_joinlist;
+ List *save_namespace;
List *clause_varnos,
*l;
/*
- * This is a tad tricky, for two reasons. First, at the point where
- * we're called, the two subtrees of the JOIN node aren't yet part of
- * the pstate's joinlist, which means that transformExpr() won't resolve
- * unqualified references to their columns correctly. We fix this in a
- * slightly klugy way: temporarily make the pstate's joinlist consist of
- * just those two subtrees (which creates exactly the namespace the ON
- * clause should see). This is OK only because the ON clause can't
- * legally alter the joinlist by causing relation refs to be added.
+ * This is a tad tricky, for two reasons. First, the namespace that
+ * the join expression should see is just the two subtrees of the JOIN
+ * plus any outer references from upper pstate levels. So, temporarily
+ * set this pstate's namespace accordingly. (We need not check for
+ * refname conflicts, because transformFromClauseItem() already did.)
+ * NOTE: this code is OK only because the ON clause can't legally alter
+ * the namespace by causing implicit relation refs to be added.
*/
- sv_joinlist = pstate->p_joinlist;
- pstate->p_joinlist = makeList2(j->larg, j->rarg);
+ save_namespace = pstate->p_namespace;
+ pstate->p_namespace = makeList2(j->larg, j->rarg);
/* This part is just like transformWhereClause() */
result = transformExpr(pstate, j->quals, EXPR_COLUMN_FIRST);
typeidTypeName(exprType(result)));
}
- pstate->p_joinlist = sv_joinlist;
+ pstate->p_namespace = save_namespace;
/*
* Second, we need to check that the ON condition doesn't refer to any
* rels outside the input subtrees of the JOIN. It could do that despite
- * our hack on the joinlist if it uses fully-qualified names. So, grovel
+ * our hack on the namespace if it uses fully-qualified names. So, grovel
* through the transformed clause and make sure there are no bogus
- * references.
+ * references. (Outer references are OK, and are ignored here.)
*/
clause_varnos = pull_varnos(result);
foreach(l, clause_varnos)
interpretInhOption(r->inhOpt), true);
/*
- * We create a RangeTblRef, but we do not add it to the joinlist here.
- * makeRangeTable will do so, if we are at top level of the FROM clause.
+ * We create a RangeTblRef, but we do not add it to the joinlist or
+ * namespace; our caller must do that if appropriate.
*/
rtr = makeNode(RangeTblRef);
/* assume new rte is at end */
static RangeTblRef *
transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
{
- List *save_rtable;
- List *save_joinlist;
+ List *save_namespace;
List *parsetrees;
Query *query;
RangeTblEntry *rte;
* does not include other FROM items). But it does need to be able to
* see any further-up parent states, so we can't just pass a null parent
* pstate link. So, temporarily make the current query level have an
- * empty rtable and joinlist.
+ * empty namespace.
*/
- save_rtable = pstate->p_rtable;
- save_joinlist = pstate->p_joinlist;
- pstate->p_rtable = NIL;
- pstate->p_joinlist = NIL;
+ save_namespace = pstate->p_namespace;
+ pstate->p_namespace = NIL;
parsetrees = parse_analyze(r->subquery, pstate);
- pstate->p_rtable = save_rtable;
- pstate->p_joinlist = save_joinlist;
+ pstate->p_namespace = save_namespace;
/*
* Check that we got something reasonable. Some of these conditions
rte = addRangeTableEntryForSubquery(pstate, query, r->name, true);
/*
- * We create a RangeTblRef, but we do not add it to the joinlist here.
- * makeRangeTable will do so, if we are at top level of the FROM clause.
+ * We create a RangeTblRef, but we do not add it to the joinlist or
+ * namespace; our caller must do that if appropriate.
*/
rtr = makeNode(RangeTblRef);
/* assume new rte is at end */
* transformFromClauseItem -
* Transform a FROM-clause item, adding any required entries to the
* range table list being built in the ParseState, and return the
- * transformed item ready to include in the joinlist.
+ * transformed item ready to include in the joinlist and namespace.
* This routine can recurse to handle SQL92 JOIN expressions.
*
* Aside from the primary return value (the transformed joinlist item)
*/
*containedRels = nconc(l_containedRels, r_containedRels);
+ /*
+ * Check for conflicting refnames in left and right subtrees. Must
+ * do this because higher levels will assume I hand back a self-
+ * consistent namespace subtree.
+ */
+ checkNameSpaceConflicts(pstate, j->larg, j->rarg);
+
/*
* Extract column name and var lists from both subtrees
*/
/*
* Process alias (AS clause), if any.
- *
- * The given table alias must be unique in the current nesting level,
- * ie it cannot match any RTE refname or jointable alias. This is
- * a bit painful to check because my own child joins are not yet in
- * the pstate's joinlist, so they have to be scanned separately.
*/
if (j->alias)
{
- /* Check against previously created RTEs and joinlist entries */
- if (refnameRangeOrJoinEntry(pstate, j->alias->relname, NULL))
- elog(ERROR, "Table name \"%s\" specified more than once",
- j->alias->relname);
- /* Check children */
- if (scanJoinListForRefname(j->larg, j->alias->relname) ||
- scanJoinListForRefname(j->rarg, j->alias->relname))
- elog(ERROR, "Table name \"%s\" specified more than once",
- j->alias->relname);
/*
* If a column alias list is specified, substitute the alias
* names into my output-column list
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.89 2001/01/24 19:43:01 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.90 2001/02/14 21:35:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
{
if (indirection == NIL)
return basenode;
- return (Node *) transformArraySubscripts(pstate, basenode,
+ return (Node *) transformArraySubscripts(pstate,
+ basenode, exprType(basenode),
indirection, false, NULL);
}
transformIdent(ParseState *pstate, Ident *ident, int precedence)
{
Node *result = NULL;
+ int sublevels_up;
/*
* try to find the ident as a relation ... but not if subscripts
* appear
*/
if (ident->indirection == NIL &&
- refnameRangeTableEntry(pstate, ident->name) != NULL)
+ refnameRangeOrJoinEntry(pstate, ident->name, &sublevels_up) != NULL)
{
ident->isRel = TRUE;
result = (Node *) ident;
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.98 2001/01/24 19:43:02 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.99 2001/02/14 21:35:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
{
RangeTblEntry *rte;
int vnum;
+ Node *rteorjoin;
int sublevels_up;
/*
*/
refname = ((Ident *) arg)->name;
- rte = refnameRangeTableEntry(pstate, refname);
- if (rte == NULL)
+ rteorjoin = refnameRangeOrJoinEntry(pstate, refname,
+ &sublevels_up);
+
+ if (rteorjoin == NULL)
+ {
rte = addImplicitRTE(pstate, refname);
+ }
+ else if (IsA(rteorjoin, RangeTblEntry))
+ {
+ rte = (RangeTblEntry *) rteorjoin;
+ }
+ else if (IsA(rteorjoin, JoinExpr))
+ {
+ elog(ERROR,
+ "function applied to tuple is not supported for joins");
+ rte = NULL; /* keep compiler quiet */
+ }
+ else
+ {
+ elog(ERROR, "ParseFuncOrColumn: unexpected node type %d",
+ nodeTag(rteorjoin));
+ rte = NULL; /* keep compiler quiet */
+ }
vnum = RTERangeTablePosn(pstate, rte, &sublevels_up);
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.51 2001/01/24 19:43:02 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.52 2001/02/14 21:35:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
*
* pstate Parse state
* arrayBase Already-transformed expression for the array as a whole
+ * (may be NULL if we are handling an INSERT)
+ * arrayType OID of array's datatype
* indirection Untransformed list of subscripts (must not be NIL)
* forceSlice If true, treat subscript as array slice in all cases
* assignFrom NULL for array fetch, else transformed expression for source.
*/
-ArrayRef *
+ArrayRef *
transformArraySubscripts(ParseState *pstate,
Node *arrayBase,
+ Oid arrayType,
List *indirection,
bool forceSlice,
Node *assignFrom)
{
- Oid typearray,
- typeelement,
- typeresult;
+ Oid elementType,
+ resultType;
HeapTuple type_tuple_array,
type_tuple_element;
Form_pg_type type_struct_array,
ArrayRef *aref;
/* Get the type tuple for the array */
- typearray = exprType(arrayBase);
-
type_tuple_array = SearchSysCache(TYPEOID,
- ObjectIdGetDatum(typearray),
+ ObjectIdGetDatum(arrayType),
0, 0, 0);
if (!HeapTupleIsValid(type_tuple_array))
elog(ERROR, "transformArraySubscripts: Cache lookup failed for array type %u",
- typearray);
+ arrayType);
type_struct_array = (Form_pg_type) GETSTRUCT(type_tuple_array);
- typeelement = type_struct_array->typelem;
- if (typeelement == InvalidOid)
+ elementType = type_struct_array->typelem;
+ if (elementType == InvalidOid)
elog(ERROR, "transformArraySubscripts: type %s is not an array",
NameStr(type_struct_array->typname));
/* Get the type tuple for the array element type */
type_tuple_element = SearchSysCache(TYPEOID,
- ObjectIdGetDatum(typeelement),
+ ObjectIdGetDatum(elementType),
0, 0, 0);
if (!HeapTupleIsValid(type_tuple_element))
elog(ERROR, "transformArraySubscripts: Cache lookup failed for array element type %u",
- typeelement);
+ elementType);
type_struct_element = (Form_pg_type) GETSTRUCT(type_tuple_element);
/*
* array type if we are fetching a slice or storing.
*/
if (isSlice || assignFrom != NULL)
- typeresult = typearray;
+ resultType = arrayType;
else
- typeresult = typeelement;
+ resultType = elementType;
/*
* Transform the subscript expressions.
if (assignFrom != NULL)
{
Oid typesource = exprType(assignFrom);
- Oid typeneeded = isSlice ? typearray : typeelement;
+ Oid typeneeded = isSlice ? arrayType : elementType;
if (typesource != InvalidOid)
{
aref = makeNode(ArrayRef);
aref->refattrlength = type_struct_array->typlen;
aref->refelemlength = type_struct_element->typlen;
- aref->refelemtype = typeresult; /* XXX should save element type
+ aref->refelemtype = resultType; /* XXX should save element type
* too */
aref->refelembyval = type_struct_element->typbyval;
aref->refupperindexpr = upperIndexpr;
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.51 2001/01/24 19:43:02 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.52 2001/02/14 21:35:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
-#include <ctype.h>
-
#include "postgres.h"
+#include <ctype.h>
+
#include "access/heapam.h"
#include "access/htup.h"
#include "catalog/pg_type.h"
#include "utils/lsyscache.h"
+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,
while (pstate != NULL)
{
- List *temp;
- JoinExpr *join;
+ Node *rte;
- /*
- * Check the rangetable for RTEs; if no match, recursively scan
- * the joinlist for join tables. We assume that no duplicate
- * entries have been made in any one nesting level.
- */
- foreach(temp, pstate->p_rtable)
- {
- RangeTblEntry *rte = lfirst(temp);
-
- if (strcmp(rte->eref->relname, refname) == 0)
- return (Node *) rte;
- }
-
- join = scanJoinListForRefname((Node *) pstate->p_joinlist, refname);
- if (join)
- return (Node *) join;
+ rte = scanNameSpaceForRefname(pstate,
+ (Node *) pstate->p_namespace,
+ refname);
+ if (rte)
+ return rte;
pstate = pstate->parentParseState;
if (sublevels_up)
}
/*
- * Recursively search a joinlist for a joinexpr with given refname
+ * Recursively search a namespace for an RTE or joinexpr with given refname.
+ *
+ * The top level of p_namespace is a list, and we recurse into any joins
+ * that are not subqueries. It is also possible to pass an individual
+ * join subtree (useful when checking for name conflicts within a scope).
*
- * Note that during parse analysis, we don't expect to find a FromExpr node
- * in p_joinlist; its top level is just a bare List.
+ * Note: we do not worry about the possibility of multiple matches;
+ * we assume the code that built the namespace checked for duplicates.
*/
-JoinExpr *
-scanJoinListForRefname(Node *jtnode, char *refname)
+static Node *
+scanNameSpaceForRefname(ParseState *pstate, Node *nsnode,
+ char *refname)
{
- JoinExpr *result = NULL;
+ Node *result = NULL;
- if (jtnode == NULL)
+ if (nsnode == NULL)
return NULL;
- if (IsA(jtnode, List))
+ if (IsA(nsnode, RangeTblRef))
{
- List *l;
+ int varno = ((RangeTblRef *) nsnode)->rtindex;
+ RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable);
- foreach(l, (List *) jtnode)
- {
- result = scanJoinListForRefname(lfirst(l), refname);
- if (result)
- break;
- }
+ if (strcmp(rte->eref->relname, refname) == 0)
+ result = (Node *) rte;
}
- else if (IsA(jtnode, RangeTblRef))
+ else if (IsA(nsnode, JoinExpr))
{
- /* ignore ... */
+ JoinExpr *j = (JoinExpr *) nsnode;
+
+ if (j->alias)
+ {
+ if (strcmp(j->alias->relname, refname) == 0)
+ return (Node *) j; /* matched a join alias */
+ /*
+ * Tables within an aliased join are invisible from outside
+ * the join, according to the scope rules of SQL92 (the join
+ * is considered a subquery). So, stop here.
+ */
+ return NULL;
+ }
+ result = scanNameSpaceForRefname(pstate, j->larg, refname);
+ if (! result)
+ result = scanNameSpaceForRefname(pstate, j->rarg, refname);
}
- else if (IsA(jtnode, JoinExpr))
+ else if (IsA(nsnode, List))
{
- JoinExpr *j = (JoinExpr *) jtnode;
+ List *l;
- if (j->alias && strcmp(j->alias->relname, refname) == 0)
- return j;
- result = scanJoinListForRefname(j->larg, refname);
- if (! result)
- result = scanJoinListForRefname(j->rarg, refname);
+ foreach(l, (List *) nsnode)
+ {
+ result = scanNameSpaceForRefname(pstate, lfirst(l), refname);
+ if (result)
+ break;
+ }
}
else
- elog(ERROR, "scanJoinListForRefname: unexpected node type %d",
- nodeTag(jtnode));
+ elog(ERROR, "scanNameSpaceForRefname: unexpected node type %d",
+ nodeTag(nsnode));
return result;
}
-/*
- * given refname, return a pointer to the range table entry.
- *
- * NOTE that this routine will ONLY find RTEs, not join tables.
- */
-RangeTblEntry *
-refnameRangeTableEntry(ParseState *pstate, char *refname)
+/* Convenience subroutine for checkNameSpaceConflicts */
+static void
+scanNameSpaceForConflict(ParseState *pstate, Node *nsnode,
+ char *refname)
{
- List *temp;
-
- while (pstate != NULL)
- {
- foreach(temp, pstate->p_rtable)
- {
- RangeTblEntry *rte = lfirst(temp);
-
- if (strcmp(rte->eref->relname, refname) == 0)
- return rte;
- }
- pstate = pstate->parentParseState;
- }
- return NULL;
+ if (scanNameSpaceForRefname(pstate, nsnode, refname) != NULL)
+ elog(ERROR, "Table name \"%s\" specified more than once", refname);
}
/*
- * given refname, return RT index (starting with 1) of the relation,
- * and optionally get its nesting depth (0 = current). If sublevels_up
- * is NULL, only consider rels at the current nesting level.
- * A zero result means name not found.
+ * Recursively check for refname conflicts between two namespaces or
+ * namespace subtrees. Raise an error if any is found.
+ *
+ * Works by recursively scanning namespace1 in the same way that
+ * scanNameSpaceForRefname does, and then looking in namespace2 for
+ * a match to each refname found in namespace1.
*
- * NOTE that this routine will ONLY find RTEs, not join tables.
+ * Note: we assume that each given argument does not contain conflicts
+ * itself; we just want to know if the two can be merged together.
*/
-int
-refnameRangeTablePosn(ParseState *pstate, char *refname, int *sublevels_up)
+void
+checkNameSpaceConflicts(ParseState *pstate, Node *namespace1,
+ Node *namespace2)
{
- int index;
- List *temp;
-
- if (sublevels_up)
- *sublevels_up = 0;
+ if (namespace1 == NULL)
+ return;
+ if (IsA(namespace1, RangeTblRef))
+ {
+ int varno = ((RangeTblRef *) namespace1)->rtindex;
+ RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable);
- while (pstate != NULL)
+ scanNameSpaceForConflict(pstate, namespace2, rte->eref->relname);
+ }
+ else if (IsA(namespace1, JoinExpr))
{
- index = 1;
- foreach(temp, pstate->p_rtable)
+ JoinExpr *j = (JoinExpr *) namespace1;
+
+ if (j->alias)
{
- RangeTblEntry *rte = lfirst(temp);
+ scanNameSpaceForConflict(pstate, namespace2, j->alias->relname);
+ /*
+ * Tables within an aliased join are invisible from outside
+ * the join, according to the scope rules of SQL92 (the join
+ * is considered a subquery). So, stop here.
+ */
+ return;
+ }
+ checkNameSpaceConflicts(pstate, j->larg, namespace2);
+ checkNameSpaceConflicts(pstate, j->rarg, namespace2);
+ }
+ else if (IsA(namespace1, List))
+ {
+ List *l;
- if (strcmp(rte->eref->relname, refname) == 0)
- return index;
- index++;
+ foreach(l, (List *) namespace1)
+ {
+ checkNameSpaceConflicts(pstate, lfirst(l), namespace2);
}
- pstate = pstate->parentParseState;
- if (sublevels_up)
- (*sublevels_up)++;
- else
- break;
}
- return 0;
+ else
+ elog(ERROR, "checkNameSpaceConflicts: unexpected node type %d",
+ nodeTag(namespace1));
}
/*
else
break;
}
+
elog(ERROR, "RTERangeTablePosn: RTE not found (internal error)");
return 0; /* keep compiler quiet */
}
while (pstate != NULL)
{
- List *jt;
+ List *ns;
/*
- * We want to look only at top-level jointree items, and even for
+ * We need to look only at top-level namespace items, and even for
* those, ignore RTEs that are marked as not inFromCl and not
* the query's target relation.
*/
- foreach(jt, pstate->p_joinlist)
+ foreach(ns, pstate->p_namespace)
{
- Node *jtnode = (Node *) lfirst(jt);
+ Node *nsnode = (Node *) lfirst(ns);
Node *newresult = NULL;
- if (IsA(jtnode, RangeTblRef))
+ if (IsA(nsnode, RangeTblRef))
{
- int varno = ((RangeTblRef *) jtnode)->rtindex;
+ int varno = ((RangeTblRef *) nsnode)->rtindex;
RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable);
if (! rte->inFromCl &&
/* use orig_pstate here to get the right sublevels_up */
newresult = scanRTEForColumn(orig_pstate, rte, colname);
}
- else if (IsA(jtnode, JoinExpr))
+ else if (IsA(nsnode, JoinExpr))
{
- JoinExpr *j = (JoinExpr *) jtnode;
+ JoinExpr *j = (JoinExpr *) nsnode;
newresult = scanJoinForColumn(j, colname, levels_up);
}
else
elog(ERROR, "colnameToVar: unexpected node type %d",
- nodeTag(jtnode));
+ nodeTag(nsnode));
if (newresult)
{
colname);
else if (IsA(rteorjoin, JoinExpr))
result = scanJoinForColumn((JoinExpr *) rteorjoin,
- colname, sublevels_up);
+ colname, sublevels_up);
else
{
elog(ERROR, "qualifiedNameToVar: unexpected node type %d",
/*
* Add an entry for a relation to the pstate's range table (p_rtable).
*
- * If the specified refname is already present, raise error.
+ * If pstate is NULL, we just build an RTE and return it without adding it
+ * to an rtable list.
*
- * If pstate is NULL, we just build an RTE and return it without worrying
- * about membership in 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,
bool inh,
bool inFromCl)
{
+ RangeTblEntry *rte = makeNode(RangeTblEntry);
char *refname = alias ? alias->relname : relname;
LOCKMODE lockmode;
Relation rel;
- RangeTblEntry *rte;
Attr *eref;
int maxattrs;
int numaliases;
int varattno;
- /* Check for conflicting RTE or jointable alias (at level 0 only) */
- if (pstate != NULL)
- {
- Node *rteorjoin = refnameRangeOrJoinEntry(pstate, refname, NULL);
-
- if (rteorjoin)
- elog(ERROR, "Table name \"%s\" specified more than once",
- refname);
- }
-
- rte = makeNode(RangeTblEntry);
-
rte->relname = relname;
rte->alias = alias;
rte->subquery = NULL;
rte->checkAsUser = InvalidOid; /* not set-uid by default, either */
/*
- * Add completed RTE to range table list.
+ * 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);
Attr *alias,
bool inFromCl)
{
+ RangeTblEntry *rte = makeNode(RangeTblEntry);
char *refname = alias->relname;
- RangeTblEntry *rte;
Attr *eref;
int numaliases;
int varattno;
List *tlistitem;
- /* Check for conflicting RTE or jointable alias (at level 0 only) */
- if (pstate != NULL)
- {
- Node *rteorjoin = refnameRangeOrJoinEntry(pstate, refname, NULL);
-
- if (rteorjoin)
- elog(ERROR, "Table name \"%s\" specified more than once",
- refname);
- }
-
- rte = makeNode(RangeTblEntry);
-
rte->relname = NULL;
rte->relid = InvalidOid;
rte->subquery = subquery;
rte->checkAsUser = InvalidOid;
/*
- * Add completed RTE to range table list.
+ * 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);
}
/*
- * Add the given RTE as a top-level entry in the pstate's join list,
- * unless there already is an entry for it.
+ * Add the given RTE as a top-level entry in the pstate's join list
+ * and/or name space list. (We assume caller has checked for any
+ * namespace conflict.)
*/
void
-addRTEtoJoinList(ParseState *pstate, RangeTblEntry *rte)
+addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte,
+ bool addToJoinList, bool addToNameSpace)
{
int rtindex = RTERangeTablePosn(pstate, rte, NULL);
- List *jt;
- RangeTblRef *rtr;
-
- foreach(jt, pstate->p_joinlist)
- {
- Node *n = (Node *) lfirst(jt);
-
- if (IsA(n, RangeTblRef))
- {
- if (rtindex == ((RangeTblRef *) n)->rtindex)
- return; /* it's already being joined to */
- }
- }
+ RangeTblRef *rtr = makeNode(RangeTblRef);
- /* Not present, so add it */
- rtr = makeNode(RangeTblRef);
rtr->rtindex = rtindex;
- pstate->p_joinlist = lappend(pstate->p_joinlist, rtr);
+
+ if (addToJoinList)
+ pstate->p_joinlist = lappend(pstate->p_joinlist, rtr);
+ if (addToNameSpace)
+ pstate->p_namespace = lappend(pstate->p_namespace, rtr);
}
/*
* Add a POSTQUEL-style implicit RTE.
*
- * We assume caller has already checked that there is no such RTE now.
+ * We assume caller has already checked that there is no RTE or join with
+ * a conflicting name.
*/
RangeTblEntry *
addImplicitRTE(ParseState *pstate, char *relname)
RangeTblEntry *rte;
rte = addRangeTableEntry(pstate, relname, NULL, false, false);
- addRTEtoJoinList(pstate, rte);
+ addRTEtoQuery(pstate, rte, true, true);
warnAutoRange(pstate, relname);
return rte;
* This is unlike get_attname() because we use aliases if available.
* In particular, it will work on an RTE for a subselect, whereas
* get_attname() only works on real relations.
+ *
+ * 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 *
pstate->parentParseState != NULL ? " in subquery" : "",
refname);
}
-
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.64 2001/01/24 19:43:02 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.65 2001/02/14 21:35:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
*/
if (indirection)
{
- Attr *att = makeAttr(pstrdup(RelationGetRelationName(rd)),
- colname);
Node *arrayBase;
ArrayRef *aref;
- arrayBase = ParseNestedFuncOrColumn(pstate, att, EXPR_COLUMN_FIRST);
- aref = transformArraySubscripts(pstate, arrayBase,
- indirection,
- pstate->p_is_insert,
- tle->expr);
if (pstate->p_is_insert)
{
-
/*
* The command is INSERT INTO table (arraycol[subscripts]) ...
* so there is not really a source array value to work with.
* Let the executor do something reasonable, if it can. Notice
- * that we forced transformArraySubscripts to treat the
- * subscripting op as an array-slice op above, so the source
- * data will have been coerced to array type.
+ * that we force transformArraySubscripts to treat the
+ * subscripting op as an array-slice op below, so the source
+ * data will have been coerced to the array type.
+ */
+ arrayBase = NULL; /* signal there is no source array */
+ }
+ else
+ {
+ /*
+ * Build a Var for the array to be updated.
*/
- aref->refexpr = NULL; /* signal there is no source array */
+ arrayBase = (Node *) make_var(pstate,
+ pstate->p_target_rangetblentry,
+ attrno);
}
+
+ aref = transformArraySubscripts(pstate,
+ arrayBase,
+ attrtype,
+ indirection,
+ pstate->p_is_insert,
+ tle->expr);
tle->expr = (Node *) aref;
}
else
/* ExpandAllTables()
* Turns '*' (in the target list) into a list of targetlist entries.
*
- * tlist entries are generated for each relation appearing in the FROM list,
- * which by now has been transformed into a joinlist.
+ * tlist entries are generated for each relation appearing at the top level
+ * of the query's namespace, except for RTEs marked not inFromCl. (These
+ * may include NEW/OLD pseudo-entries, implicit RTEs, etc.)
*/
static List *
ExpandAllTables(ParseState *pstate)
{
List *target = NIL;
- List *jt;
+ List *ns;
- /* SELECT *; */
- if (pstate->p_joinlist == NIL)
- elog(ERROR, "Wildcard with no tables specified not allowed");
-
- foreach(jt, pstate->p_joinlist)
+ foreach(ns, pstate->p_namespace)
{
- Node *n = (Node *) lfirst(jt);
+ Node *n = (Node *) lfirst(ns);
if (IsA(n, RangeTblRef))
{
"\n\t%s", nodeToString(n));
}
+ /* Check for SELECT *; */
+ if (target == NIL)
+ elog(ERROR, "Wildcard with no tables specified not allowed");
+
return target;
}
* back to source text
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.71 2001/01/03 22:01:05 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.72 2001/02/14 21:35:05 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
*
**********************************************************************/
+#include "postgres.h"
+
#include <unistd.h>
#include <fcntl.h>
-#include "postgres.h"
-
#include "catalog/pg_index.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_shadow.h"
#include "parser/parse_expr.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteManip.h"
+#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
* Local data types
* ----------
*/
+
+/* Context info needed for invoking a recursive querytree display routine */
typedef struct
{
StringInfo buf; /* output buffer to append to */
- List *rangetables; /* List of List of RangeTblEntry */
+ List *namespaces; /* List of deparse_namespace nodes */
bool varprefix; /* TRUE to print prefixes on Vars */
} deparse_context;
+/*
+ * Each level of query context around a subtree needs a level of Var namespace.
+ * The rangetable is the list of actual RTEs, and the namespace indicates
+ * which parts of the rangetable are accessible (and under what aliases)
+ * in the expression currently being looked at. A Var having varlevelsup=N
+ * refers to the N'th item (counting from 0) in the current context's
+ * namespaces list.
+ */
+typedef struct
+{
+ List *rtable; /* List of RangeTblEntry nodes */
+ List *namespace; /* List of joinlist items (RangeTblRef and
+ * JoinExpr nodes) */
+} deparse_namespace;
+
/* ----------
* Global data
*/
static void make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc);
static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc);
-static void get_query_def(Query *query, StringInfo buf, List *parentrtables);
+static void get_query_def(Query *query, StringInfo buf, List *parentnamespace);
static void get_select_query_def(Query *query, deparse_context *context);
static void get_insert_query_def(Query *query, deparse_context *context);
static void get_update_query_def(Query *query, deparse_context *context);
static void get_setop_query(Node *setOp, Query *query,
deparse_context *context, bool toplevel);
static bool simple_distinct(List *distinctClause, List *targetList);
-static RangeTblEntry *get_rte_for_var(Var *var, deparse_context *context);
+static void get_names_for_var(Var *var, deparse_context *context,
+ char **refname, char **attname);
+static bool get_alias_for_case(CaseExpr *caseexpr, deparse_context *context,
+ char **refname, char **attname);
+static bool find_alias_in_namespace(Node *nsnode, Node *expr,
+ List *rangetable, int levelsup,
+ char **refname, char **attname);
+static bool phony_equal(Node *expr1, Node *expr2, int levelsup);
static void get_rule_expr(Node *node, deparse_context *context);
static void get_func_expr(Expr *expr, deparse_context *context);
static void get_tle_expr(TargetEntry *tle, deparse_context *context);
* expr is the node tree to be deparsed. It must be a transformed expression
* tree (ie, not the raw output of gram.y).
*
- * rangetables is a List of Lists of RangeTblEntry nodes: first sublist is for
- * varlevelsup = 0, next for varlevelsup = 1, etc. In each sublist the first
- * item is for varno = 1, next varno = 2, etc. (Each sublist has the same
- * format as the rtable list of a parsetree or query.)
+ * dpcontext is a list of deparse_namespace nodes representing the context
+ * for interpreting Vars in the node tree.
*
* forceprefix is TRUE to force all Vars to be prefixed with their table names.
- * Otherwise, a prefix is printed only if there's more than one table involved
- * (and someday the code might try to print one only if there's ambiguity).
*
* The result is a palloc'd string.
* ----------
*/
char *
-deparse_expression(Node *expr, List *rangetables, bool forceprefix)
+deparse_expression(Node *expr, List *dpcontext, bool forceprefix)
{
StringInfoData buf;
deparse_context context;
initStringInfo(&buf);
context.buf = &buf;
- context.rangetables = rangetables;
- context.varprefix = (forceprefix ||
- length(rangetables) != 1 ||
- length((List *) lfirst(rangetables)) != 1);
+ context.namespaces = dpcontext;
+ context.varprefix = forceprefix;
rulename = ""; /* in case of errors */
return buf.data;
}
+/* ----------
+ * deparse_context_for - Build deparse context for a single relation
+ *
+ * Given the name and OID of a relation, build deparsing context for an
+ * expression referencing only that relation (as varno 1, varlevelsup 0).
+ * This is presently sufficient for the external uses of deparse_expression.
+ * ----------
+ */
+List *
+deparse_context_for(char *relname, Oid relid)
+{
+ deparse_namespace *dpns;
+ RangeTblEntry *rte;
+ RangeTblRef *rtr;
+
+ dpns = (deparse_namespace *) palloc(sizeof(deparse_namespace));
+
+ /* Build a minimal RTE for the rel */
+ rte = makeNode(RangeTblEntry);
+ rte->relname = relname;
+ rte->relid = relid;
+ rte->eref = makeNode(Attr);
+ rte->eref->relname = relname;
+ rte->inh = false;
+ rte->inFromCl = true;
+ /* Build one-element rtable */
+ dpns->rtable = makeList1(rte);
+
+ /* Build a namespace list referencing this RTE only */
+ rtr = makeNode(RangeTblRef);
+ rtr->rtindex = 1;
+ dpns->namespace = makeList1(rtr);
+
+ /* Return a one-deep namespace stack */
+ return makeList1(dpns);
+}
+
/* ----------
* make_ruledef - reconstruct the CREATE RULE command
* for a given pg_rewrite tuple
Node *qual;
Query *query;
deparse_context context;
+ deparse_namespace dpns;
appendStringInfo(buf, " WHERE ");
query = (Query *) lfirst(actions);
context.buf = buf;
- context.rangetables = makeList1(query->rtable);
+ context.namespaces = makeList1(&dpns);
context.varprefix = (length(query->rtable) != 1);
+ dpns.rtable = query->rtable;
+ dpns.namespace = query->jointree ? query->jointree->fromlist : NIL;
get_rule_expr(qual, &context);
}
* ----------
*/
static void
-get_query_def(Query *query, StringInfo buf, List *parentrtables)
+get_query_def(Query *query, StringInfo buf, List *parentnamespace)
{
deparse_context context;
+ deparse_namespace dpns;
context.buf = buf;
- context.rangetables = lcons(query->rtable, parentrtables);
- context.varprefix = (parentrtables != NIL ||
+ context.namespaces = lcons(&dpns, parentnamespace);
+ context.varprefix = (parentnamespace != NIL ||
length(query->rtable) != 1);
+ dpns.rtable = query->rtable;
+ dpns.namespace = query->jointree ? query->jointree->fromlist : NIL;
switch (query->commandType)
{
else
{
Var *var = (Var *) (tle->expr);
- RangeTblEntry *rte;
+ char *refname;
char *attname;
- rte = get_rte_for_var(var, context);
- attname = get_rte_attribute_name(rte, var->varattno);
+ get_names_for_var(var, context, &refname, &attname);
tell_as = (strcmp(attname, tle->resdom->resname) != 0);
}
Query *subquery = rte->subquery;
Assert(subquery != NULL);
- get_query_def(subquery, buf, context->rangetables);
+ get_query_def(subquery, buf, context->namespaces);
}
else if (IsA(setOp, SetOperationStmt))
{
/*
- * Find the RTE referenced by a (possibly nonlocal) Var.
+ * Get the relation refname and attname for a (possibly nonlocal) Var.
+ *
+ * This is trickier than it ought to be because of the possibility of aliases
+ * and limited scope of refnames. We have to try to return the correct alias
+ * with respect to the current namespace given by the context.
*/
-static RangeTblEntry *
-get_rte_for_var(Var *var, deparse_context *context)
+static void
+get_names_for_var(Var *var, deparse_context *context,
+ char **refname, char **attname)
{
- List *rtlist = context->rangetables;
+ List *nslist = context->namespaces;
int sup = var->varlevelsup;
+ deparse_namespace *dpns;
+ RangeTblEntry *rte;
+
+ /* Find appropriate nesting depth */
+ while (sup-- > 0 && nslist != NIL)
+ nslist = lnext(nslist);
+ if (nslist == NIL)
+ elog(ERROR, "get_names_for_var: bogus varlevelsup %d",
+ var->varlevelsup);
+ dpns = (deparse_namespace *) lfirst(nslist);
+
+ /* Scan namespace to see if we can find an alias for the var */
+ if (find_alias_in_namespace((Node *) dpns->namespace, (Node *) var,
+ dpns->rtable, var->varlevelsup,
+ refname, attname))
+ return;
+
+ /*
+ * Otherwise, fall back on the rangetable entry. This should happen
+ * only for uses of special RTEs like *NEW* and *OLD*, which won't
+ * get placed in our namespace.
+ */
+ rte = rt_fetch(var->varno, dpns->rtable);
+ *refname = rte->eref->relname;
+ *attname = get_rte_attribute_name(rte, var->varattno);
+}
+
+/*
+ * Check to see if a CASE expression matches a FULL JOIN's output expression.
+ * If so, return the refname and alias it should be expressed as.
+ */
+static bool
+get_alias_for_case(CaseExpr *caseexpr, deparse_context *context,
+ char **refname, char **attname)
+{
+ List *nslist;
+ int sup;
- while (sup-- > 0)
- rtlist = lnext(rtlist);
+ /*
+ * This could be done more efficiently if we first groveled through the
+ * CASE to find varlevelsup values, but it's probably not worth the
+ * trouble. All this code will go away someday anyway ...
+ */
- return rt_fetch(var->varno, (List *) lfirst(rtlist));
+ sup = 0;
+ foreach(nslist, context->namespaces)
+ {
+ deparse_namespace *dpns = (deparse_namespace *) lfirst(nslist);
+
+ if (find_alias_in_namespace((Node *) dpns->namespace,
+ (Node *) caseexpr,
+ dpns->rtable, sup,
+ refname, attname))
+ return true;
+ sup++;
+ }
+ return false;
}
+/*
+ * Recursively scan a namespace (same representation as a jointree) to see
+ * if we can find an alias for the given expression. If so, return the
+ * correct alias refname and attname. The expression may be either a plain
+ * Var or a CASE expression (which may be a FULL JOIN reference).
+ */
+static bool
+find_alias_in_namespace(Node *nsnode, Node *expr,
+ List *rangetable, int levelsup,
+ char **refname, char **attname)
+{
+ if (nsnode == NULL)
+ return false;
+ if (IsA(nsnode, RangeTblRef))
+ {
+ if (IsA(expr, Var))
+ {
+ Var *var = (Var *) expr;
+ int rtindex = ((RangeTblRef *) nsnode)->rtindex;
+
+ if (var->varno == rtindex && var->varlevelsup == levelsup)
+ {
+ RangeTblEntry *rte = rt_fetch(rtindex, rangetable);
+
+ *refname = rte->eref->relname;
+ *attname = get_rte_attribute_name(rte, var->varattno);
+ return true;
+ }
+ }
+ }
+ else if (IsA(nsnode, JoinExpr))
+ {
+ JoinExpr *j = (JoinExpr *) nsnode;
+
+ if (j->alias)
+ {
+ List *vlist;
+ List *nlist;
+
+ /*
+ * Does the expr match any of the output columns of the join?
+ *
+ * We can't just use equal() here, because the given expr may
+ * have nonzero levelsup, whereas the saved expression in the
+ * JoinExpr should have zero levelsup.
+ */
+ nlist = j->colnames;
+ foreach(vlist, j->colvars)
+ {
+ if (phony_equal(lfirst(vlist), expr, levelsup))
+ {
+ *refname = j->alias->relname;
+ *attname = strVal(lfirst(nlist));
+ return true;
+ }
+ nlist = lnext(nlist);
+ }
+ /*
+ * Tables within an aliased join are invisible from outside
+ * the join, according to the scope rules of SQL92 (the join
+ * is considered a subquery). So, stop here.
+ */
+ return false;
+ }
+ if (find_alias_in_namespace(j->larg, expr,
+ rangetable, levelsup,
+ refname, attname))
+ return true;
+ if (find_alias_in_namespace(j->rarg, expr,
+ rangetable, levelsup,
+ refname, attname))
+ return true;
+ }
+ else if (IsA(nsnode, List))
+ {
+ List *l;
+
+ foreach(l, (List *) nsnode)
+ {
+ if (find_alias_in_namespace(lfirst(l), expr,
+ rangetable, levelsup,
+ refname, attname))
+ return true;
+ }
+ }
+ else
+ elog(ERROR, "find_alias_in_namespace: unexpected node type %d",
+ nodeTag(nsnode));
+ return false;
+}
+
+/*
+ * Check for equality of two expressions, with the proviso that all Vars in
+ * expr1 should have varlevelsup = 0, while all Vars in expr2 should have
+ * varlevelsup = levelsup.
+ *
+ * In reality we only need to support equality checks on Vars and the type
+ * of CASE expression that is used for FULL JOIN outputs, so not all node
+ * types need be handled here.
+ *
+ * Otherwise, this code is a straight ripoff from equalfuncs.c.
+ */
+static bool
+phony_equal(Node *expr1, Node *expr2, int levelsup)
+{
+ if (expr1 == NULL || expr2 == NULL)
+ return (expr1 == expr2);
+ if (nodeTag(expr1) != nodeTag(expr2))
+ return false;
+ if (IsA(expr1, Var))
+ {
+ Var *a = (Var *) expr1;
+ Var *b = (Var *) expr2;
+
+ if (a->varno != b->varno)
+ return false;
+ if (a->varattno != b->varattno)
+ return false;
+ if (a->vartype != b->vartype)
+ return false;
+ if (a->vartypmod != b->vartypmod)
+ return false;
+ if (a->varlevelsup != 0 || b->varlevelsup != levelsup)
+ return false;
+ if (a->varnoold != b->varnoold)
+ return false;
+ if (a->varoattno != b->varoattno)
+ return false;
+ return true;
+ }
+ if (IsA(expr1, CaseExpr))
+ {
+ CaseExpr *a = (CaseExpr *) expr1;
+ CaseExpr *b = (CaseExpr *) expr2;
+
+ if (a->casetype != b->casetype)
+ return false;
+ if (!phony_equal(a->arg, b->arg, levelsup))
+ return false;
+ if (!phony_equal((Node *) a->args, (Node *) b->args, levelsup))
+ return false;
+ if (!phony_equal(a->defresult, b->defresult, levelsup))
+ return false;
+ return true;
+ }
+ if (IsA(expr1, CaseWhen))
+ {
+ CaseWhen *a = (CaseWhen *) expr1;
+ CaseWhen *b = (CaseWhen *) expr2;
+
+ if (!phony_equal(a->expr, b->expr, levelsup))
+ return false;
+ if (!phony_equal(a->result, b->result, levelsup))
+ return false;
+ return true;
+ }
+ if (IsA(expr1, Expr))
+ {
+ Expr *a = (Expr *) expr1;
+ Expr *b = (Expr *) expr2;
+
+ if (a->opType != b->opType)
+ return false;
+ if (!phony_equal(a->oper, b->oper, levelsup))
+ return false;
+ if (!phony_equal((Node *) a->args, (Node *) b->args, levelsup))
+ return false;
+ return true;
+ }
+ if (IsA(expr1, Func))
+ {
+ Func *a = (Func *) expr1;
+ Func *b = (Func *) expr2;
+
+ if (a->funcid != b->funcid)
+ return false;
+ if (a->functype != b->functype)
+ return false;
+ return true;
+ }
+ if (IsA(expr1, List))
+ {
+ List *la = (List *) expr1;
+ List *lb = (List *) expr2;
+ List *l;
+
+ if (length(la) != length(lb))
+ return false;
+ foreach(l, la)
+ {
+ if (!phony_equal(lfirst(l), lfirst(lb), levelsup))
+ return false;
+ lb = lnext(lb);
+ }
+ return true;
+ }
+ /* If we get here, there was something weird in a JOIN's colvars list */
+ elog(ERROR, "phony_equal: unexpected node type %d", nodeTag(expr1));
+ return false;
+}
/* ----------
* get_rule_expr - Parse back an expression
case T_Var:
{
Var *var = (Var *) node;
- RangeTblEntry *rte = get_rte_for_var(var, context);
+ char *refname;
+ char *attname;
+ get_names_for_var(var, context, &refname, &attname);
if (context->varprefix)
{
- if (strcmp(rte->eref->relname, "*NEW*") == 0)
+ if (strcmp(refname, "*NEW*") == 0)
appendStringInfo(buf, "new.");
- else if (strcmp(rte->eref->relname, "*OLD*") == 0)
+ else if (strcmp(refname, "*OLD*") == 0)
appendStringInfo(buf, "old.");
else
appendStringInfo(buf, "%s.",
- quote_identifier(rte->eref->relname));
+ quote_identifier(refname));
}
- appendStringInfo(buf, "%s",
- quote_identifier(get_rte_attribute_name(rte,
- var->varattno)));
+ appendStringInfo(buf, "%s", quote_identifier(attname));
}
break;
{
CaseExpr *caseexpr = (CaseExpr *) node;
List *temp;
+ char *refname;
+ char *attname;
+
+ /* Hack for providing aliases for FULL JOIN outputs */
+ if (get_alias_for_case(caseexpr, context,
+ &refname, &attname))
+ {
+ if (context->varprefix)
+ appendStringInfo(buf, "%s.",
+ quote_identifier(refname));
+ appendStringInfo(buf, "%s", quote_identifier(attname));
+ break;
+ }
appendStringInfo(buf, "CASE");
foreach(temp, caseexpr->args)
{
StringInfo buf = context->buf;
Func *func = (Func *) (expr->oper);
+ Oid funcoid = func->funcid;
HeapTuple proctup;
Form_pg_proc procStruct;
char *proname;
List *l;
char *sep;
+ /*
+ * nullvalue() and nonnullvalue() should get turned into special
+ * syntax
+ */
+ if (funcoid == F_NULLVALUE)
+ {
+ appendStringInfoChar(buf, '(');
+ get_rule_expr((Node *) lfirst(expr->args), context);
+ appendStringInfo(buf, " ISNULL)");
+ return;
+ }
+ if (funcoid == F_NONNULLVALUE)
+ {
+ appendStringInfoChar(buf, '(');
+ get_rule_expr((Node *) lfirst(expr->args), context);
+ appendStringInfo(buf, " NOTNULL)");
+ return;
+ }
+
/*
* Get the functions pg_proc tuple
*/
proctup = SearchSysCache(PROCOID,
- ObjectIdGetDatum(func->funcid),
+ ObjectIdGetDatum(funcoid),
0, 0, 0);
if (!HeapTupleIsValid(proctup))
- elog(ERROR, "cache lookup for proc %u failed", func->funcid);
+ elog(ERROR, "cache lookup for proc %u failed", funcoid);
procStruct = (Form_pg_proc) GETSTRUCT(proctup);
proname = NameStr(procStruct->proname);
- /*
- * nullvalue() and nonnullvalue() should get turned into special
- * syntax
- */
- if (procStruct->pronargs == 1 && procStruct->proargtypes[0] == InvalidOid)
- {
- if (strcmp(proname, "nullvalue") == 0)
- {
- appendStringInfoChar(buf, '(');
- get_rule_expr((Node *) lfirst(expr->args), context);
- appendStringInfo(buf, " ISNULL)");
- ReleaseSysCache(proctup);
- return;
- }
- if (strcmp(proname, "nonnullvalue") == 0)
- {
- appendStringInfoChar(buf, '(');
- get_rule_expr((Node *) lfirst(expr->args), context);
- appendStringInfo(buf, " NOTNULL)");
- ReleaseSysCache(proctup);
- return;
- }
- }
-
/*
* Check to see if function is a length-coercion function for some
* datatype. If so, display the operation as a type cast.
if (need_paren)
appendStringInfoChar(buf, '(');
- get_query_def(query, buf, context->rangetables);
+ get_query_def(query, buf, context->namespaces);
if (need_paren)
appendStringInfo(buf, "))");
get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
{
StringInfo buf = context->buf;
+ deparse_namespace *dpns;
+ List *sv_namespace;
+
+ /*
+ * FROM-clause items have limited visibility of query's namespace.
+ * Save and restore the outer namespace setting while we munge it.
+ */
+ dpns = (deparse_namespace *) lfirst(context->namespaces);
+ sv_namespace = dpns->namespace;
+ dpns->namespace = NIL;
if (IsA(jtnode, RangeTblRef))
{
/* Subquery RTE */
Assert(rte->subquery != NULL);
appendStringInfoChar(buf, '(');
- get_query_def(rte->subquery, buf, context->rangetables);
+ get_query_def(rte->subquery, buf, context->namespaces);
appendStringInfoChar(buf, ')');
}
if (rte->alias != NULL)
{
List *col;
- appendStringInfo(buf, " (");
+ appendStringInfo(buf, "(");
foreach(col, rte->alias->attrs)
{
if (col != rte->alias->attrs)
}
else if (j->quals)
{
+ dpns->namespace = makeList2(j->larg, j->rarg);
appendStringInfo(buf, " ON (");
get_rule_expr(j->quals, context);
appendStringInfoChar(buf, ')');
{
List *col;
- appendStringInfo(buf, " (");
+ appendStringInfo(buf, "(");
foreach(col, j->alias->attrs)
{
if (col != j->alias->attrs)
else
elog(ERROR, "get_from_clause_item: unexpected node type %d",
nodeTag(jtnode));
+
+ dpns->namespace = sv_namespace;
}
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: primnodes.h,v 1.51 2001/01/24 19:43:26 momjian Exp $
+ * $Id: primnodes.h,v 1.52 2001/02/14 21:35:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* ----------------------------------------------------------------
*/
-/*
+/*--------------------
* Resdom (Result Domain)
*
* Notes:
*
* Both reskey and reskeyop are typically zero during parse/plan stages.
* The executor does not pay any attention to ressortgroupref.
- *
+ *--------------------
*/
typedef struct Resdom
{
* list. But varnoold/varoattno continue to hold the original values.
* The code doesn't really need varnoold/varoattno, but they are very useful
* for debugging and interpreting completed plans, so we keep them around.
- * ----------------
*/
#define INNER 65000
#define OUTER 65001
AttrNumber varoattno; /* original value of varattno */
} Var;
-/*
+/*--------------------
* Oper
*
* NOTE: in the good old days 'opno' used to be both (or either, or
* Note also that opid is not necessarily filled in immediately on creation
* of the node. The planner makes sure it is valid before passing the node
* tree to the executor, but during parsing/planning opid is typically 0.
- *
+ *--------------------
*/
typedef struct Oper
{
* are not equivalent to ON() since they also affect the output column list.
*
* alias is an Attr node representing the AS alias-clause attached to the
- * join expression, or NULL if no clause. During parse analysis, colnames
- * is filled with a list of String nodes giving the column names (real or
- * alias) of the output of the join, and colvars is filled with a list of
- * expressions that can be copied to reference the output columns.
+ * join expression, or NULL if no clause. NB: presence or absence of the
+ * alias has a critical impact on semantics, because a join with an alias
+ * restricts visibility of the tables/columns inside it.
+ *
+ * During parse analysis, colnames is filled with a list of String nodes
+ * giving the column names (real or alias) of the output of the join,
+ * and colvars is filled with a list of expressions that can be copied to
+ * reference the output columns.
*----------
*/
typedef struct JoinExpr
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: parse_clause.h,v 1.22 2001/01/24 19:43:27 momjian Exp $
+ * $Id: parse_clause.h,v 1.23 2001/02/14 21:35:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "parser/parse_node.h"
-extern void makeRangeTable(ParseState *pstate, List *frmList);
-extern void lockTargetTable(ParseState *pstate, char *relname);
-extern void setTargetTable(ParseState *pstate, char *relname,
- bool inh, bool inJoinSet);
+extern void transformFromClause(ParseState *pstate, List *frmList);
+extern int setTargetTable(ParseState *pstate, char *relname,
+ bool inh, bool alsoSource);
extern bool interpretInhOption(InhOption inhOpt);
extern Node *transformWhereClause(ParseState *pstate, Node *where);
extern List *transformGroupClause(ParseState *pstate, List *grouplist,
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: parse_node.h,v 1.24 2001/01/24 19:43:27 momjian Exp $
+ * $Id: parse_node.h,v 1.25 2001/02/14 21:35:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/*
* State information used during parse analysis
+ *
+ * p_rtable: list of RTEs that will become the rangetable of the query.
+ * Note that neither relname nor refname of these entries are necessarily
+ * unique; searching the rtable by name is a bad idea.
+ *
+ * p_joinlist: list of join items (RangeTblRef and JoinExpr nodes) that
+ * will become the fromlist of the query's top-level FromExpr node.
+ *
+ * p_namespace: list of join items that represents the current namespace
+ * for table and column lookup. This may be just a subset of the rtable +
+ * joinlist, and/or may contain entries that are not yet added to the main
+ * joinlist. Note that an RTE that is present in p_namespace, but does not
+ * have its inFromCl flag set, is accessible only with an explicit qualifier;
+ * lookups of unqualified column names should ignore it.
*/
typedef struct ParseState
{
List *p_rtable; /* range table so far */
List *p_joinlist; /* join items so far (will become
* FromExpr node's fromlist) */
+ List *p_namespace; /* current lookup namespace (join items) */
int p_last_resno; /* last targetlist resno assigned */
List *p_forUpdate; /* FOR UPDATE clause, if any (see gram.y) */
bool p_hasAggs;
extern Var *make_var(ParseState *pstate, RangeTblEntry *rte, int attrno);
extern ArrayRef *transformArraySubscripts(ParseState *pstate,
Node *arrayBase,
+ Oid arrayType,
List *indirection,
bool forceSlice,
Node *assignFrom);
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: parse_relation.h,v 1.21 2001/01/24 19:43:27 momjian Exp $
+ * $Id: parse_relation.h,v 1.22 2001/02/14 21:35:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern Node *refnameRangeOrJoinEntry(ParseState *pstate,
char *refname,
int *sublevels_up);
-extern RangeTblEntry *refnameRangeTableEntry(ParseState *pstate,
- char *refname);
-extern int refnameRangeTablePosn(ParseState *pstate,
- char *refname,
- int *sublevels_up);
+extern void checkNameSpaceConflicts(ParseState *pstate, Node *namespace1,
+ Node *namespace2);
extern int RTERangeTablePosn(ParseState *pstate,
RangeTblEntry *rte,
int *sublevels_up);
-extern JoinExpr *scanJoinListForRefname(Node *jtnode, char *refname);
extern Node *colnameToVar(ParseState *pstate, char *colname);
extern Node *qualifiedNameToVar(ParseState *pstate, char *refname,
char *colname, bool implicitRTEOK);
Query *subquery,
Attr *alias,
bool inFromCl);
-extern void addRTEtoJoinList(ParseState *pstate, RangeTblEntry *rte);
+extern void addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte,
+ bool addToJoinList, bool addToNameSpace);
extern RangeTblEntry *addImplicitRTE(ParseState *pstate, char *relname);
extern void expandRTE(ParseState *pstate, RangeTblEntry *rte,
List **colnames, List **colvars);
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: builtins.h,v 1.146 2001/01/24 19:43:28 momjian Exp $
+ * $Id: builtins.h,v 1.147 2001/02/14 21:35:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern Datum pg_get_viewdef(PG_FUNCTION_ARGS);
extern Datum pg_get_indexdef(PG_FUNCTION_ARGS);
extern Datum pg_get_userbyid(PG_FUNCTION_ARGS);
-extern char *deparse_expression(Node *expr, List *rangetables,
+extern char *deparse_expression(Node *expr, List *dpcontext,
bool forceprefix);
+extern List *deparse_context_for(char *relname, Oid relid);
/* selfuncs.c */
extern Datum eqsel(PG_FUNCTION_ARGS);
HREF="../../include/nodes/parsenodes.h"> RangeTableEntry,</A> and they
are linked together to form the <I>range table</I> of the query, which
is generated by <A HREF="../../backend/parser/parse_clause.c">
-makeRangeTable().</A> Query.rtable holds the query's range table.<P>
+transformFromClause().</A> Query.rtable holds the query's range table.<P>
Certain queries, like <I>SELECT,</I> return columns of data. Other