1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-29 10:41:53 +03:00

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.
This commit is contained in:
Tom Lane
2004-06-19 18:19:56 +00:00
parent 57d2665108
commit f0cc132621
3 changed files with 211 additions and 124 deletions

View File

@ -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)
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 */
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