mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-12-21 08:29:39 +08:00
Change scoping of table and join refnames to conform to SQL92: a JOIN
clause with an alias is a <subquery> and therefore hides table references appearing within it, according to the spec. This is the same as the preliminary patch I posted to pgsql-patches yesterday, plus some really grotty code in ruleutils.c to reverse-list a query tree with the correct alias name depending on context. I'd rather not have done that, but unless we want to force another initdb for 7.1, there's no other way for now.
This commit is contained in:
parent
d42d31e78e
commit
4a66f9dd54
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* 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
|
||||
@ -1533,7 +1533,6 @@ StoreAttrDefault(Relation rel, AttrNumber attnum, char *adbin,
|
||||
bool updatePgAttribute)
|
||||
{
|
||||
Node *expr;
|
||||
RangeTblEntry *rte;
|
||||
char *adsrc;
|
||||
Relation adrel;
|
||||
Relation idescs[Num_pg_attrdef_indices];
|
||||
@ -1551,16 +1550,12 @@ StoreAttrDefault(Relation rel, AttrNumber attnum, char *adbin,
|
||||
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;
|
||||
@ -1619,7 +1614,6 @@ static void
|
||||
StoreRelCheck(Relation rel, char *ccname, char *ccbin)
|
||||
{
|
||||
Node *expr;
|
||||
RangeTblEntry *rte;
|
||||
char *ccsrc;
|
||||
Relation rcrel;
|
||||
Relation idescs[Num_pg_relcheck_indices];
|
||||
@ -1634,16 +1628,12 @@ StoreRelCheck(Relation rel, char *ccname, char *ccbin)
|
||||
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,
|
||||
@ -1764,9 +1754,8 @@ AddRelationRawConstraints(Relation rel,
|
||||
* 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.
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* 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
|
||||
@ -1136,10 +1136,9 @@ AlterTableAddConstraint(char *relationName,
|
||||
* 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,
|
||||
|
@ -6,7 +6,7 @@
|
||||
* 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 $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -257,11 +257,10 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
|
||||
|
||||
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;
|
||||
|
||||
@ -271,7 +270,6 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
|
||||
/* 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;
|
||||
@ -289,6 +287,8 @@ static Query *
|
||||
transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
|
||||
{
|
||||
Query *qry = makeNode(Query);
|
||||
List *sub_rtable;
|
||||
List *sub_namespace;
|
||||
List *icolumns;
|
||||
List *attrnos;
|
||||
List *attnos;
|
||||
@ -300,11 +300,35 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
|
||||
pstate->p_is_insert = true;
|
||||
|
||||
/*
|
||||
* Must get write lock on 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.
|
||||
* 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.
|
||||
*/
|
||||
lockTargetTable(pstate, stmt->relname);
|
||||
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. Note that the target
|
||||
* table is not added to the joinlist or namespace.
|
||||
*/
|
||||
qry->resultRelation = setTargetTable(pstate, stmt->relname,
|
||||
false, false);
|
||||
|
||||
/*
|
||||
* Is it INSERT ... SELECT or INSERT ... VALUES?
|
||||
@ -323,15 +347,12 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
|
||||
* 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);
|
||||
|
||||
@ -341,7 +362,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
|
||||
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,
|
||||
@ -400,13 +421,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
|
||||
/*
|
||||
* 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)
|
||||
@ -495,7 +510,6 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
|
||||
/* 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;
|
||||
@ -1565,27 +1579,27 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
|
||||
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",
|
||||
@ -1638,8 +1652,9 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
|
||||
* 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),
|
||||
@ -1649,6 +1664,8 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
|
||||
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));
|
||||
@ -1712,10 +1729,10 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt)
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
@ -1779,8 +1796,8 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
|
||||
/* 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);
|
||||
@ -2055,7 +2072,6 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt)
|
||||
if (isLeaf)
|
||||
{
|
||||
/* Process leaf SELECT */
|
||||
List *save_rtable;
|
||||
List *selectList;
|
||||
Query *selectQuery;
|
||||
char selectName[32];
|
||||
@ -2063,16 +2079,13 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt)
|
||||
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);
|
||||
@ -2202,19 +2215,15 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
|
||||
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);
|
||||
|
||||
@ -2222,7 +2231,6 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
|
||||
|
||||
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;
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* 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 $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -58,26 +58,30 @@ static bool exprIsInSortList(Node *expr, List *sortList, List *targetList);
|
||||
|
||||
|
||||
/*
|
||||
* 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
|
||||
* 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.
|
||||
*
|
||||
* 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.)
|
||||
*
|
||||
* 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!
|
||||
*/
|
||||
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)
|
||||
{
|
||||
@ -85,27 +89,41 @@ makeRangeTable(ParseState *pstate, List *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.
|
||||
*
|
||||
* The ParseState's link to the target relcache entry is also set here.
|
||||
* 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.
|
||||
*
|
||||
* 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
|
||||
@ -115,62 +133,36 @@ lockTargetTable(ParseState *pstate, char *relname)
|
||||
* 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);
|
||||
/*
|
||||
* If UPDATE/DELETE, add table to joinlist and namespace.
|
||||
*/
|
||||
if (alsoSource)
|
||||
addRTEtoQuery(pstate, rte, true, true);
|
||||
|
||||
/* lockTargetTable should have been called earlier */
|
||||
Assert(pstate->p_target_relation != NULL);
|
||||
|
||||
pstate->p_target_rangetblentry = rte;
|
||||
return rtindex;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -313,22 +305,21 @@ transformJoinOnClause(ParseState *pstate, JoinExpr *j,
|
||||
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);
|
||||
@ -338,14 +329,14 @@ transformJoinOnClause(ParseState *pstate, JoinExpr *j,
|
||||
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)
|
||||
@ -384,8 +375,8 @@ transformTableEntry(ParseState *pstate, RangeVar *r)
|
||||
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 */
|
||||
@ -402,8 +393,7 @@ transformTableEntry(ParseState *pstate, RangeVar *r)
|
||||
static RangeTblRef *
|
||||
transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
|
||||
{
|
||||
List *save_rtable;
|
||||
List *save_joinlist;
|
||||
List *save_namespace;
|
||||
List *parsetrees;
|
||||
Query *query;
|
||||
RangeTblEntry *rte;
|
||||
@ -424,15 +414,12 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
|
||||
* 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
|
||||
@ -456,8 +443,8 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
|
||||
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 */
|
||||
@ -472,7 +459,7 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
|
||||
* 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)
|
||||
@ -525,6 +512,13 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
|
||||
*/
|
||||
*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
|
||||
*/
|
||||
@ -733,23 +727,9 @@ transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels)
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* 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 $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -541,7 +541,8 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
|
||||
{
|
||||
if (indirection == NIL)
|
||||
return basenode;
|
||||
return (Node *) transformArraySubscripts(pstate, basenode,
|
||||
return (Node *) transformArraySubscripts(pstate,
|
||||
basenode, exprType(basenode),
|
||||
indirection, false, NULL);
|
||||
}
|
||||
|
||||
@ -558,13 +559,14 @@ static Node *
|
||||
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;
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* 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 $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -427,6 +427,7 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
|
||||
{
|
||||
RangeTblEntry *rte;
|
||||
int vnum;
|
||||
Node *rteorjoin;
|
||||
int sublevels_up;
|
||||
|
||||
/*
|
||||
@ -434,9 +435,29 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
|
||||
*/
|
||||
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);
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* 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 $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -229,20 +229,22 @@ make_var(ParseState *pstate, RangeTblEntry *rte, int attrno)
|
||||
*
|
||||
* 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,
|
||||
@ -254,28 +256,26 @@ transformArraySubscripts(ParseState *pstate,
|
||||
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);
|
||||
|
||||
/*
|
||||
@ -308,9 +308,9 @@ transformArraySubscripts(ParseState *pstate,
|
||||
* 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.
|
||||
@ -359,7 +359,7 @@ transformArraySubscripts(ParseState *pstate,
|
||||
if (assignFrom != NULL)
|
||||
{
|
||||
Oid typesource = exprType(assignFrom);
|
||||
Oid typeneeded = isSlice ? typearray : typeelement;
|
||||
Oid typeneeded = isSlice ? arrayType : elementType;
|
||||
|
||||
if (typesource != InvalidOid)
|
||||
{
|
||||
@ -385,7 +385,7 @@ transformArraySubscripts(ParseState *pstate,
|
||||
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;
|
||||
|
@ -8,14 +8,14 @@
|
||||
*
|
||||
*
|
||||
* 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"
|
||||
@ -30,6 +30,8 @@
|
||||
#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,
|
||||
@ -93,25 +95,13 @@ refnameRangeOrJoinEntry(ParseState *pstate,
|
||||
|
||||
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)
|
||||
@ -123,108 +113,129 @@ refnameRangeOrJoinEntry(ParseState *pstate,
|
||||
}
|
||||
|
||||
/*
|
||||
* Recursively search a joinlist for a joinexpr with given refname
|
||||
* Recursively search a namespace for an RTE or joinexpr with given refname.
|
||||
*
|
||||
* 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.
|
||||
* 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: 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))
|
||||
{
|
||||
int varno = ((RangeTblRef *) nsnode)->rtindex;
|
||||
RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable);
|
||||
|
||||
if (strcmp(rte->eref->relname, refname) == 0)
|
||||
result = (Node *) rte;
|
||||
}
|
||||
else if (IsA(nsnode, JoinExpr))
|
||||
{
|
||||
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(nsnode, List))
|
||||
{
|
||||
List *l;
|
||||
|
||||
foreach(l, (List *) jtnode)
|
||||
foreach(l, (List *) nsnode)
|
||||
{
|
||||
result = scanJoinListForRefname(lfirst(l), refname);
|
||||
result = scanNameSpaceForRefname(pstate, lfirst(l), refname);
|
||||
if (result)
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (IsA(jtnode, RangeTblRef))
|
||||
{
|
||||
/* ignore ... */
|
||||
}
|
||||
else if (IsA(jtnode, JoinExpr))
|
||||
{
|
||||
JoinExpr *j = (JoinExpr *) jtnode;
|
||||
|
||||
if (j->alias && strcmp(j->alias->relname, refname) == 0)
|
||||
return j;
|
||||
result = scanJoinListForRefname(j->larg, refname);
|
||||
if (! result)
|
||||
result = scanJoinListForRefname(j->rarg, refname);
|
||||
}
|
||||
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.
|
||||
*
|
||||
* NOTE that this routine will ONLY find RTEs, not join tables.
|
||||
* 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: 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;
|
||||
|
||||
while (pstate != NULL)
|
||||
if (namespace1 == NULL)
|
||||
return;
|
||||
if (IsA(namespace1, RangeTblRef))
|
||||
{
|
||||
index = 1;
|
||||
foreach(temp, pstate->p_rtable)
|
||||
{
|
||||
RangeTblEntry *rte = lfirst(temp);
|
||||
int varno = ((RangeTblRef *) namespace1)->rtindex;
|
||||
RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable);
|
||||
|
||||
if (strcmp(rte->eref->relname, refname) == 0)
|
||||
return index;
|
||||
index++;
|
||||
}
|
||||
pstate = pstate->parentParseState;
|
||||
if (sublevels_up)
|
||||
(*sublevels_up)++;
|
||||
else
|
||||
break;
|
||||
scanNameSpaceForConflict(pstate, namespace2, rte->eref->relname);
|
||||
}
|
||||
return 0;
|
||||
else if (IsA(namespace1, JoinExpr))
|
||||
{
|
||||
JoinExpr *j = (JoinExpr *) namespace1;
|
||||
|
||||
if (j->alias)
|
||||
{
|
||||
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;
|
||||
|
||||
foreach(l, (List *) namespace1)
|
||||
{
|
||||
checkNameSpaceConflicts(pstate, lfirst(l), namespace2);
|
||||
}
|
||||
}
|
||||
else
|
||||
elog(ERROR, "checkNameSpaceConflicts: unexpected node type %d",
|
||||
nodeTag(namespace1));
|
||||
}
|
||||
|
||||
/*
|
||||
@ -257,6 +268,7 @@ RTERangeTablePosn(ParseState *pstate, RangeTblEntry *rte, int *sublevels_up)
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
elog(ERROR, "RTERangeTablePosn: RTE not found (internal error)");
|
||||
return 0; /* keep compiler quiet */
|
||||
}
|
||||
@ -369,21 +381,21 @@ colnameToVar(ParseState *pstate, char *colname)
|
||||
|
||||
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 &&
|
||||
@ -393,15 +405,15 @@ colnameToVar(ParseState *pstate, char *colname)
|
||||
/* 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)
|
||||
{
|
||||
@ -451,7 +463,7 @@ qualifiedNameToVar(ParseState *pstate, char *refname, char *colname,
|
||||
colname);
|
||||
else if (IsA(rteorjoin, JoinExpr))
|
||||
result = scanJoinForColumn((JoinExpr *) rteorjoin,
|
||||
colname, sublevels_up);
|
||||
colname, sublevels_up);
|
||||
else
|
||||
{
|
||||
elog(ERROR, "qualifiedNameToVar: unexpected node type %d",
|
||||
@ -465,10 +477,11 @@ qualifiedNameToVar(ParseState *pstate, char *refname, char *colname,
|
||||
/*
|
||||
* 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,
|
||||
@ -477,27 +490,15 @@ 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;
|
||||
@ -559,7 +560,8 @@ addRangeTableEntry(ParseState *pstate,
|
||||
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);
|
||||
@ -579,25 +581,13 @@ addRangeTableEntryForSubquery(ParseState *pstate,
|
||||
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;
|
||||
@ -647,7 +637,8 @@ addRangeTableEntryForSubquery(ParseState *pstate,
|
||||
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);
|
||||
@ -691,37 +682,30 @@ isForUpdate(ParseState *pstate, char *relname)
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
RangeTblRef *rtr = makeNode(RangeTblRef);
|
||||
|
||||
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 */
|
||||
}
|
||||
}
|
||||
|
||||
/* 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)
|
||||
@ -729,7 +713,7 @@ 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;
|
||||
@ -922,6 +906,11 @@ expandNamesVars(ParseState *pstate, List *names, List *vars)
|
||||
* 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 *
|
||||
@ -1088,4 +1077,3 @@ warnAutoRange(ParseState *pstate, char *refname)
|
||||
pstate->parentParseState != NULL ? " in subquery" : "",
|
||||
refname);
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* 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 $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -212,29 +212,37 @@ updateTargetListEntry(ParseState *pstate,
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
aref->refexpr = NULL; /* signal there is no source array */
|
||||
arrayBase = NULL; /* signal there is no source array */
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Build a Var for the array to be updated.
|
||||
*/
|
||||
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
|
||||
@ -385,22 +393,19 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos)
|
||||
/* 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))
|
||||
{
|
||||
@ -431,6 +436,10 @@ ExpandAllTables(ParseState *pstate)
|
||||
"\n\t%s", nodeToString(n));
|
||||
}
|
||||
|
||||
/* Check for SELECT *; */
|
||||
if (target == NIL)
|
||||
elog(ERROR, "Wildcard with no tables specified not allowed");
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
* 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.
|
||||
*
|
||||
@ -35,11 +35,11 @@
|
||||
*
|
||||
**********************************************************************/
|
||||
|
||||
#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"
|
||||
@ -52,6 +52,7 @@
|
||||
#include "parser/parse_expr.h"
|
||||
#include "parser/parsetree.h"
|
||||
#include "rewrite/rewriteManip.h"
|
||||
#include "utils/fmgroids.h"
|
||||
#include "utils/lsyscache.h"
|
||||
|
||||
|
||||
@ -59,13 +60,30 @@
|
||||
* 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
|
||||
@ -92,7 +110,7 @@ static char *query_getopclass = "SELECT * FROM pg_opclass WHERE oid = $1";
|
||||
*/
|
||||
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);
|
||||
@ -102,7 +120,14 @@ static void get_basic_select_query(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);
|
||||
@ -599,30 +624,24 @@ pg_get_userbyid(PG_FUNCTION_ARGS)
|
||||
* 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 */
|
||||
|
||||
@ -631,6 +650,43 @@ deparse_expression(Node *expr, List *rangetables, bool forceprefix)
|
||||
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
|
||||
@ -722,6 +778,7 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc)
|
||||
Node *qual;
|
||||
Query *query;
|
||||
deparse_context context;
|
||||
deparse_namespace dpns;
|
||||
|
||||
appendStringInfo(buf, " WHERE ");
|
||||
|
||||
@ -729,8 +786,10 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc)
|
||||
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);
|
||||
}
|
||||
@ -844,14 +903,17 @@ make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc)
|
||||
* ----------
|
||||
*/
|
||||
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)
|
||||
{
|
||||
@ -1025,11 +1087,10 @@ get_basic_select_query(Query *query, deparse_context *context)
|
||||
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);
|
||||
}
|
||||
|
||||
@ -1088,7 +1149,7 @@ get_setop_query(Node *setOp, Query *query, deparse_context *context,
|
||||
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))
|
||||
{
|
||||
@ -1336,20 +1397,277 @@ get_utility_query_def(Query *query, deparse_context *context)
|
||||
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
while (sup-- > 0)
|
||||
rtlist = lnext(rtlist);
|
||||
/* 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);
|
||||
|
||||
return rt_fetch(var->varno, (List *) lfirst(rtlist));
|
||||
/* 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;
|
||||
|
||||
/*
|
||||
* 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 ...
|
||||
*/
|
||||
|
||||
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
|
||||
@ -1381,21 +1699,21 @@ get_rule_expr(Node *node, deparse_context *context)
|
||||
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;
|
||||
|
||||
@ -1606,6 +1924,19 @@ get_rule_expr(Node *node, deparse_context *context)
|
||||
{
|
||||
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)
|
||||
@ -1645,6 +1976,7 @@ get_func_expr(Expr *expr, deparse_context *context)
|
||||
{
|
||||
StringInfo buf = context->buf;
|
||||
Func *func = (Func *) (expr->oper);
|
||||
Oid funcoid = func->funcid;
|
||||
HeapTuple proctup;
|
||||
Form_pg_proc procStruct;
|
||||
char *proname;
|
||||
@ -1652,41 +1984,36 @@ get_func_expr(Expr *expr, deparse_context *context)
|
||||
List *l;
|
||||
char *sep;
|
||||
|
||||
/*
|
||||
* Get the functions pg_proc tuple
|
||||
*/
|
||||
proctup = SearchSysCache(PROCOID,
|
||||
ObjectIdGetDatum(func->funcid),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(proctup))
|
||||
elog(ERROR, "cache lookup for proc %u failed", func->funcid);
|
||||
|
||||
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 (funcoid == F_NULLVALUE)
|
||||
{
|
||||
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;
|
||||
}
|
||||
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(funcoid),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(proctup))
|
||||
elog(ERROR, "cache lookup for proc %u failed", funcoid);
|
||||
|
||||
procStruct = (Form_pg_proc) GETSTRUCT(proctup);
|
||||
proname = NameStr(procStruct->proname);
|
||||
|
||||
/*
|
||||
* Check to see if function is a length-coercion function for some
|
||||
@ -1968,7 +2295,7 @@ get_sublink_expr(Node *node, deparse_context *context)
|
||||
if (need_paren)
|
||||
appendStringInfoChar(buf, '(');
|
||||
|
||||
get_query_def(query, buf, context->rangetables);
|
||||
get_query_def(query, buf, context->namespaces);
|
||||
|
||||
if (need_paren)
|
||||
appendStringInfo(buf, "))");
|
||||
@ -2024,6 +2351,16 @@ static void
|
||||
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))
|
||||
{
|
||||
@ -2042,7 +2379,7 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
|
||||
/* 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)
|
||||
@ -2053,7 +2390,7 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
|
||||
{
|
||||
List *col;
|
||||
|
||||
appendStringInfo(buf, " (");
|
||||
appendStringInfo(buf, "(");
|
||||
foreach(col, rte->alias->attrs)
|
||||
{
|
||||
if (col != rte->alias->attrs)
|
||||
@ -2116,6 +2453,7 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
|
||||
}
|
||||
else if (j->quals)
|
||||
{
|
||||
dpns->namespace = makeList2(j->larg, j->rarg);
|
||||
appendStringInfo(buf, " ON (");
|
||||
get_rule_expr(j->quals, context);
|
||||
appendStringInfoChar(buf, ')');
|
||||
@ -2131,7 +2469,7 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
|
||||
{
|
||||
List *col;
|
||||
|
||||
appendStringInfo(buf, " (");
|
||||
appendStringInfo(buf, "(");
|
||||
foreach(col, j->alias->attrs)
|
||||
{
|
||||
if (col != j->alias->attrs)
|
||||
@ -2146,6 +2484,8 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
|
||||
else
|
||||
elog(ERROR, "get_from_clause_item: unexpected node type %d",
|
||||
nodeTag(jtnode));
|
||||
|
||||
dpns->namespace = sv_namespace;
|
||||
}
|
||||
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
* 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 $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -29,7 +29,7 @@ typedef struct FunctionCache *FunctionCachePtr;
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
/*--------------------
|
||||
* Resdom (Result Domain)
|
||||
*
|
||||
* Notes:
|
||||
@ -50,7 +50,7 @@ typedef struct FunctionCache *FunctionCachePtr;
|
||||
*
|
||||
* Both reskey and reskeyop are typically zero during parse/plan stages.
|
||||
* The executor does not pay any attention to ressortgroupref.
|
||||
*
|
||||
*--------------------
|
||||
*/
|
||||
typedef struct Resdom
|
||||
{
|
||||
@ -129,7 +129,6 @@ typedef struct Expr
|
||||
* 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
|
||||
@ -153,7 +152,7 @@ typedef struct Var
|
||||
AttrNumber varoattno; /* original value of varattno */
|
||||
} Var;
|
||||
|
||||
/*
|
||||
/*--------------------
|
||||
* Oper
|
||||
*
|
||||
* NOTE: in the good old days 'opno' used to be both (or either, or
|
||||
@ -169,7 +168,7 @@ typedef struct Var
|
||||
* 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
|
||||
{
|
||||
@ -499,10 +498,14 @@ typedef struct RangeTblRef
|
||||
* 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
|
||||
|
@ -7,7 +7,7 @@
|
||||
* 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 $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -16,10 +16,9 @@
|
||||
|
||||
#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,
|
||||
|
@ -6,7 +6,7 @@
|
||||
* 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 $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -18,6 +18,20 @@
|
||||
|
||||
/*
|
||||
* 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
|
||||
{
|
||||
@ -25,6 +39,7 @@ 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;
|
||||
@ -42,6 +57,7 @@ extern Node *make_operand(char *opname, Node *tree,
|
||||
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);
|
||||
|
@ -7,7 +7,7 @@
|
||||
* 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 $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -19,15 +19,11 @@
|
||||
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);
|
||||
@ -40,7 +36,8 @@ extern RangeTblEntry *addRangeTableEntryForSubquery(ParseState *pstate,
|
||||
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);
|
||||
|
@ -7,7 +7,7 @@
|
||||
* 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 $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -325,8 +325,9 @@ extern Datum pg_get_ruledef(PG_FUNCTION_ARGS);
|
||||
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);
|
||||
|
@ -67,7 +67,7 @@ Each table referenced in the query is represented by a <A
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user