mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-01-18 18:44:06 +08:00
Cause schema-qualified FROM items and schema-qualified variable references
to behave according to SQL92 (or according to my current understanding of same, anyway). Per pghackers discussion way back in March 2002: thread 'Do FROM items of different schemas conflict?'
This commit is contained in:
parent
e42f8e32e9
commit
b084cc3504
@ -13,7 +13,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/catalog/namespace.c,v 1.28 2002/08/06 05:40:44 ishii Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/catalog/namespace.c,v 1.29 2002/08/08 01:44:30 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -1080,7 +1080,7 @@ DeconstructQualifiedName(List *names,
|
||||
* Returns the namespace OID. Raises elog if any problem.
|
||||
*/
|
||||
Oid
|
||||
LookupExplicitNamespace(char *nspname)
|
||||
LookupExplicitNamespace(const char *nspname)
|
||||
{
|
||||
Oid namespaceId;
|
||||
AclResult aclresult;
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.124 2002/08/04 06:46:12 thomas Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.125 2002/08/08 01:44:30 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -709,6 +709,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
|
||||
|
||||
/* Try to identify as an unqualified column */
|
||||
node = colnameToVar(pstate, name);
|
||||
|
||||
if (node == NULL)
|
||||
{
|
||||
/*
|
||||
@ -716,11 +717,15 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
|
||||
* try to find the name as a relation ... but not if
|
||||
* subscripts appear. Note also that only relations
|
||||
* already entered into the rangetable will be recognized.
|
||||
*
|
||||
* This is a hack for backwards compatibility with PostQUEL-
|
||||
* inspired syntax. The preferred form now is "rel.*".
|
||||
*/
|
||||
int levels_up;
|
||||
|
||||
if (cref->indirection == NIL &&
|
||||
refnameRangeTblEntry(pstate, name, &levels_up) != NULL)
|
||||
refnameRangeTblEntry(pstate, NULL, name,
|
||||
&levels_up) != NULL)
|
||||
{
|
||||
rv = makeNode(RangeVar);
|
||||
rv->relname = name;
|
||||
@ -748,7 +753,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
|
||||
}
|
||||
|
||||
/* Try to identify as a once-qualified column */
|
||||
node = qualifiedNameToVar(pstate, name1, name2, true);
|
||||
node = qualifiedNameToVar(pstate, NULL, name1, name2, true);
|
||||
if (node == NULL)
|
||||
{
|
||||
/*
|
||||
@ -784,8 +789,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
|
||||
}
|
||||
|
||||
/* Try to identify as a twice-qualified column */
|
||||
/* XXX do something with schema name here */
|
||||
node = qualifiedNameToVar(pstate, name2, name3, true);
|
||||
node = qualifiedNameToVar(pstate, name1, name2, name3, true);
|
||||
if (node == NULL)
|
||||
{
|
||||
/* Try it as a function call */
|
||||
@ -825,8 +829,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
|
||||
}
|
||||
|
||||
/* Try to identify as a twice-qualified column */
|
||||
/* XXX do something with schema name here */
|
||||
node = qualifiedNameToVar(pstate, name3, name4, true);
|
||||
node = qualifiedNameToVar(pstate, name2, name3, name4, true);
|
||||
if (node == NULL)
|
||||
{
|
||||
/* Try it as a function call */
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.133 2002/08/02 18:15:07 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.134 2002/08/08 01:44:30 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -51,6 +51,8 @@ static int match_argtypes(int nargs,
|
||||
static FieldSelect *setup_field_select(Node *input, char *attname, Oid relid);
|
||||
static FuncCandidateList func_select_candidate(int nargs, Oid *input_typeids,
|
||||
FuncCandidateList candidates);
|
||||
static void unknown_attribute(const char *schemaname, const char *relname,
|
||||
const char *attname);
|
||||
|
||||
|
||||
/*
|
||||
@ -80,7 +82,6 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
|
||||
Oid funcid;
|
||||
List *i;
|
||||
Node *first_arg = NULL;
|
||||
char *refname;
|
||||
int nargs = length(fargs);
|
||||
int argn;
|
||||
Oid oid_array[FUNC_MAX_ARGS];
|
||||
@ -121,10 +122,11 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
|
||||
if (IsA(first_arg, RangeVar))
|
||||
{
|
||||
/* First arg is a relation. This could be a projection. */
|
||||
refname = ((RangeVar *) first_arg)->relname;
|
||||
|
||||
/* XXX WRONG: ignores possible qualification of argument */
|
||||
retval = qualifiedNameToVar(pstate, refname, cname, true);
|
||||
retval = qualifiedNameToVar(pstate,
|
||||
((RangeVar *) first_arg)->schemaname,
|
||||
((RangeVar *) first_arg)->relname,
|
||||
cname,
|
||||
true);
|
||||
if (retval)
|
||||
return retval;
|
||||
}
|
||||
@ -156,16 +158,19 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
|
||||
|
||||
if (IsA(arg, RangeVar))
|
||||
{
|
||||
char *schemaname;
|
||||
char *relname;
|
||||
RangeTblEntry *rte;
|
||||
int vnum;
|
||||
int sublevels_up;
|
||||
|
||||
/*
|
||||
* a relation
|
||||
* a relation: look it up in the range table, or add if needed
|
||||
*/
|
||||
refname = ((RangeVar *) arg)->relname;
|
||||
schemaname = ((RangeVar *) arg)->schemaname;
|
||||
relname = ((RangeVar *) arg)->relname;
|
||||
|
||||
rte = refnameRangeTblEntry(pstate, refname,
|
||||
rte = refnameRangeTblEntry(pstate, schemaname, relname,
|
||||
&sublevels_up);
|
||||
|
||||
if (rte == NULL)
|
||||
@ -199,11 +204,11 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
|
||||
* named tuple type
|
||||
*/
|
||||
if (is_column)
|
||||
elog(ERROR, "No such attribute %s.%s",
|
||||
refname, strVal(lfirst(funcname)));
|
||||
unknown_attribute(schemaname, relname,
|
||||
strVal(lfirst(funcname)));
|
||||
else
|
||||
elog(ERROR, "Cannot pass result of sub-select or join %s to a function",
|
||||
refname);
|
||||
relname);
|
||||
toid = InvalidOid; /* keep compiler quiet */
|
||||
break;
|
||||
}
|
||||
@ -268,8 +273,9 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
|
||||
|
||||
Assert(nargs == 1);
|
||||
if (IsA(first_arg, RangeVar))
|
||||
elog(ERROR, "No such attribute %s.%s",
|
||||
((RangeVar *) first_arg)->relname, colname);
|
||||
unknown_attribute(((RangeVar *) first_arg)->schemaname,
|
||||
((RangeVar *) first_arg)->relname,
|
||||
colname);
|
||||
relTypeId = exprType(first_arg);
|
||||
if (!ISCOMPLEX(relTypeId))
|
||||
elog(ERROR, "Attribute notation .%s applied to type %s, which is not a complex type",
|
||||
@ -1225,6 +1231,21 @@ ParseComplexProjection(ParseState *pstate,
|
||||
return (Node *) fselect;
|
||||
}
|
||||
|
||||
/*
|
||||
* Simple helper routine for delivering "No such attribute" error message
|
||||
*/
|
||||
static void
|
||||
unknown_attribute(const char *schemaname, const char *relname,
|
||||
const char *attname)
|
||||
{
|
||||
if (schemaname)
|
||||
elog(ERROR, "No such attribute %s.%s.%s",
|
||||
schemaname, relname, attname);
|
||||
else
|
||||
elog(ERROR, "No such attribute %s.%s",
|
||||
relname, attname);
|
||||
}
|
||||
|
||||
/*
|
||||
* Error message when function lookup fails that gives details of the
|
||||
* argument types
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.75 2002/08/06 05:34:10 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.76 2002/08/08 01:44:30 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -19,6 +19,7 @@
|
||||
#include "access/heapam.h"
|
||||
#include "access/htup.h"
|
||||
#include "catalog/heap.h"
|
||||
#include "catalog/namespace.h"
|
||||
#include "catalog/pg_type.h"
|
||||
#include "nodes/makefuncs.h"
|
||||
#include "parser/parsetree.h"
|
||||
@ -33,7 +34,11 @@
|
||||
|
||||
|
||||
static Node *scanNameSpaceForRefname(ParseState *pstate, Node *nsnode,
|
||||
char *refname);
|
||||
const char *refname);
|
||||
static Node *scanNameSpaceForRelid(ParseState *pstate, Node *nsnode,
|
||||
Oid relid);
|
||||
static void scanNameSpaceForConflict(ParseState *pstate, Node *nsnode,
|
||||
RangeTblEntry *rte1, const char *aliasname1);
|
||||
static Node *scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte,
|
||||
char *colname);
|
||||
static bool isForUpdate(ParseState *pstate, char *refname);
|
||||
@ -45,26 +50,58 @@ static void warnAutoRange(ParseState *pstate, RangeVar *relation);
|
||||
|
||||
/*
|
||||
* refnameRangeTblEntry
|
||||
* Given a refname, look to see if it matches any RTE.
|
||||
* If so, return a pointer to the RangeTblEntry.
|
||||
* Optionally get its nesting depth (0 = current). If sublevels_up
|
||||
* is NULL, only consider items at the current nesting level.
|
||||
* Given a possibly-qualified refname, look to see if it matches any RTE.
|
||||
* If so, return a pointer to the RangeTblEntry; else return NULL.
|
||||
*
|
||||
* Optionally get RTE's nesting depth (0 = current) into *sublevels_up.
|
||||
* If sublevels_up is NULL, only consider items at the current nesting
|
||||
* level.
|
||||
*
|
||||
* An unqualified refname (schemaname == NULL) can match any RTE with matching
|
||||
* alias, or matching unqualified relname in the case of alias-less relation
|
||||
* RTEs. It is possible that such a refname matches multiple RTEs in the
|
||||
* nearest nesting level that has a match; if so, we report an error via elog.
|
||||
*
|
||||
* A qualified refname (schemaname != NULL) can only match a relation RTE
|
||||
* that (a) has no alias and (b) is for the same relation identified by
|
||||
* schemaname.refname. In this case we convert schemaname.refname to a
|
||||
* relation OID and search by relid, rather than by alias name. This is
|
||||
* peculiar, but it's what SQL92 says to do.
|
||||
*/
|
||||
RangeTblEntry *
|
||||
refnameRangeTblEntry(ParseState *pstate,
|
||||
char *refname,
|
||||
const char *schemaname,
|
||||
const char *refname,
|
||||
int *sublevels_up)
|
||||
{
|
||||
Oid relId = InvalidOid;
|
||||
|
||||
if (sublevels_up)
|
||||
*sublevels_up = 0;
|
||||
|
||||
if (schemaname != NULL)
|
||||
{
|
||||
Oid namespaceId;
|
||||
|
||||
namespaceId = LookupExplicitNamespace(schemaname);
|
||||
relId = get_relname_relid(refname, namespaceId);
|
||||
if (!OidIsValid(relId))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (pstate != NULL)
|
||||
{
|
||||
Node *nsnode;
|
||||
|
||||
nsnode = scanNameSpaceForRefname(pstate,
|
||||
(Node *) pstate->p_namespace,
|
||||
refname);
|
||||
if (OidIsValid(relId))
|
||||
nsnode = scanNameSpaceForRelid(pstate,
|
||||
(Node *) pstate->p_namespace,
|
||||
relId);
|
||||
else
|
||||
nsnode = scanNameSpaceForRefname(pstate,
|
||||
(Node *) pstate->p_namespace,
|
||||
refname);
|
||||
|
||||
if (nsnode)
|
||||
{
|
||||
/* should get an RTE or JoinExpr */
|
||||
@ -84,20 +121,19 @@ refnameRangeTblEntry(ParseState *pstate,
|
||||
}
|
||||
|
||||
/*
|
||||
* Recursively search a namespace for an RTE or joinexpr with given refname.
|
||||
* Recursively search a namespace for an RTE or joinexpr matching the
|
||||
* given unqualified refname. Return the node if a unique match, or NULL
|
||||
* if no match. Raise error if multiple matches.
|
||||
*
|
||||
* 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.
|
||||
* that are not subqueries.
|
||||
*/
|
||||
static Node *
|
||||
scanNameSpaceForRefname(ParseState *pstate, Node *nsnode,
|
||||
char *refname)
|
||||
const char *refname)
|
||||
{
|
||||
Node *result = NULL;
|
||||
Node *newresult;
|
||||
|
||||
if (nsnode == NULL)
|
||||
return NULL;
|
||||
@ -126,8 +162,11 @@ scanNameSpaceForRefname(ParseState *pstate, Node *nsnode,
|
||||
return NULL;
|
||||
}
|
||||
result = scanNameSpaceForRefname(pstate, j->larg, refname);
|
||||
newresult = scanNameSpaceForRefname(pstate, j->rarg, refname);
|
||||
if (!result)
|
||||
result = scanNameSpaceForRefname(pstate, j->rarg, refname);
|
||||
result = newresult;
|
||||
else if (newresult)
|
||||
elog(ERROR, "Table reference \"%s\" is ambiguous", refname);
|
||||
}
|
||||
else if (IsA(nsnode, List))
|
||||
{
|
||||
@ -135,9 +174,11 @@ scanNameSpaceForRefname(ParseState *pstate, Node *nsnode,
|
||||
|
||||
foreach(l, (List *) nsnode)
|
||||
{
|
||||
result = scanNameSpaceForRefname(pstate, lfirst(l), refname);
|
||||
if (result)
|
||||
break;
|
||||
newresult = scanNameSpaceForRefname(pstate, lfirst(l), refname);
|
||||
if (!result)
|
||||
result = newresult;
|
||||
else if (newresult)
|
||||
elog(ERROR, "Table reference \"%s\" is ambiguous", refname);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -146,25 +187,89 @@ scanNameSpaceForRefname(ParseState *pstate, Node *nsnode,
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Convenience subroutine for checkNameSpaceConflicts */
|
||||
static void
|
||||
scanNameSpaceForConflict(ParseState *pstate, Node *nsnode,
|
||||
char *refname)
|
||||
/*
|
||||
* Recursively search a namespace for a relation RTE matching the
|
||||
* given relation OID. Return the node if a unique match, or NULL
|
||||
* if no match. Raise error if multiple matches (which shouldn't
|
||||
* happen if the namespace was checked correctly when it was created).
|
||||
*
|
||||
* The top level of p_namespace is a list, and we recurse into any joins
|
||||
* that are not subqueries.
|
||||
*
|
||||
* See the comments for refnameRangeTblEntry to understand why this
|
||||
* acts the way it does.
|
||||
*/
|
||||
static Node *
|
||||
scanNameSpaceForRelid(ParseState *pstate, Node *nsnode, Oid relid)
|
||||
{
|
||||
if (scanNameSpaceForRefname(pstate, nsnode, refname) != NULL)
|
||||
elog(ERROR, "Table name \"%s\" specified more than once", refname);
|
||||
Node *result = NULL;
|
||||
Node *newresult;
|
||||
|
||||
if (nsnode == NULL)
|
||||
return NULL;
|
||||
if (IsA(nsnode, RangeTblRef))
|
||||
{
|
||||
int varno = ((RangeTblRef *) nsnode)->rtindex;
|
||||
RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable);
|
||||
|
||||
/* yes, the test for alias==NULL should be there... */
|
||||
if (rte->rtekind == RTE_RELATION &&
|
||||
rte->relid == relid &&
|
||||
rte->alias == NULL)
|
||||
result = (Node *) rte;
|
||||
}
|
||||
else if (IsA(nsnode, JoinExpr))
|
||||
{
|
||||
JoinExpr *j = (JoinExpr *) nsnode;
|
||||
|
||||
if (j->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 = scanNameSpaceForRelid(pstate, j->larg, relid);
|
||||
newresult = scanNameSpaceForRelid(pstate, j->rarg, relid);
|
||||
if (!result)
|
||||
result = newresult;
|
||||
else if (newresult)
|
||||
elog(ERROR, "Table reference %u is ambiguous", relid);
|
||||
}
|
||||
else if (IsA(nsnode, List))
|
||||
{
|
||||
List *l;
|
||||
|
||||
foreach(l, (List *) nsnode)
|
||||
{
|
||||
newresult = scanNameSpaceForRelid(pstate, lfirst(l), relid);
|
||||
if (!result)
|
||||
result = newresult;
|
||||
else if (newresult)
|
||||
elog(ERROR, "Table reference %u is ambiguous", relid);
|
||||
}
|
||||
}
|
||||
else
|
||||
elog(ERROR, "scanNameSpaceForRelid: unexpected node type %d",
|
||||
nodeTag(nsnode));
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Recursively check for refname conflicts between two namespaces or
|
||||
* Recursively check for name conflicts between two namespaces or
|
||||
* namespace subtrees. Raise an error if any is found.
|
||||
*
|
||||
* Works by recursively scanning namespace1 in the same way that
|
||||
* scanNameSpaceForRefname does, and then looking in namespace2 for
|
||||
* a match to each refname found in namespace1.
|
||||
* Works by recursively scanning namespace1 for RTEs and join nodes,
|
||||
* and for each one recursively scanning namespace2 for a match.
|
||||
*
|
||||
* Note: we assume that each given argument does not contain conflicts
|
||||
* itself; we just want to know if the two can be merged together.
|
||||
*
|
||||
* Per SQL92, two alias-less plain relation RTEs do not conflict even if
|
||||
* they have the same eref->aliasname (ie, same relation name), if they
|
||||
* are for different relation OIDs (implying they are in different schemas).
|
||||
*/
|
||||
void
|
||||
checkNameSpaceConflicts(ParseState *pstate, Node *namespace1,
|
||||
@ -177,7 +282,12 @@ checkNameSpaceConflicts(ParseState *pstate, Node *namespace1,
|
||||
int varno = ((RangeTblRef *) namespace1)->rtindex;
|
||||
RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable);
|
||||
|
||||
scanNameSpaceForConflict(pstate, namespace2, rte->eref->aliasname);
|
||||
if (rte->rtekind == RTE_RELATION && rte->alias == NULL)
|
||||
scanNameSpaceForConflict(pstate, namespace2,
|
||||
rte, rte->eref->aliasname);
|
||||
else
|
||||
scanNameSpaceForConflict(pstate, namespace2,
|
||||
NULL, rte->eref->aliasname);
|
||||
}
|
||||
else if (IsA(namespace1, JoinExpr))
|
||||
{
|
||||
@ -185,7 +295,8 @@ checkNameSpaceConflicts(ParseState *pstate, Node *namespace1,
|
||||
|
||||
if (j->alias)
|
||||
{
|
||||
scanNameSpaceForConflict(pstate, namespace2, j->alias->aliasname);
|
||||
scanNameSpaceForConflict(pstate, namespace2,
|
||||
NULL, j->alias->aliasname);
|
||||
|
||||
/*
|
||||
* Tables within an aliased join are invisible from outside
|
||||
@ -202,13 +313,70 @@ checkNameSpaceConflicts(ParseState *pstate, Node *namespace1,
|
||||
List *l;
|
||||
|
||||
foreach(l, (List *) namespace1)
|
||||
{
|
||||
checkNameSpaceConflicts(pstate, lfirst(l), namespace2);
|
||||
}
|
||||
}
|
||||
else
|
||||
elog(ERROR, "checkNameSpaceConflicts: unexpected node type %d",
|
||||
nodeTag(namespace1));
|
||||
}
|
||||
|
||||
/*
|
||||
* Subroutine for checkNameSpaceConflicts: scan namespace2
|
||||
*/
|
||||
static void
|
||||
scanNameSpaceForConflict(ParseState *pstate, Node *nsnode,
|
||||
RangeTblEntry *rte1, const char *aliasname1)
|
||||
{
|
||||
if (nsnode == NULL)
|
||||
return;
|
||||
if (IsA(nsnode, RangeTblRef))
|
||||
{
|
||||
int varno = ((RangeTblRef *) nsnode)->rtindex;
|
||||
RangeTblEntry *rte = rt_fetch(varno, pstate->p_rtable);
|
||||
|
||||
if (strcmp(rte->eref->aliasname, aliasname1) != 0)
|
||||
return; /* definitely no conflict */
|
||||
if (rte->rtekind == RTE_RELATION && rte->alias == NULL &&
|
||||
rte1 != NULL && rte->relid != rte1->relid)
|
||||
return; /* no conflict per SQL92 rule */
|
||||
elog(ERROR, "Table name \"%s\" specified more than once",
|
||||
aliasname1);
|
||||
}
|
||||
else if (IsA(nsnode, JoinExpr))
|
||||
{
|
||||
JoinExpr *j = (JoinExpr *) nsnode;
|
||||
|
||||
if (j->alias)
|
||||
{
|
||||
if (strcmp(j->alias->aliasname, aliasname1) == 0)
|
||||
elog(ERROR, "Table name \"%s\" specified more than once",
|
||||
aliasname1);
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
scanNameSpaceForConflict(pstate, j->larg, rte1, aliasname1);
|
||||
scanNameSpaceForConflict(pstate, j->rarg, rte1, aliasname1);
|
||||
}
|
||||
else if (IsA(nsnode, List))
|
||||
{
|
||||
List *l;
|
||||
|
||||
foreach(l, (List *) nsnode)
|
||||
{
|
||||
scanNameSpaceForConflict(pstate, lfirst(l), rte1, aliasname1);
|
||||
}
|
||||
}
|
||||
else
|
||||
elog(ERROR, "scanNameSpaceForConflict: unexpected node type %d",
|
||||
nodeTag(nsnode));
|
||||
}
|
||||
|
||||
/*
|
||||
* given an RTE, return RT index (starting with 1) of the entry,
|
||||
* and optionally get its nesting depth (0 = current). If sublevels_up
|
||||
@ -403,24 +571,29 @@ colnameToVar(ParseState *pstate, char *colname)
|
||||
|
||||
/*
|
||||
* qualifiedNameToVar
|
||||
* Search for a qualified column name (refname + column name).
|
||||
* Search for a qualified column name: either refname.colname or
|
||||
* schemaname.relname.colname.
|
||||
*
|
||||
* If found, return the appropriate Var node.
|
||||
* If not found, return NULL. If the name proves ambiguous, raise error.
|
||||
*/
|
||||
Node *
|
||||
qualifiedNameToVar(ParseState *pstate, char *refname, char *colname,
|
||||
qualifiedNameToVar(ParseState *pstate,
|
||||
char *schemaname,
|
||||
char *refname,
|
||||
char *colname,
|
||||
bool implicitRTEOK)
|
||||
{
|
||||
RangeTblEntry *rte;
|
||||
int sublevels_up;
|
||||
|
||||
rte = refnameRangeTblEntry(pstate, refname, &sublevels_up);
|
||||
rte = refnameRangeTblEntry(pstate, schemaname, refname, &sublevels_up);
|
||||
|
||||
if (rte == NULL)
|
||||
{
|
||||
if (!implicitRTEOK)
|
||||
return NULL;
|
||||
rte = addImplicitRTE(pstate, makeRangeVar(NULL, refname));
|
||||
rte = addImplicitRTE(pstate, makeRangeVar(schemaname, refname));
|
||||
}
|
||||
|
||||
return scanRTEForColumn(pstate, rte, colname);
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.86 2002/08/02 18:15:07 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.87 2002/08/08 01:44:31 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -155,11 +155,11 @@ transformTargetList(ParseState *pstate, List *targetlist)
|
||||
break;
|
||||
}
|
||||
|
||||
/* XXX do something with schema name */
|
||||
rte = refnameRangeTblEntry(pstate, relname,
|
||||
rte = refnameRangeTblEntry(pstate, schemaname, relname,
|
||||
&sublevels_up);
|
||||
if (rte == NULL)
|
||||
rte = addImplicitRTE(pstate, makeRangeVar(NULL, relname));
|
||||
rte = addImplicitRTE(pstate, makeRangeVar(schemaname,
|
||||
relname));
|
||||
|
||||
p_target = nconc(p_target,
|
||||
expandRelAttrs(pstate, rte));
|
||||
|
@ -3,7 +3,7 @@
|
||||
* back to source text
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.112 2002/07/18 23:11:28 petere Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.113 2002/08/08 01:44:31 tgl Exp $
|
||||
*
|
||||
* This software is copyrighted by Jan Wieck - Hamburg.
|
||||
*
|
||||
@ -131,7 +131,9 @@ static Node *get_rule_sortgroupclause(SortClause *srt, List *tlist,
|
||||
bool force_colno,
|
||||
deparse_context *context);
|
||||
static void get_names_for_var(Var *var, deparse_context *context,
|
||||
char **refname, char **attname);
|
||||
char **schemaname, char **refname, char **attname);
|
||||
static RangeTblEntry *find_rte_by_refname(const char *refname,
|
||||
deparse_context *context);
|
||||
static void get_rule_expr(Node *node, deparse_context *context);
|
||||
static void get_oper_expr(Expr *expr, deparse_context *context);
|
||||
static void get_func_expr(Expr *expr, deparse_context *context);
|
||||
@ -1204,10 +1206,11 @@ get_basic_select_query(Query *query, deparse_context *context)
|
||||
else
|
||||
{
|
||||
Var *var = (Var *) (tle->expr);
|
||||
char *schemaname;
|
||||
char *refname;
|
||||
char *attname;
|
||||
|
||||
get_names_for_var(var, context, &refname, &attname);
|
||||
get_names_for_var(var, context, &schemaname, &refname, &attname);
|
||||
tell_as = (attname == NULL ||
|
||||
strcmp(attname, tle->resdom->resname) != 0);
|
||||
}
|
||||
@ -1513,17 +1516,22 @@ get_utility_query_def(Query *query, deparse_context *context)
|
||||
|
||||
|
||||
/*
|
||||
* Get the relation refname and attname for a (possibly nonlocal) Var.
|
||||
* Get the schemaname, refname and attname for a (possibly nonlocal) Var.
|
||||
*
|
||||
* schemaname is usually returned as NULL. It will be non-null only if
|
||||
* use of the unqualified refname would find the wrong RTE.
|
||||
*
|
||||
* refname will be returned as NULL if the Var references an unnamed join.
|
||||
* In this case the Var *must* be displayed without any qualification.
|
||||
*
|
||||
* attname will be returned as NULL if the Var represents a whole tuple
|
||||
* of the relation.
|
||||
* of the relation. (Typically we'd want to display the Var as "foo.*",
|
||||
* but it's convenient to return NULL to make it easier for callers to
|
||||
* distinguish this case.)
|
||||
*/
|
||||
static void
|
||||
get_names_for_var(Var *var, deparse_context *context,
|
||||
char **refname, char **attname)
|
||||
char **schemaname, char **refname, char **attname)
|
||||
{
|
||||
List *nslist = context->namespaces;
|
||||
int sup = var->varlevelsup;
|
||||
@ -1552,10 +1560,29 @@ get_names_for_var(Var *var, deparse_context *context,
|
||||
var->varno);
|
||||
|
||||
/* Emit results */
|
||||
if (rte->rtekind == RTE_JOIN && rte->alias == NULL)
|
||||
*refname = NULL;
|
||||
else
|
||||
*refname = rte->eref->aliasname;
|
||||
*schemaname = NULL; /* default assumptions */
|
||||
*refname = rte->eref->aliasname;
|
||||
|
||||
/* Exceptions occur only if the RTE is alias-less */
|
||||
if (rte->alias == NULL)
|
||||
{
|
||||
if (rte->rtekind == RTE_RELATION)
|
||||
{
|
||||
/*
|
||||
* It's possible that use of the bare refname would find another
|
||||
* more-closely-nested RTE, or be ambiguous, in which case
|
||||
* we need to specify the schemaname to avoid these errors.
|
||||
*/
|
||||
if (find_rte_by_refname(rte->eref->aliasname, context) != rte)
|
||||
*schemaname =
|
||||
get_namespace_name(get_rel_namespace(rte->relid));
|
||||
}
|
||||
else if (rte->rtekind == RTE_JOIN)
|
||||
{
|
||||
/* Unnamed join has neither schemaname nor refname */
|
||||
*refname = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (var->varattno == InvalidAttrNumber)
|
||||
*attname = NULL;
|
||||
@ -1563,6 +1590,61 @@ get_names_for_var(Var *var, deparse_context *context,
|
||||
*attname = get_rte_attribute_name(rte, var->varattno);
|
||||
}
|
||||
|
||||
/*
|
||||
* find_rte_by_refname - look up an RTE by refname in a deparse context
|
||||
*
|
||||
* Returns NULL if there is no matching RTE or the refname is ambiguous.
|
||||
*
|
||||
* NOTE: this code is not really correct since it does not take account of
|
||||
* the fact that not all the RTEs in a rangetable may be visible from the
|
||||
* point where a Var reference appears. For the purposes we need, however,
|
||||
* the only consequence of a false match is that we might stick a schema
|
||||
* qualifier on a Var that doesn't really need it. So it seems close
|
||||
* enough.
|
||||
*/
|
||||
static RangeTblEntry *
|
||||
find_rte_by_refname(const char *refname, deparse_context *context)
|
||||
{
|
||||
RangeTblEntry *result = NULL;
|
||||
List *nslist;
|
||||
|
||||
foreach(nslist, context->namespaces)
|
||||
{
|
||||
deparse_namespace *dpns = (deparse_namespace *) lfirst(nslist);
|
||||
List *rtlist;
|
||||
|
||||
foreach(rtlist, dpns->rtable)
|
||||
{
|
||||
RangeTblEntry *rte = (RangeTblEntry *) lfirst(rtlist);
|
||||
|
||||
if (strcmp(rte->eref->aliasname, refname) == 0)
|
||||
{
|
||||
if (result)
|
||||
return NULL; /* it's ambiguous */
|
||||
result = rte;
|
||||
}
|
||||
}
|
||||
if (dpns->outer_rte &&
|
||||
strcmp(dpns->outer_rte->eref->aliasname, refname) == 0)
|
||||
{
|
||||
if (result)
|
||||
return NULL; /* it's ambiguous */
|
||||
result = dpns->outer_rte;
|
||||
}
|
||||
if (dpns->inner_rte &&
|
||||
strcmp(dpns->inner_rte->eref->aliasname, refname) == 0)
|
||||
{
|
||||
if (result)
|
||||
return NULL; /* it's ambiguous */
|
||||
result = dpns->inner_rte;
|
||||
}
|
||||
if (result)
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/* ----------
|
||||
* get_rule_expr - Parse back an expression
|
||||
* ----------
|
||||
@ -1592,24 +1674,30 @@ get_rule_expr(Node *node, deparse_context *context)
|
||||
case T_Var:
|
||||
{
|
||||
Var *var = (Var *) node;
|
||||
char *schemaname;
|
||||
char *refname;
|
||||
char *attname;
|
||||
|
||||
get_names_for_var(var, context, &refname, &attname);
|
||||
get_names_for_var(var, context,
|
||||
&schemaname, &refname, &attname);
|
||||
if (refname && (context->varprefix || attname == NULL))
|
||||
{
|
||||
if (schemaname)
|
||||
appendStringInfo(buf, "%s.",
|
||||
quote_identifier(schemaname));
|
||||
|
||||
if (strcmp(refname, "*NEW*") == 0)
|
||||
appendStringInfo(buf, "new");
|
||||
appendStringInfo(buf, "new.");
|
||||
else if (strcmp(refname, "*OLD*") == 0)
|
||||
appendStringInfo(buf, "old");
|
||||
appendStringInfo(buf, "old.");
|
||||
else
|
||||
appendStringInfo(buf, "%s",
|
||||
appendStringInfo(buf, "%s.",
|
||||
quote_identifier(refname));
|
||||
if (attname)
|
||||
appendStringInfoChar(buf, '.');
|
||||
}
|
||||
if (attname)
|
||||
appendStringInfo(buf, "%s", quote_identifier(attname));
|
||||
else
|
||||
appendStringInfo(buf, "*");
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: namespace.h,v 1.18 2002/08/06 05:40:45 ishii Exp $
|
||||
* $Id: namespace.h,v 1.19 2002/08/08 01:44:31 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -69,7 +69,7 @@ extern bool OpclassIsVisible(Oid opcid);
|
||||
extern void DeconstructQualifiedName(List *names,
|
||||
char **nspname_p,
|
||||
char **objname_p);
|
||||
extern Oid LookupExplicitNamespace(char *nspname);
|
||||
extern Oid LookupExplicitNamespace(const char *nspname);
|
||||
|
||||
extern Oid QualifiedNameGetCreationNamespace(List *names, char **objname_p);
|
||||
extern RangeVar *makeRangeVarFromNameList(List *names);
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: parse_relation.h,v 1.37 2002/08/05 02:30:50 tgl Exp $
|
||||
* $Id: parse_relation.h,v 1.38 2002/08/08 01:44:31 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -17,7 +17,8 @@
|
||||
#include "parser/parse_node.h"
|
||||
|
||||
extern RangeTblEntry *refnameRangeTblEntry(ParseState *pstate,
|
||||
char *refname,
|
||||
const char *schemaname,
|
||||
const char *refname,
|
||||
int *sublevels_up);
|
||||
extern void checkNameSpaceConflicts(ParseState *pstate, Node *namespace1,
|
||||
Node *namespace2);
|
||||
@ -25,8 +26,11 @@ extern int RTERangeTablePosn(ParseState *pstate,
|
||||
RangeTblEntry *rte,
|
||||
int *sublevels_up);
|
||||
extern Node *colnameToVar(ParseState *pstate, char *colname);
|
||||
extern Node *qualifiedNameToVar(ParseState *pstate, char *refname,
|
||||
char *colname, bool implicitRTEOK);
|
||||
extern Node *qualifiedNameToVar(ParseState *pstate,
|
||||
char *schemaname,
|
||||
char *refname,
|
||||
char *colname,
|
||||
bool implicitRTEOK);
|
||||
extern RangeTblEntry *addRangeTableEntry(ParseState *pstate,
|
||||
RangeVar *relation,
|
||||
Alias *alias,
|
||||
|
Loading…
Reference in New Issue
Block a user