diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index d5d71b67afdbe320db81c7d87ecab5154c702afb..883ad1ca9f609630bbae9a36ec1817a29fc36388 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -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; } /* diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index e0f0e6c930e757b31dd5869183f36dab7e3a7fc4..7dcbc7b7b741d0e9a04d54ff1e3824730cd37b36 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -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 diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c index 8ad60423c78e7e686fc93ccc1e22fa0608cd1933..362480b250b50f09f9cff7c3beb96fe4304fb7be 100644 --- a/src/backend/utils/cache/typcache.c +++ b/src/backend/utils/cache/typcache.c @@ -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