diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c index bc2098034f..4999ea4b64 100644 --- a/src/backend/catalog/namespace.c +++ b/src/backend/catalog/namespace.c @@ -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; diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 3cd09a9b43..85cdc431a0 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -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 */ diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index 677acf9d1a..edd0e81095 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -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 diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 8a15c0f7cb..229eab829a 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.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); diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index 1e51f23d70..6f5b5bab90 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -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)); diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 5999ad9628..98bbba534a 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -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; diff --git a/src/include/catalog/namespace.h b/src/include/catalog/namespace.h index f7e4ec32fd..dd6c786ef2 100644 --- a/src/include/catalog/namespace.h +++ b/src/include/catalog/namespace.h @@ -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); diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h index 38729a81a6..5d40590617 100644 --- a/src/include/parser/parse_relation.h +++ b/src/include/parser/parse_relation.h @@ -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,