提交 f0cc1326 编写于 作者: T 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.
上级 57d26651
......@@ -8,7 +8,7 @@
*
*
* 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);
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(ParseState *pstate, Node *relref, char *attname);
......@@ -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 -
* handles function calls with a single argument that is of complex type.
......@@ -1170,6 +1142,7 @@ ParseComplexProjection(ParseState *pstate, char *funcname, Node *first_arg)
Oid argtype;
Oid argrelid;
AttrNumber attnum;
FieldSelect *fselect;
/*
* 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)
return NULL; /* funcname does not match any column */
/* 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 @@
*
*
* 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 @@
#include "parser/parse_type.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/typcache.h"
static void markTargetListOrigin(ParseState *pstate, Resdom *res, Var *var);
......@@ -37,7 +38,9 @@ static Node *transformAssignmentIndirection(ParseState *pstate,
int32 targetTypMod,
ListCell *indirection,
Node *rhs);
static List *ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref);
static List *ExpandAllTables(ParseState *pstate);
static List *ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind);
static char *FigureColname(Node *node);
static int FigureColnameInternal(Node *node, char **name);
......@@ -108,103 +111,47 @@ transformTargetList(ParseState *pstate, List *targetlist)
{
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))
{
ColumnRef *cref = (ColumnRef *) res->val;
List *fields = cref->fields;
if (strcmp(strVal(llast(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
if (strcmp(strVal(llast(cref->fields)), "*") == 0)
{
/* Plain ColumnRef node, treat it as an expression */
p_target = lappend(p_target,
transformTargetEntry(pstate,
res->val,
NULL,
res->name,
false));
/* It is something.*, expand into multiple items */
p_target = list_concat(p_target,
ExpandColumnRefStar(pstate, cref));
continue;
}
}
else
else if (IsA(res->val, A_Indirection))
{
/* Everything else but ColumnRef */
p_target = lappend(p_target,
transformTargetEntry(pstate,
res->val,
NULL,
res->name,
false));
A_Indirection *ind = (A_Indirection *) res->val;
Node *lastitem = llast(ind->indirection);
if (IsA(lastitem, String) &&
strcmp(strVal(lastitem), "*") == 0)
{
/* 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;
......@@ -719,8 +666,90 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos)
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
* of the query's namespace, except for RTEs marked not inFromCl. (These
......@@ -770,6 +799,84 @@ ExpandAllTables(ParseState *pstate)
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 -
* if the name of the resulting column is not specified in the target
......
......@@ -36,7 +36,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* 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)
if (typentry->tupDesc == NULL && !noError)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("type %u is not composite",
type_id)));
errmsg("type %s is not composite",
format_type_be(type_id))));
return typentry->tupDesc;
}
else
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册