Commit b084cc35 authored by Tom Lane's avatar Tom Lane

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?'
parent e42f8e32
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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, ...@@ -1080,7 +1080,7 @@ DeconstructQualifiedName(List *names,
* Returns the namespace OID. Raises elog if any problem. * Returns the namespace OID. Raises elog if any problem.
*/ */
Oid Oid
LookupExplicitNamespace(char *nspname) LookupExplicitNamespace(const char *nspname)
{ {
Oid namespaceId; Oid namespaceId;
AclResult aclresult; AclResult aclresult;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -709,6 +709,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
/* Try to identify as an unqualified column */ /* Try to identify as an unqualified column */
node = colnameToVar(pstate, name); node = colnameToVar(pstate, name);
if (node == NULL) if (node == NULL)
{ {
/* /*
...@@ -716,11 +717,15 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) ...@@ -716,11 +717,15 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
* try to find the name as a relation ... but not if * try to find the name as a relation ... but not if
* subscripts appear. Note also that only relations * subscripts appear. Note also that only relations
* already entered into the rangetable will be recognized. * 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; int levels_up;
if (cref->indirection == NIL && if (cref->indirection == NIL &&
refnameRangeTblEntry(pstate, name, &levels_up) != NULL) refnameRangeTblEntry(pstate, NULL, name,
&levels_up) != NULL)
{ {
rv = makeNode(RangeVar); rv = makeNode(RangeVar);
rv->relname = name; rv->relname = name;
...@@ -748,7 +753,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) ...@@ -748,7 +753,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
} }
/* Try to identify as a once-qualified column */ /* Try to identify as a once-qualified column */
node = qualifiedNameToVar(pstate, name1, name2, true); node = qualifiedNameToVar(pstate, NULL, name1, name2, true);
if (node == NULL) if (node == NULL)
{ {
/* /*
...@@ -784,8 +789,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) ...@@ -784,8 +789,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
} }
/* Try to identify as a twice-qualified column */ /* Try to identify as a twice-qualified column */
/* XXX do something with schema name here */ node = qualifiedNameToVar(pstate, name1, name2, name3, true);
node = qualifiedNameToVar(pstate, name2, name3, true);
if (node == NULL) if (node == NULL)
{ {
/* Try it as a function call */ /* Try it as a function call */
...@@ -825,8 +829,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) ...@@ -825,8 +829,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
} }
/* Try to identify as a twice-qualified column */ /* Try to identify as a twice-qualified column */
/* XXX do something with schema name here */ node = qualifiedNameToVar(pstate, name2, name3, name4, true);
node = qualifiedNameToVar(pstate, name3, name4, true);
if (node == NULL) if (node == NULL)
{ {
/* Try it as a function call */ /* Try it as a function call */
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -51,6 +51,8 @@ static int match_argtypes(int nargs,
static FieldSelect *setup_field_select(Node *input, char *attname, Oid relid); static FieldSelect *setup_field_select(Node *input, char *attname, Oid relid);
static FuncCandidateList func_select_candidate(int nargs, Oid *input_typeids, static FuncCandidateList func_select_candidate(int nargs, Oid *input_typeids,
FuncCandidateList candidates); 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, ...@@ -80,7 +82,6 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
Oid funcid; Oid funcid;
List *i; List *i;
Node *first_arg = NULL; Node *first_arg = NULL;
char *refname;
int nargs = length(fargs); int nargs = length(fargs);
int argn; int argn;
Oid oid_array[FUNC_MAX_ARGS]; Oid oid_array[FUNC_MAX_ARGS];
...@@ -121,10 +122,11 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, ...@@ -121,10 +122,11 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
if (IsA(first_arg, RangeVar)) if (IsA(first_arg, RangeVar))
{ {
/* First arg is a relation. This could be a projection. */ /* First arg is a relation. This could be a projection. */
refname = ((RangeVar *) first_arg)->relname; retval = qualifiedNameToVar(pstate,
((RangeVar *) first_arg)->schemaname,
/* XXX WRONG: ignores possible qualification of argument */ ((RangeVar *) first_arg)->relname,
retval = qualifiedNameToVar(pstate, refname, cname, true); cname,
true);
if (retval) if (retval)
return retval; return retval;
} }
...@@ -156,16 +158,19 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, ...@@ -156,16 +158,19 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
if (IsA(arg, RangeVar)) if (IsA(arg, RangeVar))
{ {
char *schemaname;
char *relname;
RangeTblEntry *rte; RangeTblEntry *rte;
int vnum; int vnum;
int sublevels_up; 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); &sublevels_up);
if (rte == NULL) if (rte == NULL)
...@@ -199,11 +204,11 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, ...@@ -199,11 +204,11 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
* named tuple type * named tuple type
*/ */
if (is_column) if (is_column)
elog(ERROR, "No such attribute %s.%s", unknown_attribute(schemaname, relname,
refname, strVal(lfirst(funcname))); strVal(lfirst(funcname)));
else else
elog(ERROR, "Cannot pass result of sub-select or join %s to a function", elog(ERROR, "Cannot pass result of sub-select or join %s to a function",
refname); relname);
toid = InvalidOid; /* keep compiler quiet */ toid = InvalidOid; /* keep compiler quiet */
break; break;
} }
...@@ -268,8 +273,9 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, ...@@ -268,8 +273,9 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
Assert(nargs == 1); Assert(nargs == 1);
if (IsA(first_arg, RangeVar)) if (IsA(first_arg, RangeVar))
elog(ERROR, "No such attribute %s.%s", unknown_attribute(((RangeVar *) first_arg)->schemaname,
((RangeVar *) first_arg)->relname, colname); ((RangeVar *) first_arg)->relname,
colname);
relTypeId = exprType(first_arg); relTypeId = exprType(first_arg);
if (!ISCOMPLEX(relTypeId)) if (!ISCOMPLEX(relTypeId))
elog(ERROR, "Attribute notation .%s applied to type %s, which is not a complex type", elog(ERROR, "Attribute notation .%s applied to type %s, which is not a complex type",
...@@ -1225,6 +1231,21 @@ ParseComplexProjection(ParseState *pstate, ...@@ -1225,6 +1231,21 @@ ParseComplexProjection(ParseState *pstate,
return (Node *) fselect; 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 * Error message when function lookup fails that gives details of the
* argument types * argument types
......
This diff is collapsed.
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -155,11 +155,11 @@ transformTargetList(ParseState *pstate, List *targetlist)
break; break;
} }
/* XXX do something with schema name */ rte = refnameRangeTblEntry(pstate, schemaname, relname,
rte = refnameRangeTblEntry(pstate, relname,
&sublevels_up); &sublevels_up);
if (rte == NULL) if (rte == NULL)
rte = addImplicitRTE(pstate, makeRangeVar(NULL, relname)); rte = addImplicitRTE(pstate, makeRangeVar(schemaname,
relname));
p_target = nconc(p_target, p_target = nconc(p_target,
expandRelAttrs(pstate, rte)); expandRelAttrs(pstate, rte));
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* back to source text * back to source text
* *
* IDENTIFICATION * 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. * This software is copyrighted by Jan Wieck - Hamburg.
* *
...@@ -131,7 +131,9 @@ static Node *get_rule_sortgroupclause(SortClause *srt, List *tlist, ...@@ -131,7 +131,9 @@ static Node *get_rule_sortgroupclause(SortClause *srt, List *tlist,
bool force_colno, bool force_colno,
deparse_context *context); deparse_context *context);
static void get_names_for_var(Var *var, 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_rule_expr(Node *node, deparse_context *context);
static void get_oper_expr(Expr *expr, deparse_context *context); static void get_oper_expr(Expr *expr, deparse_context *context);
static void get_func_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) ...@@ -1204,10 +1206,11 @@ get_basic_select_query(Query *query, deparse_context *context)
else else
{ {
Var *var = (Var *) (tle->expr); Var *var = (Var *) (tle->expr);
char *schemaname;
char *refname; char *refname;
char *attname; char *attname;
get_names_for_var(var, context, &refname, &attname); get_names_for_var(var, context, &schemaname, &refname, &attname);
tell_as = (attname == NULL || tell_as = (attname == NULL ||
strcmp(attname, tle->resdom->resname) != 0); strcmp(attname, tle->resdom->resname) != 0);
} }
...@@ -1513,17 +1516,22 @@ get_utility_query_def(Query *query, deparse_context *context) ...@@ -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. * refname will be returned as NULL if the Var references an unnamed join.
* In this case the Var *must* be displayed without any qualification. * In this case the Var *must* be displayed without any qualification.
* *
* attname will be returned as NULL if the Var represents a whole tuple * 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 static void
get_names_for_var(Var *var, deparse_context *context, get_names_for_var(Var *var, deparse_context *context,
char **refname, char **attname) char **schemaname, char **refname, char **attname)
{ {
List *nslist = context->namespaces; List *nslist = context->namespaces;
int sup = var->varlevelsup; int sup = var->varlevelsup;
...@@ -1552,10 +1560,29 @@ get_names_for_var(Var *var, deparse_context *context, ...@@ -1552,10 +1560,29 @@ get_names_for_var(Var *var, deparse_context *context,
var->varno); var->varno);
/* Emit results */ /* Emit results */
if (rte->rtekind == RTE_JOIN && rte->alias == NULL) *schemaname = NULL; /* default assumptions */
*refname = NULL; *refname = rte->eref->aliasname;
else
*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) if (var->varattno == InvalidAttrNumber)
*attname = NULL; *attname = NULL;
...@@ -1563,6 +1590,61 @@ get_names_for_var(Var *var, deparse_context *context, ...@@ -1563,6 +1590,61 @@ get_names_for_var(Var *var, deparse_context *context,
*attname = get_rte_attribute_name(rte, var->varattno); *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 * get_rule_expr - Parse back an expression
* ---------- * ----------
...@@ -1592,24 +1674,30 @@ get_rule_expr(Node *node, deparse_context *context) ...@@ -1592,24 +1674,30 @@ get_rule_expr(Node *node, deparse_context *context)
case T_Var: case T_Var:
{ {
Var *var = (Var *) node; Var *var = (Var *) node;
char *schemaname;
char *refname; char *refname;
char *attname; 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 (refname && (context->varprefix || attname == NULL))
{ {
if (schemaname)
appendStringInfo(buf, "%s.",
quote_identifier(schemaname));
if (strcmp(refname, "*NEW*") == 0) if (strcmp(refname, "*NEW*") == 0)
appendStringInfo(buf, "new"); appendStringInfo(buf, "new.");
else if (strcmp(refname, "*OLD*") == 0) else if (strcmp(refname, "*OLD*") == 0)
appendStringInfo(buf, "old"); appendStringInfo(buf, "old.");
else else
appendStringInfo(buf, "%s", appendStringInfo(buf, "%s.",
quote_identifier(refname)); quote_identifier(refname));
if (attname)
appendStringInfoChar(buf, '.');
} }
if (attname) if (attname)
appendStringInfo(buf, "%s", quote_identifier(attname)); appendStringInfo(buf, "%s", quote_identifier(attname));
else
appendStringInfo(buf, "*");
} }
break; break;
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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); ...@@ -69,7 +69,7 @@ extern bool OpclassIsVisible(Oid opcid);
extern void DeconstructQualifiedName(List *names, extern void DeconstructQualifiedName(List *names,
char **nspname_p, char **nspname_p,
char **objname_p); char **objname_p);
extern Oid LookupExplicitNamespace(char *nspname); extern Oid LookupExplicitNamespace(const char *nspname);
extern Oid QualifiedNameGetCreationNamespace(List *names, char **objname_p); extern Oid QualifiedNameGetCreationNamespace(List *names, char **objname_p);
extern RangeVar *makeRangeVarFromNameList(List *names); extern RangeVar *makeRangeVarFromNameList(List *names);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 @@ ...@@ -17,7 +17,8 @@
#include "parser/parse_node.h" #include "parser/parse_node.h"
extern RangeTblEntry *refnameRangeTblEntry(ParseState *pstate, extern RangeTblEntry *refnameRangeTblEntry(ParseState *pstate,
char *refname, const char *schemaname,
const char *refname,
int *sublevels_up); int *sublevels_up);
extern void checkNameSpaceConflicts(ParseState *pstate, Node *namespace1, extern void checkNameSpaceConflicts(ParseState *pstate, Node *namespace1,
Node *namespace2); Node *namespace2);
...@@ -25,8 +26,11 @@ extern int RTERangeTablePosn(ParseState *pstate, ...@@ -25,8 +26,11 @@ extern int RTERangeTablePosn(ParseState *pstate,
RangeTblEntry *rte, RangeTblEntry *rte,
int *sublevels_up); int *sublevels_up);
extern Node *colnameToVar(ParseState *pstate, char *colname); extern Node *colnameToVar(ParseState *pstate, char *colname);
extern Node *qualifiedNameToVar(ParseState *pstate, char *refname, extern Node *qualifiedNameToVar(ParseState *pstate,
char *colname, bool implicitRTEOK); char *schemaname,
char *refname,
char *colname,
bool implicitRTEOK);
extern RangeTblEntry *addRangeTableEntry(ParseState *pstate, extern RangeTblEntry *addRangeTableEntry(ParseState *pstate,
RangeVar *relation, RangeVar *relation,
Alias *alias, Alias *alias,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment