Get rid of crocky use of RangeVar nodes in parser to represent partially
transformed whole-row variables. Cleaner to use regular whole-row Vars.
This commit is contained in:
parent
94d8da8fec
commit
27a4f06ade
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.167 2004/03/24 22:40:28 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.168 2004/04/02 19:06:57 tgl Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@ -2550,16 +2550,6 @@ expression_tree_walker(Node *node,
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case T_RangeVar:
|
||||
/*
|
||||
* Give a useful complaint if someone uses a bare relation name
|
||||
* in an expression (see comments in transformColumnRef()).
|
||||
*/
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("relation reference \"%s\" cannot be used in an expression",
|
||||
((RangeVar *) node)->relname)));
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "unrecognized node type: %d",
|
||||
(int) nodeTag(node));
|
||||
@ -3031,16 +3021,6 @@ expression_tree_mutator(Node *node,
|
||||
return (Node *) newnode;
|
||||
}
|
||||
break;
|
||||
case T_RangeVar:
|
||||
/*
|
||||
* Give a useful complaint if someone uses a bare relation name
|
||||
* in an expression (see comments in transformColumnRef()).
|
||||
*/
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("relation reference \"%s\" cannot be used in an expression",
|
||||
((RangeVar *) node)->relname)));
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "unrecognized node type: %d",
|
||||
(int) nodeTag(node));
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.167 2004/03/24 22:40:29 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.168 2004/04/02 19:06:58 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -40,6 +40,8 @@ bool Transform_null_equals = false;
|
||||
static Node *typecast_expression(ParseState *pstate, Node *expr,
|
||||
TypeName *typename);
|
||||
static Node *transformColumnRef(ParseState *pstate, ColumnRef *cref);
|
||||
static Node *transformWholeRowRef(ParseState *pstate, char *schemaname,
|
||||
char *relname);
|
||||
static Node *transformIndirection(ParseState *pstate, Node *basenode,
|
||||
List *indirection);
|
||||
|
||||
@ -932,34 +934,29 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
|
||||
{
|
||||
int numnames = length(cref->fields);
|
||||
Node *node;
|
||||
RangeVar *rv;
|
||||
int levels_up;
|
||||
|
||||
/*----------
|
||||
* The allowed syntaxes are:
|
||||
*
|
||||
* A First try to resolve as unqualified column name;
|
||||
* if no luck, try to resolve as unqual. table name (A.*).
|
||||
* A.B A is an unqual. table name; B is either a
|
||||
* if no luck, try to resolve as unqualified table name (A.*).
|
||||
* A.B A is an unqualified table name; B is either a
|
||||
* column or function name (trying column name first).
|
||||
* A.B.C schema A, table B, col or func name C.
|
||||
* A.B.C.D catalog A, schema B, table C, col or func D.
|
||||
* A.* A is an unqual. table name; means whole-row value.
|
||||
* A.* A is an unqualified table name; means whole-row value.
|
||||
* A.B.* whole-row value of table B in schema A.
|
||||
* A.B.C.* whole-row value of table C in schema B in catalog A.
|
||||
*
|
||||
* We do not need to cope with bare "*"; that will only be accepted by
|
||||
* the grammar at the top level of a SELECT list, and transformTargetList
|
||||
* will take care of it before it ever gets here.
|
||||
* will take care of it before it ever gets here. Also, "A.*" etc will
|
||||
* be expanded by transformTargetList if they appear at SELECT top level,
|
||||
* so here we are only going to see them as function or operator inputs.
|
||||
*
|
||||
* Currently, if a catalog name is given then it must equal the current
|
||||
* database name; we check it here and then discard it.
|
||||
*
|
||||
* For whole-row references, the result is an untransformed RangeVar,
|
||||
* which will work as the argument to a function call, but not in any
|
||||
* other context at present. (We could instead coerce to a whole-row Var,
|
||||
* but that will fail for subselect and join RTEs, because there is no
|
||||
* pg_type entry for their rowtypes.)
|
||||
*----------
|
||||
*/
|
||||
switch (numnames)
|
||||
@ -1001,16 +998,12 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
|
||||
if (cref->indirection == NIL &&
|
||||
refnameRangeTblEntry(pstate, NULL, name,
|
||||
&levels_up) != NULL)
|
||||
{
|
||||
rv = makeNode(RangeVar);
|
||||
rv->relname = name;
|
||||
rv->inhOpt = INH_DEFAULT;
|
||||
node = (Node *) rv;
|
||||
}
|
||||
node = transformWholeRowRef(pstate, NULL, name);
|
||||
else
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_COLUMN),
|
||||
errmsg("column \"%s\" does not exist", name)));
|
||||
errmsg("column \"%s\" does not exist",
|
||||
name)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -1022,10 +1015,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
|
||||
/* Whole-row reference? */
|
||||
if (strcmp(name2, "*") == 0)
|
||||
{
|
||||
rv = makeNode(RangeVar);
|
||||
rv->relname = name1;
|
||||
rv->inhOpt = INH_DEFAULT;
|
||||
node = (Node *) rv;
|
||||
node = transformWholeRowRef(pstate, NULL, name1);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1038,12 +1028,10 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
|
||||
* try it as a function call. Here, we will create an
|
||||
* implicit RTE for tables not already entered.
|
||||
*/
|
||||
rv = makeNode(RangeVar);
|
||||
rv->relname = name1;
|
||||
rv->inhOpt = INH_DEFAULT;
|
||||
node = transformWholeRowRef(pstate, NULL, name1);
|
||||
node = ParseFuncOrColumn(pstate,
|
||||
makeList1(makeString(name2)),
|
||||
makeList1(rv),
|
||||
makeList1(node),
|
||||
false, false, true);
|
||||
}
|
||||
break;
|
||||
@ -1057,11 +1045,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
|
||||
/* Whole-row reference? */
|
||||
if (strcmp(name3, "*") == 0)
|
||||
{
|
||||
rv = makeNode(RangeVar);
|
||||
rv->schemaname = name1;
|
||||
rv->relname = name2;
|
||||
rv->inhOpt = INH_DEFAULT;
|
||||
node = (Node *) rv;
|
||||
node = transformWholeRowRef(pstate, name1, name2);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1070,13 +1054,10 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
|
||||
if (node == NULL)
|
||||
{
|
||||
/* Try it as a function call */
|
||||
rv = makeNode(RangeVar);
|
||||
rv->schemaname = name1;
|
||||
rv->relname = name2;
|
||||
rv->inhOpt = INH_DEFAULT;
|
||||
node = transformWholeRowRef(pstate, name1, name2);
|
||||
node = ParseFuncOrColumn(pstate,
|
||||
makeList1(makeString(name3)),
|
||||
makeList1(rv),
|
||||
makeList1(node),
|
||||
false, false, true);
|
||||
}
|
||||
break;
|
||||
@ -1100,11 +1081,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
|
||||
/* Whole-row reference? */
|
||||
if (strcmp(name4, "*") == 0)
|
||||
{
|
||||
rv = makeNode(RangeVar);
|
||||
rv->schemaname = name2;
|
||||
rv->relname = name3;
|
||||
rv->inhOpt = INH_DEFAULT;
|
||||
node = (Node *) rv;
|
||||
node = transformWholeRowRef(pstate, name2, name3);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1113,13 +1090,10 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
|
||||
if (node == NULL)
|
||||
{
|
||||
/* Try it as a function call */
|
||||
rv = makeNode(RangeVar);
|
||||
rv->schemaname = name2;
|
||||
rv->relname = name3;
|
||||
rv->inhOpt = INH_DEFAULT;
|
||||
node = transformWholeRowRef(pstate, name2, name3);
|
||||
node = ParseFuncOrColumn(pstate,
|
||||
makeList1(makeString(name4)),
|
||||
makeList1(rv),
|
||||
makeList1(node),
|
||||
false, false, true);
|
||||
}
|
||||
break;
|
||||
@ -1136,6 +1110,99 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
|
||||
return transformIndirection(pstate, node, cref->indirection);
|
||||
}
|
||||
|
||||
/*
|
||||
* Construct a whole-row reference to represent the notation "relation.*".
|
||||
*
|
||||
* In simple cases, this will be a Var with varno set to the correct range
|
||||
* table entry, and varattno == 0 to signal that it references the whole
|
||||
* tuple. (Use of zero here is unclean, since it could easily be confused
|
||||
* with error cases, but it's not worth changing now.) The vartype indicates
|
||||
* a rowtype; either a named composite type, or RECORD.
|
||||
*
|
||||
* We also need the ability to build a row-constructor expression, but the
|
||||
* infrastructure for that doesn't exist just yet.
|
||||
*/
|
||||
static Node *
|
||||
transformWholeRowRef(ParseState *pstate, char *schemaname, char *relname)
|
||||
{
|
||||
Node *result;
|
||||
RangeTblEntry *rte;
|
||||
int vnum;
|
||||
int sublevels_up;
|
||||
Oid toid;
|
||||
|
||||
/* Look up the referenced RTE, creating it if needed */
|
||||
|
||||
rte = refnameRangeTblEntry(pstate, schemaname, relname,
|
||||
&sublevels_up);
|
||||
|
||||
if (rte == NULL)
|
||||
rte = addImplicitRTE(pstate, makeRangeVar(schemaname, relname));
|
||||
|
||||
vnum = RTERangeTablePosn(pstate, rte, &sublevels_up);
|
||||
|
||||
/* Build the appropriate referencing node */
|
||||
|
||||
switch (rte->rtekind)
|
||||
{
|
||||
case RTE_RELATION:
|
||||
/* relation: the rowtype is a named composite type */
|
||||
toid = get_rel_type_id(rte->relid);
|
||||
if (!OidIsValid(toid))
|
||||
elog(ERROR, "could not find type OID for relation %u",
|
||||
rte->relid);
|
||||
result = (Node *) makeVar(vnum,
|
||||
InvalidAttrNumber,
|
||||
toid,
|
||||
-1,
|
||||
sublevels_up);
|
||||
break;
|
||||
case RTE_FUNCTION:
|
||||
toid = exprType(rte->funcexpr);
|
||||
if (toid == RECORDOID || get_typtype(toid) == 'c')
|
||||
{
|
||||
/* func returns composite; same as relation case */
|
||||
result = (Node *) makeVar(vnum,
|
||||
InvalidAttrNumber,
|
||||
toid,
|
||||
-1,
|
||||
sublevels_up);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* func returns scalar; instead of making a whole-row Var,
|
||||
* just reference the function's scalar output. (XXX this
|
||||
* seems a tad inconsistent, especially if "f.*" was
|
||||
* explicitly written ...)
|
||||
*/
|
||||
result = (Node *) makeVar(vnum,
|
||||
1,
|
||||
toid,
|
||||
-1,
|
||||
sublevels_up);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
* RTE is a join or subselect. For the moment we represent this
|
||||
* as a whole-row Var of RECORD type, but this will not actually
|
||||
* work; need a row-constructor expression instead.
|
||||
*
|
||||
* XXX after fixing, be sure that unknown_attribute still
|
||||
* does the right thing.
|
||||
*/
|
||||
result = (Node *) makeVar(vnum,
|
||||
InvalidAttrNumber,
|
||||
RECORDOID,
|
||||
-1,
|
||||
sublevels_up);
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* exprType -
|
||||
* returns the Oid of the type of the expression. (Used for typechecking.)
|
||||
@ -1294,19 +1361,6 @@ exprType(Node *expr)
|
||||
case T_SetToDefault:
|
||||
type = ((SetToDefault *) expr)->typeId;
|
||||
break;
|
||||
case T_RangeVar:
|
||||
|
||||
/*
|
||||
* If someone uses a bare relation name in an expression, we
|
||||
* will likely first notice a problem here (see comments in
|
||||
* transformColumnRef()). Issue an appropriate error message.
|
||||
*/
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("relation reference \"%s\" cannot be used in an expression",
|
||||
((RangeVar *) expr)->relname)));
|
||||
type = InvalidOid; /* keep compiler quiet */
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
|
||||
type = InvalidOid; /* keep compiler quiet */
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.166 2004/04/01 21:28:44 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.167 2004/04/02 19:06:58 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -32,14 +32,14 @@
|
||||
#include "utils/syscache.h"
|
||||
|
||||
|
||||
static Node *ParseComplexProjection(char *funcname, Node *first_arg);
|
||||
static Node *ParseComplexProjection(ParseState *pstate, char *funcname,
|
||||
Node *first_arg);
|
||||
static Oid **argtype_inherit(int nargs, Oid *argtypes);
|
||||
|
||||
static int find_inheritors(Oid relid, Oid **supervec);
|
||||
static Oid **gen_cross_product(InhPaths *arginh, int nargs);
|
||||
static FieldSelect *setup_field_select(Node *input, char *attname, Oid relid);
|
||||
static void unknown_attribute(const char *schemaname, const char *relname,
|
||||
const char *attname);
|
||||
static void unknown_attribute(ParseState *pstate, Node *relref, char *attname);
|
||||
|
||||
|
||||
/*
|
||||
@ -48,7 +48,9 @@ static void unknown_attribute(const char *schemaname, const char *relname,
|
||||
* For historical reasons, Postgres tries to treat the notations tab.col
|
||||
* and col(tab) as equivalent: if a single-argument function call has an
|
||||
* argument of complex type and the (unqualified) function name matches
|
||||
* any attribute of the type, we take it as a column projection.
|
||||
* any attribute of the type, we take it as a column projection. Conversely
|
||||
* a function of a single complex-type argument can be written like a
|
||||
* column reference, allowing functions to act like computed columns.
|
||||
*
|
||||
* Hence, both cases come through here. The is_column parameter tells us
|
||||
* which syntactic construct is actually being dealt with, but this is
|
||||
@ -57,9 +59,7 @@ static void unknown_attribute(const char *schemaname, const char *relname,
|
||||
* a single argument (the putative table), unqualified function name
|
||||
* equal to the column name, and no aggregate decoration.
|
||||
*
|
||||
* In the function-call case, the argument expressions have been transformed
|
||||
* already. In the column case, we may get either a transformed expression
|
||||
* or a RangeVar node as argument.
|
||||
* The argument expressions (in fargs) must have been transformed already.
|
||||
*/
|
||||
Node *
|
||||
ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
|
||||
@ -96,44 +96,32 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
|
||||
}
|
||||
|
||||
/*
|
||||
* check for column projection: if function has one argument, and that
|
||||
* Check for column projection: if function has one argument, and that
|
||||
* argument is of complex type, and function name is not qualified,
|
||||
* then the "function call" could be a projection. We also check that
|
||||
* there wasn't any aggregate decoration.
|
||||
*/
|
||||
if (nargs == 1 && !agg_star && !agg_distinct && length(funcname) == 1)
|
||||
{
|
||||
char *cname = strVal(lfirst(funcname));
|
||||
Oid argtype = exprType(first_arg);
|
||||
|
||||
/* Is it a not-yet-transformed RangeVar node? */
|
||||
if (IsA(first_arg, RangeVar))
|
||||
if (argtype == RECORDOID || ISCOMPLEX(argtype))
|
||||
{
|
||||
/* First arg is a relation. This could be a projection. */
|
||||
retval = qualifiedNameToVar(pstate,
|
||||
((RangeVar *) first_arg)->schemaname,
|
||||
((RangeVar *) first_arg)->relname,
|
||||
cname,
|
||||
true);
|
||||
retval = ParseComplexProjection(pstate,
|
||||
strVal(lfirst(funcname)),
|
||||
first_arg);
|
||||
if (retval)
|
||||
return retval;
|
||||
}
|
||||
else if (ISCOMPLEX(exprType(first_arg)))
|
||||
{
|
||||
/*
|
||||
* Attempt to handle projection of a complex argument. If
|
||||
* ParseComplexProjection can't handle the projection, we have
|
||||
* to keep going.
|
||||
* If ParseComplexProjection doesn't recognize it as a projection,
|
||||
* just press on.
|
||||
*/
|
||||
retval = ParseComplexProjection(cname, first_arg);
|
||||
if (retval)
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Okay, it's not a column projection, so it must really be a
|
||||
* function. Extract arg type info and transform RangeVar arguments
|
||||
* into varnodes of the appropriate form.
|
||||
* function. Extract arg type info in preparation for function lookup.
|
||||
*/
|
||||
MemSet(actual_arg_types, 0, FUNC_MAX_ARGS * sizeof(Oid));
|
||||
|
||||
@ -141,96 +129,8 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
|
||||
foreach(i, fargs)
|
||||
{
|
||||
Node *arg = lfirst(i);
|
||||
Oid toid;
|
||||
|
||||
if (IsA(arg, RangeVar))
|
||||
{
|
||||
char *schemaname;
|
||||
char *relname;
|
||||
RangeTblEntry *rte;
|
||||
int vnum;
|
||||
int sublevels_up;
|
||||
|
||||
/*
|
||||
* a relation: look it up in the range table, or add if needed
|
||||
*/
|
||||
schemaname = ((RangeVar *) arg)->schemaname;
|
||||
relname = ((RangeVar *) arg)->relname;
|
||||
|
||||
rte = refnameRangeTblEntry(pstate, schemaname, relname,
|
||||
&sublevels_up);
|
||||
|
||||
if (rte == NULL)
|
||||
rte = addImplicitRTE(pstate, (RangeVar *) arg);
|
||||
|
||||
vnum = RTERangeTablePosn(pstate, rte, &sublevels_up);
|
||||
|
||||
/*
|
||||
* The parameter to be passed to the function is the whole
|
||||
* tuple from the relation. We build a special VarNode to
|
||||
* reflect this -- it has varno set to the correct range table
|
||||
* entry, but has varattno == 0 to signal that the whole tuple
|
||||
* is the argument.
|
||||
*/
|
||||
switch (rte->rtekind)
|
||||
{
|
||||
case RTE_RELATION:
|
||||
toid = get_rel_type_id(rte->relid);
|
||||
if (!OidIsValid(toid))
|
||||
elog(ERROR, "could not find type OID for relation %u",
|
||||
rte->relid);
|
||||
/* replace RangeVar in the arg list */
|
||||
lfirst(i) = makeVar(vnum,
|
||||
InvalidAttrNumber,
|
||||
toid,
|
||||
-1,
|
||||
sublevels_up);
|
||||
break;
|
||||
case RTE_FUNCTION:
|
||||
toid = exprType(rte->funcexpr);
|
||||
if (get_typtype(toid) == 'c')
|
||||
{
|
||||
/* func returns composite; same as relation case */
|
||||
lfirst(i) = makeVar(vnum,
|
||||
InvalidAttrNumber,
|
||||
toid,
|
||||
-1,
|
||||
sublevels_up);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* func returns scalar; use attno 1 instead */
|
||||
lfirst(i) = makeVar(vnum,
|
||||
1,
|
||||
toid,
|
||||
-1,
|
||||
sublevels_up);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
||||
/*
|
||||
* RTE is a join or subselect; must fail for lack of a
|
||||
* named tuple type
|
||||
*
|
||||
* XXX FIXME
|
||||
*/
|
||||
if (is_column)
|
||||
unknown_attribute(schemaname, relname,
|
||||
strVal(lfirst(funcname)));
|
||||
else
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("cannot pass result of subquery or join \"%s\" to a function",
|
||||
relname)));
|
||||
toid = InvalidOid; /* keep compiler quiet */
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
toid = exprType(arg);
|
||||
|
||||
actual_arg_types[argn++] = toid;
|
||||
actual_arg_types[argn++] = exprType(arg);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -281,25 +181,9 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
|
||||
*/
|
||||
if (is_column)
|
||||
{
|
||||
char *colname = strVal(lfirst(funcname));
|
||||
Oid relTypeId;
|
||||
|
||||
Assert(nargs == 1);
|
||||
if (IsA(first_arg, RangeVar))
|
||||
unknown_attribute(((RangeVar *) first_arg)->schemaname,
|
||||
((RangeVar *) first_arg)->relname,
|
||||
colname);
|
||||
relTypeId = exprType(first_arg);
|
||||
if (!ISCOMPLEX(relTypeId))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
||||
errmsg("attribute notation .%s applied to type %s, which is not a complex type",
|
||||
colname, format_type_be(relTypeId))));
|
||||
else
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_COLUMN),
|
||||
errmsg("attribute \"%s\" not found in data type %s",
|
||||
colname, format_type_be(relTypeId))));
|
||||
Assert(length(funcname) == 1);
|
||||
unknown_attribute(pstate, first_arg, strVal(lfirst(funcname)));
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1284,80 +1168,92 @@ setup_field_select(Node *input, char *attname, Oid relid)
|
||||
* handles function calls with a single argument that is of complex type.
|
||||
* If the function call is actually a column projection, return a suitably
|
||||
* transformed expression tree. If not, return NULL.
|
||||
*
|
||||
* NB: argument is expected to be transformed already, ie, not a RangeVar.
|
||||
*/
|
||||
static Node *
|
||||
ParseComplexProjection(char *funcname, Node *first_arg)
|
||||
ParseComplexProjection(ParseState *pstate, char *funcname, Node *first_arg)
|
||||
{
|
||||
Oid argtype = exprType(first_arg);
|
||||
Oid argtype;
|
||||
Oid argrelid;
|
||||
AttrNumber attnum;
|
||||
FieldSelect *fselect;
|
||||
|
||||
/*
|
||||
* Special case for whole-row Vars so that we can resolve (foo.*).bar
|
||||
* even when foo is a reference to a subselect, join, or RECORD function.
|
||||
* A bonus is that we avoid generating an unnecessary FieldSelect; our
|
||||
* result can omit the whole-row Var and just be a Var for the selected
|
||||
* field.
|
||||
*/
|
||||
if (IsA(first_arg, Var) &&
|
||||
((Var *) first_arg)->varattno == InvalidAttrNumber)
|
||||
{
|
||||
RangeTblEntry *rte;
|
||||
|
||||
rte = GetRTEByRangeTablePosn(pstate,
|
||||
((Var *) first_arg)->varno,
|
||||
((Var *) first_arg)->varlevelsup);
|
||||
/* Return a Var if funcname matches a column, else NULL */
|
||||
return scanRTEForColumn(pstate, rte, funcname);
|
||||
}
|
||||
|
||||
/*
|
||||
* Else do it the hard way. Note that if the arg is of RECORD type,
|
||||
* we will never recognize a column name, and always assume the item
|
||||
* must be a function.
|
||||
*/
|
||||
argtype = exprType(first_arg);
|
||||
argrelid = typeidTypeRelid(argtype);
|
||||
if (!argrelid)
|
||||
return NULL; /* probably should not happen */
|
||||
return NULL; /* can only happen if RECORD */
|
||||
|
||||
attnum = get_attnum(argrelid, funcname);
|
||||
if (attnum == InvalidAttrNumber)
|
||||
return NULL; /* funcname does not match any column */
|
||||
|
||||
/*
|
||||
* Check for special cases where we don't want to return a
|
||||
* FieldSelect.
|
||||
*/
|
||||
switch (nodeTag(first_arg))
|
||||
{
|
||||
case T_Var:
|
||||
{
|
||||
Var *var = (Var *) first_arg;
|
||||
|
||||
/*
|
||||
* If the Var is a whole-row tuple, we can just replace it
|
||||
* with a simple Var reference.
|
||||
*/
|
||||
if (var->varattno == InvalidAttrNumber)
|
||||
{
|
||||
Oid vartype;
|
||||
int32 vartypmod;
|
||||
|
||||
get_atttypetypmod(argrelid, attnum,
|
||||
&vartype, &vartypmod);
|
||||
|
||||
return (Node *) makeVar(var->varno,
|
||||
attnum,
|
||||
vartype,
|
||||
vartypmod,
|
||||
var->varlevelsup);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Else generate a FieldSelect expression */
|
||||
fselect = setup_field_select(first_arg, funcname, argrelid);
|
||||
return (Node *) fselect;
|
||||
/* Success, so generate a FieldSelect expression */
|
||||
return (Node *) setup_field_select(first_arg, funcname, argrelid);
|
||||
}
|
||||
|
||||
/*
|
||||
* Simple helper routine for delivering "column does not exist" error message
|
||||
* helper routine for delivering "column does not exist" error message
|
||||
*/
|
||||
static void
|
||||
unknown_attribute(const char *schemaname, const char *relname,
|
||||
const char *attname)
|
||||
unknown_attribute(ParseState *pstate, Node *relref, char *attname)
|
||||
{
|
||||
if (schemaname)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_COLUMN),
|
||||
errmsg("column %s.%s.%s does not exist",
|
||||
schemaname, relname, attname)));
|
||||
else
|
||||
RangeTblEntry *rte;
|
||||
|
||||
if (IsA(relref, Var))
|
||||
{
|
||||
/* Reference the RTE by alias not by actual table name */
|
||||
rte = GetRTEByRangeTablePosn(pstate,
|
||||
((Var *) relref)->varno,
|
||||
((Var *) relref)->varlevelsup);
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_COLUMN),
|
||||
errmsg("column %s.%s does not exist",
|
||||
relname, attname)));
|
||||
rte->eref->aliasname, attname)));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Have to do it by reference to the type of the expression */
|
||||
Oid relTypeId = exprType(relref);
|
||||
|
||||
if (ISCOMPLEX(relTypeId))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_COLUMN),
|
||||
errmsg("column \"%s\" not found in data type %s",
|
||||
attname, format_type_be(relTypeId))));
|
||||
else if (relTypeId == RECORDOID)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_COLUMN),
|
||||
errmsg("could not identify column \"%s\" in record data type",
|
||||
attname)));
|
||||
else
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
||||
errmsg("column notation .%s applied to type %s, "
|
||||
"which is not a composite type",
|
||||
attname, format_type_be(relTypeId))));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.92 2004/01/14 23:01:55 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.93 2004/04/02 19:06:58 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -41,8 +41,6 @@ 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);
|
||||
static bool get_rte_attribute_is_dropped(RangeTblEntry *rte,
|
||||
AttrNumber attnum);
|
||||
@ -424,6 +422,24 @@ RTERangeTablePosn(ParseState *pstate, RangeTblEntry *rte, int *sublevels_up)
|
||||
return 0; /* keep compiler quiet */
|
||||
}
|
||||
|
||||
/*
|
||||
* Given an RT index and nesting depth, find the corresponding RTE.
|
||||
* This is the inverse of RTERangeTablePosn.
|
||||
*/
|
||||
RangeTblEntry *
|
||||
GetRTEByRangeTablePosn(ParseState *pstate,
|
||||
int varno,
|
||||
int sublevels_up)
|
||||
{
|
||||
while (sublevels_up-- > 0)
|
||||
{
|
||||
pstate = pstate->parentParseState;
|
||||
Assert(pstate != NULL);
|
||||
}
|
||||
Assert(varno > 0 && varno <= length(pstate->p_rtable));
|
||||
return rt_fetch(varno, pstate->p_rtable);
|
||||
}
|
||||
|
||||
/*
|
||||
* scanRTEForColumn
|
||||
* Search the column names of a single RTE for the given name.
|
||||
@ -439,7 +455,7 @@ RTERangeTablePosn(ParseState *pstate, RangeTblEntry *rte, int *sublevels_up)
|
||||
* expression can only appear in a FROM clause, and any table named in
|
||||
* FROM will be marked as requiring read access from the beginning.
|
||||
*/
|
||||
static Node *
|
||||
Node *
|
||||
scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
|
||||
{
|
||||
Node *result = NULL;
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.115 2004/02/13 01:08:20 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.116 2004/04/02 19:06:58 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -60,14 +60,6 @@ transformTargetEntry(ParseState *pstate,
|
||||
if (expr == NULL)
|
||||
expr = transformExpr(pstate, node);
|
||||
|
||||
if (IsA(expr, RangeVar))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("relation reference \"%s\" cannot be used as a select-list entry",
|
||||
((RangeVar *) expr)->relname),
|
||||
errhint("Write \"%s\".* to denote all the columns of the relation.",
|
||||
((RangeVar *) expr)->relname)));
|
||||
|
||||
type_id = exprType(expr);
|
||||
type_mod = exprTypmod(expr);
|
||||
|
||||
@ -243,21 +235,12 @@ markTargetListOrigins(ParseState *pstate, List *targetlist)
|
||||
static void
|
||||
markTargetListOrigin(ParseState *pstate, Resdom *res, Var *var)
|
||||
{
|
||||
Index levelsup;
|
||||
RangeTblEntry *rte;
|
||||
AttrNumber attnum;
|
||||
|
||||
if (var == NULL || !IsA(var, Var))
|
||||
return;
|
||||
levelsup = var->varlevelsup;
|
||||
while (levelsup-- > 0)
|
||||
{
|
||||
pstate = pstate->parentParseState;
|
||||
Assert(pstate != NULL);
|
||||
}
|
||||
Assert(var->varno > 0 &&
|
||||
(int) var->varno <= length(pstate->p_rtable));
|
||||
rte = rt_fetch(var->varno, pstate->p_rtable);
|
||||
rte = GetRTEByRangeTablePosn(pstate, var->varno, var->varlevelsup);
|
||||
attnum = var->varattno;
|
||||
|
||||
switch (rte->rtekind)
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/include/parser/parse_relation.h,v 1.42 2003/11/29 22:41:09 pgsql Exp $
|
||||
* $PostgreSQL: pgsql/src/include/parser/parse_relation.h,v 1.43 2004/04/02 19:07:02 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -27,6 +27,11 @@ extern void checkNameSpaceConflicts(ParseState *pstate, Node *namespace1,
|
||||
extern int RTERangeTablePosn(ParseState *pstate,
|
||||
RangeTblEntry *rte,
|
||||
int *sublevels_up);
|
||||
extern RangeTblEntry *GetRTEByRangeTablePosn(ParseState *pstate,
|
||||
int varno,
|
||||
int sublevels_up);
|
||||
extern Node *scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte,
|
||||
char *colname);
|
||||
extern Node *colnameToVar(ParseState *pstate, char *colname);
|
||||
extern Node *qualifiedNameToVar(ParseState *pstate,
|
||||
char *schemaname,
|
||||
|
Loading…
x
Reference in New Issue
Block a user