Commit f0cc1326 authored by Tom Lane's avatar Tom Lane

Fix oversight in recent rowtype-handling improvements: transformTargetList

should recognize 'foo.*' when the star appears in A_Indirection, not only
in ColumnRef.  This allows 'SELECT something.*' to do what the user
expects when the something is an expression yielding a row.
parent 57d26651
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.171 2004/06/16 01:26:45 tgl Exp $ * $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.172 2004/06/19 18:19:55 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -38,7 +38,6 @@ static Oid **argtype_inherit(int nargs, Oid *argtypes); ...@@ -38,7 +38,6 @@ static Oid **argtype_inherit(int nargs, Oid *argtypes);
static int find_inheritors(Oid relid, Oid **supervec); static int find_inheritors(Oid relid, Oid **supervec);
static Oid **gen_cross_product(InhPaths *arginh, int nargs); static Oid **gen_cross_product(InhPaths *arginh, int nargs);
static FieldSelect *setup_field_select(Node *input, char *attname, Oid relid);
static void unknown_attribute(ParseState *pstate, Node *relref, char *attname); static void unknown_attribute(ParseState *pstate, Node *relref, char *attname);
...@@ -1131,33 +1130,6 @@ make_fn_arguments(ParseState *pstate, ...@@ -1131,33 +1130,6 @@ make_fn_arguments(ParseState *pstate,
} }
} }
/*
* setup_field_select
* Build a FieldSelect node that says which attribute to project to.
* This routine is called by ParseFuncOrColumn() when we have found
* a projection on a function result or parameter.
*/
static FieldSelect *
setup_field_select(Node *input, char *attname, Oid relid)
{
FieldSelect *fselect = makeNode(FieldSelect);
AttrNumber attno;
attno = get_attnum(relid, attname);
if (attno == InvalidAttrNumber)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("column \"%s\" of relation \"%s\" does not exist",
attname, get_rel_name(relid))));
fselect->arg = (Expr *) input;
fselect->fieldnum = attno;
fselect->resulttype = get_atttype(relid, attno);
fselect->resulttypmod = get_atttypmod(relid, attno);
return fselect;
}
/* /*
* ParseComplexProjection - * ParseComplexProjection -
* handles function calls with a single argument that is of complex type. * handles function calls with a single argument that is of complex type.
...@@ -1170,6 +1142,7 @@ ParseComplexProjection(ParseState *pstate, char *funcname, Node *first_arg) ...@@ -1170,6 +1142,7 @@ ParseComplexProjection(ParseState *pstate, char *funcname, Node *first_arg)
Oid argtype; Oid argtype;
Oid argrelid; Oid argrelid;
AttrNumber attnum; AttrNumber attnum;
FieldSelect *fselect;
/* /*
* Special case for whole-row Vars so that we can resolve (foo.*).bar * Special case for whole-row Vars so that we can resolve (foo.*).bar
...@@ -1205,7 +1178,14 @@ ParseComplexProjection(ParseState *pstate, char *funcname, Node *first_arg) ...@@ -1205,7 +1178,14 @@ ParseComplexProjection(ParseState *pstate, char *funcname, Node *first_arg)
return NULL; /* funcname does not match any column */ return NULL; /* funcname does not match any column */
/* Success, so generate a FieldSelect expression */ /* Success, so generate a FieldSelect expression */
return (Node *) setup_field_select(first_arg, funcname, argrelid); fselect = makeNode(FieldSelect);
fselect->arg = (Expr *) first_arg;
fselect->fieldnum = attnum;
get_atttypetypmod(argrelid, attnum,
&fselect->resulttype,
&fselect->resulttypmod);
return (Node *) fselect;
} }
/* /*
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.121 2004/06/09 19:08:17 tgl Exp $ * $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.122 2004/06/19 18:19:55 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include "parser/parse_type.h" #include "parser/parse_type.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
#include "utils/typcache.h"
static void markTargetListOrigin(ParseState *pstate, Resdom *res, Var *var); static void markTargetListOrigin(ParseState *pstate, Resdom *res, Var *var);
...@@ -37,7 +38,9 @@ static Node *transformAssignmentIndirection(ParseState *pstate, ...@@ -37,7 +38,9 @@ static Node *transformAssignmentIndirection(ParseState *pstate,
int32 targetTypMod, int32 targetTypMod,
ListCell *indirection, ListCell *indirection,
Node *rhs); Node *rhs);
static List *ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref);
static List *ExpandAllTables(ParseState *pstate); static List *ExpandAllTables(ParseState *pstate);
static List *ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind);
static char *FigureColname(Node *node); static char *FigureColname(Node *node);
static int FigureColnameInternal(Node *node, char **name); static int FigureColnameInternal(Node *node, char **name);
...@@ -108,103 +111,47 @@ transformTargetList(ParseState *pstate, List *targetlist) ...@@ -108,103 +111,47 @@ transformTargetList(ParseState *pstate, List *targetlist)
{ {
ResTarget *res = (ResTarget *) lfirst(o_target); ResTarget *res = (ResTarget *) lfirst(o_target);
/*
* Check for "something.*". Depending on the complexity of the
* "something", the star could appear as the last name in ColumnRef,
* or as the last indirection item in A_Indirection.
*/
if (IsA(res->val, ColumnRef)) if (IsA(res->val, ColumnRef))
{ {
ColumnRef *cref = (ColumnRef *) res->val; ColumnRef *cref = (ColumnRef *) res->val;
List *fields = cref->fields;
if (strcmp(strVal(llast(fields)), "*") == 0) if (strcmp(strVal(llast(cref->fields)), "*") == 0)
{
int numnames = list_length(fields);
if (numnames == 1)
{
/*
* Target item is a single '*', expand all tables
* (e.g., SELECT * FROM emp)
*/
p_target = list_concat(p_target,
ExpandAllTables(pstate));
}
else
{
/*
* Target item is relation.*, expand that table
* (e.g., SELECT emp.*, dname FROM emp, dept)
*/
char *schemaname;
char *relname;
RangeTblEntry *rte;
int sublevels_up;
switch (numnames)
{
case 2:
schemaname = NULL;
relname = strVal(linitial(fields));
break;
case 3:
schemaname = strVal(linitial(fields));
relname = strVal(lsecond(fields));
break;
case 4:
{
char *name1 = strVal(linitial(fields));
/*
* We check the catalog name and then ignore
* it.
*/
if (strcmp(name1, get_database_name(MyDatabaseId)) != 0)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cross-database references are not implemented: %s",
NameListToString(fields))));
schemaname = strVal(lsecond(fields));
relname = strVal(lthird(fields));
break;
}
default:
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("improper qualified name (too many dotted names): %s",
NameListToString(fields))));
schemaname = NULL; /* keep compiler quiet */
relname = NULL;
break;
}
rte = refnameRangeTblEntry(pstate, schemaname, relname,
&sublevels_up);
if (rte == NULL)
rte = addImplicitRTE(pstate, makeRangeVar(schemaname,
relname));
p_target = list_concat(p_target,
expandRelAttrs(pstate, rte));
}
}
else
{ {
/* Plain ColumnRef node, treat it as an expression */ /* It is something.*, expand into multiple items */
p_target = lappend(p_target, p_target = list_concat(p_target,
transformTargetEntry(pstate, ExpandColumnRefStar(pstate, cref));
res->val, continue;
NULL,
res->name,
false));
} }
} }
else else if (IsA(res->val, A_Indirection))
{ {
/* Everything else but ColumnRef */ A_Indirection *ind = (A_Indirection *) res->val;
p_target = lappend(p_target, Node *lastitem = llast(ind->indirection);
transformTargetEntry(pstate,
res->val, if (IsA(lastitem, String) &&
NULL, strcmp(strVal(lastitem), "*") == 0)
res->name, {
false)); /* It is something.*, expand into multiple items */
p_target = list_concat(p_target,
ExpandIndirectionStar(pstate, ind));
continue;
}
} }
/*
* Not "something.*", so transform as a single expression
*/
p_target = lappend(p_target,
transformTargetEntry(pstate,
res->val,
NULL,
res->name,
false));
} }
return p_target; return p_target;
...@@ -719,8 +666,90 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos) ...@@ -719,8 +666,90 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos)
return cols; return cols;
} }
/* ExpandAllTables() /*
* Turns '*' (in the target list) into a list of targetlist entries. * ExpandColumnRefStar()
* Turns foo.* (in the target list) into a list of targetlist entries.
*
* This handles the case where '*' appears as the last or only name in a
* ColumnRef.
*/
static List *
ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref)
{
List *fields = cref->fields;
int numnames = list_length(fields);
if (numnames == 1)
{
/*
* Target item is a bare '*', expand all tables
*
* (e.g., SELECT * FROM emp, dept)
*/
return ExpandAllTables(pstate);
}
else
{
/*
* Target item is relation.*, expand that table
*
* (e.g., SELECT emp.*, dname FROM emp, dept)
*/
char *schemaname;
char *relname;
RangeTblEntry *rte;
int sublevels_up;
switch (numnames)
{
case 2:
schemaname = NULL;
relname = strVal(linitial(fields));
break;
case 3:
schemaname = strVal(linitial(fields));
relname = strVal(lsecond(fields));
break;
case 4:
{
char *name1 = strVal(linitial(fields));
/*
* We check the catalog name and then ignore
* it.
*/
if (strcmp(name1, get_database_name(MyDatabaseId)) != 0)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cross-database references are not implemented: %s",
NameListToString(fields))));
schemaname = strVal(lsecond(fields));
relname = strVal(lthird(fields));
break;
}
default:
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("improper qualified name (too many dotted names): %s",
NameListToString(fields))));
schemaname = NULL; /* keep compiler quiet */
relname = NULL;
break;
}
rte = refnameRangeTblEntry(pstate, schemaname, relname,
&sublevels_up);
if (rte == NULL)
rte = addImplicitRTE(pstate, makeRangeVar(schemaname,
relname));
return expandRelAttrs(pstate, rte);
}
}
/*
* ExpandAllTables()
* Turns '*' (in the target list) into a list of targetlist entries.
* *
* tlist entries are generated for each relation appearing at the top level * tlist entries are generated for each relation appearing at the top level
* of the query's namespace, except for RTEs marked not inFromCl. (These * of the query's namespace, except for RTEs marked not inFromCl. (These
...@@ -770,6 +799,84 @@ ExpandAllTables(ParseState *pstate) ...@@ -770,6 +799,84 @@ ExpandAllTables(ParseState *pstate)
return target; return target;
} }
/*
* ExpandIndirectionStar()
* Turns foo.* (in the target list) into a list of targetlist entries.
*
* This handles the case where '*' appears as the last item in A_Indirection.
*/
static List *
ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind)
{
Node *expr;
TupleDesc tupleDesc;
int numAttrs;
int i;
List *te_list = NIL;
/* Strip off the '*' to create a reference to the rowtype object */
ind = copyObject(ind);
ind->indirection = list_truncate(ind->indirection,
list_length(ind->indirection) - 1);
/* And transform that */
expr = transformExpr(pstate, (Node *) ind);
/* Verify it's a composite type, and get the tupdesc */
tupleDesc = lookup_rowtype_tupdesc(exprType(expr), exprTypmod(expr));
/* Generate a list of references to the individual fields */
numAttrs = tupleDesc->natts;
for (i = 0; i < numAttrs; i++)
{
Form_pg_attribute att = tupleDesc->attrs[i];
Node *fieldnode;
TargetEntry *te;
if (att->attisdropped)
continue;
/*
* If we got a whole-row Var from the rowtype reference, we can
* expand the fields as simple Vars. Otherwise we must generate
* multiple copies of the rowtype reference and do FieldSelects.
*/
if (IsA(expr, Var) &&
((Var *) expr)->varattno == InvalidAttrNumber)
{
Var *var = (Var *) expr;
fieldnode = (Node *) makeVar(var->varno,
i + 1,
att->atttypid,
att->atttypmod,
var->varlevelsup);
}
else
{
FieldSelect *fselect = makeNode(FieldSelect);
fselect->arg = (Expr *) copyObject(expr);
fselect->fieldnum = i + 1;
fselect->resulttype = att->atttypid;
fselect->resulttypmod = att->atttypmod;
fieldnode = (Node *) fselect;
}
te = makeNode(TargetEntry);
te->resdom = makeResdom((AttrNumber) pstate->p_next_resno++,
att->atttypid,
att->atttypmod,
pstrdup(NameStr(att->attname)),
false);
te->expr = (Expr *) fieldnode;
te_list = lappend(te_list, te);
}
return te_list;
}
/* /*
* FigureColname - * FigureColname -
* if the name of the resulting column is not specified in the target * if the name of the resulting column is not specified in the target
......
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/cache/typcache.c,v 1.7 2004/06/05 01:55:05 tgl Exp $ * $PostgreSQL: pgsql/src/backend/utils/cache/typcache.c,v 1.8 2004/06/19 18:19:56 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -409,8 +409,8 @@ lookup_rowtype_tupdesc_noerror(Oid type_id, int32 typmod, bool noError) ...@@ -409,8 +409,8 @@ lookup_rowtype_tupdesc_noerror(Oid type_id, int32 typmod, bool noError)
if (typentry->tupDesc == NULL && !noError) if (typentry->tupDesc == NULL && !noError)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE), (errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("type %u is not composite", errmsg("type %s is not composite",
type_id))); format_type_be(type_id))));
return typentry->tupDesc; return typentry->tupDesc;
} }
else else
......
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