From 449a00fbbd49c00ba031432e8b6913a55e8ad1f6 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Sat, 30 Aug 2008 01:39:14 +0000 Subject: [PATCH] Fix the raw-parsetree representation of star (as in SELECT * FROM or SELECT foo.*) so that it cannot be confused with a quoted identifier "*". Instead create a separate node type A_Star to represent this notation. Per pgsql-hackers discussion of 2007-Sep-27. --- src/backend/catalog/namespace.c | 16 +++++- src/backend/commands/async.c | 18 ++----- src/backend/nodes/copyfuncs.c | 13 ++++- src/backend/nodes/equalfuncs.c | 11 ++++- src/backend/nodes/outfuncs.c | 11 ++++- src/backend/parser/gram.y | 69 +++++++++++++++++--------- src/backend/parser/parse_clause.c | 5 +- src/backend/parser/parse_expr.c | 82 ++++++++++++++++++++++++------- src/backend/parser/parse_target.c | 42 ++++++++-------- src/backend/parser/parse_type.c | 5 +- src/backend/tcop/utility.c | 7 ++- src/include/nodes/nodes.h | 3 +- src/include/nodes/parsenodes.h | 47 +++++++++++------- 13 files changed, 223 insertions(+), 106 deletions(-) diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c index c6db3d16b9..7f0ea88c43 100644 --- a/src/backend/catalog/namespace.c +++ b/src/backend/catalog/namespace.c @@ -13,7 +13,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.109 2008/07/16 16:55:23 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.110 2008/08/30 01:39:13 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -2189,6 +2189,9 @@ makeRangeVarFromNameList(List *names) * * This is used primarily to form error messages, and so we do not quote * the list elements, for the sake of legibility. + * + * In most scenarios the list elements should always be Value strings, + * but we also allow A_Star for the convenience of ColumnRef processing. */ char * NameListToString(List *names) @@ -2200,9 +2203,18 @@ NameListToString(List *names) foreach(l, names) { + Node *name = (Node *) lfirst(l); + if (l != list_head(names)) appendStringInfoChar(&string, '.'); - appendStringInfoString(&string, strVal(lfirst(l))); + + if (IsA(name, String)) + appendStringInfoString(&string, strVal(name)); + else if (IsA(name, A_Star)) + appendStringInfoString(&string, "*"); + else + elog(ERROR, "unexpected node type in name list: %d", + (int) nodeTag(name)); } return string.data; diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c index 0a58d5e752..fabba799c4 100644 --- a/src/backend/commands/async.c +++ b/src/backend/commands/async.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/async.c,v 1.140 2008/03/26 21:10:37 alvherre Exp $ + * $PostgreSQL: pgsql/src/backend/commands/async.c,v 1.141 2008/08/30 01:39:13 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -274,24 +274,16 @@ Async_Listen(const char *relname) void Async_Unlisten(const char *relname) { - /* Handle specially the `unlisten "*"' command */ - if ((!relname) || (*relname == '\0') || (strcmp(relname, "*") == 0)) - { - Async_UnlistenAll(); - } - else - { - if (Trace_notify) - elog(DEBUG1, "Async_Unlisten(%s,%d)", relname, MyProcPid); + if (Trace_notify) + elog(DEBUG1, "Async_Unlisten(%s,%d)", relname, MyProcPid); - queue_listen(LISTEN_UNLISTEN, relname); - } + queue_listen(LISTEN_UNLISTEN, relname); } /* * Async_UnlistenAll * - * This is invoked by UNLISTEN "*" command, and also at backend exit. + * This is invoked by UNLISTEN * command, and also at backend exit. */ void Async_UnlistenAll(void) diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 4e984719d3..67a25500da 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.402 2008/08/28 23:09:45 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.403 2008/08/30 01:39:13 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1697,6 +1697,14 @@ _copyFuncCall(FuncCall *from) return newnode; } +static A_Star * +_copyAStar(A_Star *from) +{ + A_Star *newnode = makeNode(A_Star); + + return newnode; +} + static A_Indices * _copyAIndices(A_Indices *from) { @@ -3589,6 +3597,9 @@ copyObject(void *from) case T_FuncCall: retval = _copyFuncCall(from); break; + case T_A_Star: + retval = _copyAStar(from); + break; case T_A_Indices: retval = _copyAIndices(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 3d130c9524..298d03aa9b 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -22,7 +22,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.329 2008/08/28 23:09:46 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.330 2008/08/30 01:39:13 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1765,6 +1765,12 @@ _equalFuncCall(FuncCall *a, FuncCall *b) return true; } +static bool +_equalAStar(A_Star *a, A_Star *b) +{ + return true; +} + static bool _equalAIndices(A_Indices *a, A_Indices *b) { @@ -2531,6 +2537,9 @@ equal(void *a, void *b) case T_FuncCall: retval = _equalFuncCall(a, b); break; + case T_A_Star: + retval = _equalAStar(a, b); + break; case T_A_Indices: retval = _equalAIndices(a, b); break; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 91fa30ae23..882dd0f9f1 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.336 2008/08/28 23:09:46 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.337 2008/08/30 01:39:14 tgl Exp $ * * NOTES * Every node type that can appear in stored rules' parsetrees *must* @@ -1989,6 +1989,12 @@ _outAConst(StringInfo str, A_Const *node) WRITE_LOCATION_FIELD(location); } +static void +_outA_Star(StringInfo str, A_Star *node) +{ + WRITE_NODE_TYPE("A_STAR"); +} + static void _outA_Indices(StringInfo str, A_Indices *node) { @@ -2467,6 +2473,9 @@ _outNode(StringInfo str, void *obj) case T_A_Const: _outAConst(str, obj); break; + case T_A_Star: + _outA_Star(str, obj); + break; case T_A_Indices: _outA_Indices(str, obj); break; diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index e908750f76..78e81fbf56 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.619 2008/08/28 23:09:47 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.620 2008/08/30 01:39:14 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -89,7 +89,7 @@ static bool QueryIsRule = FALSE; */ /*#define __YYSCLASS*/ -static Node *makeColumnRef(char *relname, List *indirection, int location); +static Node *makeColumnRef(char *colname, List *indirection, int location); static Node *makeTypeCast(Node *arg, TypeName *typename, int location); static Node *makeStringConst(char *str, int location); static Node *makeStringConstCast(char *str, int location, TypeName *typename); @@ -102,6 +102,7 @@ static Node *makeBoolAConst(bool state, int location); static FuncCall *makeOverlaps(List *largs, List *rargs, int location); static void check_qualified_name(List *names); static List *check_func_name(List *names); +static List *check_indirection(List *indirection); static List *extractArgTypes(List *parameters); static SelectStmt *findLeftmostSelect(SelectStmt *node); static void insertSelectOptions(SelectStmt *stmt, @@ -5144,9 +5145,7 @@ UnlistenStmt: | UNLISTEN '*' { UnlistenStmt *n = makeNode(UnlistenStmt); - n->relation = makeNode(RangeVar); - n->relation->relname = "*"; - n->relation->schemaname = NULL; + n->relation = NULL; $$ = (Node *)n; } ; @@ -5999,7 +5998,7 @@ insert_column_item: { $$ = makeNode(ResTarget); $$->name = $1; - $$->indirection = $2; + $$->indirection = check_indirection($2); $$->val = NULL; $$->location = @1; } @@ -6138,7 +6137,7 @@ set_target: { $$ = makeNode(ResTarget); $$->name = $1; - $$->indirection = $2; + $$->indirection = check_indirection($2); $$->val = NULL; /* upper production sets this */ $$->location = @1; } @@ -7842,7 +7841,7 @@ c_expr: columnref { $$ = $1; } { A_Indirection *n = makeNode(A_Indirection); n->arg = (Node *) p; - n->indirection = $2; + n->indirection = check_indirection($2); $$ = (Node *) n; } else @@ -7854,7 +7853,7 @@ c_expr: columnref { $$ = $1; } { A_Indirection *n = makeNode(A_Indirection); n->arg = $2; - n->indirection = $4; + n->indirection = check_indirection($4); $$ = (Node *)n; } else @@ -8409,7 +8408,7 @@ xml_attribute_el: a_expr AS ColLabel { $$ = makeNode(ResTarget); $$->name = $3; - $$->indirection = NULL; + $$->indirection = NIL; $$->val = (Node *) $1; $$->location = @1; } @@ -8417,7 +8416,7 @@ xml_attribute_el: a_expr AS ColLabel { $$ = makeNode(ResTarget); $$->name = NULL; - $$->indirection = NULL; + $$->indirection = NIL; $$->val = (Node *) $1; $$->location = @1; } @@ -8724,7 +8723,7 @@ indirection_el: } | '.' '*' { - $$ = (Node *) makeString("*"); + $$ = (Node *) makeNode(A_Star); } | '[' a_expr ']' { @@ -8833,7 +8832,7 @@ target_el: a_expr AS ColLabel | '*' { ColumnRef *n = makeNode(ColumnRef); - n->fields = list_make1(makeString("*")); + n->fields = list_make1(makeNode(A_Star)); n->location = @1; $$ = makeNode(ResTarget); @@ -9511,7 +9510,7 @@ SpecialRuleRelation: %% static Node * -makeColumnRef(char *relname, List *indirection, int location) +makeColumnRef(char *colname, List *indirection, int location) { /* * Generate a ColumnRef node, with an A_Indirection node added if there @@ -9533,23 +9532,30 @@ makeColumnRef(char *relname, List *indirection, int location) if (nfields == 0) { /* easy case - all indirection goes to A_Indirection */ - c->fields = list_make1(makeString(relname)); - i->indirection = indirection; + c->fields = list_make1(makeString(colname)); + i->indirection = check_indirection(indirection); } else { /* got to split the list in two */ - i->indirection = list_copy_tail(indirection, nfields); + i->indirection = check_indirection(list_copy_tail(indirection, + nfields)); indirection = list_truncate(indirection, nfields); - c->fields = lcons(makeString(relname), indirection); + c->fields = lcons(makeString(colname), indirection); } i->arg = (Node *) c; return (Node *) i; } + else if (IsA(lfirst(l), A_Star)) + { + /* We only allow '*' at the end of a ColumnRef */ + if (lnext(l) != NULL) + yyerror("improper use of \"*\""); + } nfields++; } /* No subscripting, so all indirection gets added to field list */ - c->fields = lcons(makeString(relname), indirection); + c->fields = lcons(makeString(colname), indirection); return (Node *) c; } @@ -9712,8 +9718,6 @@ check_qualified_name(List *names) { if (!IsA(lfirst(i), String)) yyerror("syntax error"); - else if (strcmp(strVal(lfirst(i)), "*") == 0) - yyerror("syntax error"); } } @@ -9731,12 +9735,31 @@ check_func_name(List *names) { if (!IsA(lfirst(i), String)) yyerror("syntax error"); - else if (strcmp(strVal(lfirst(i)), "*") == 0) - yyerror("syntax error"); } return names; } +/* check_indirection --- check the result of indirection production + * + * We only allow '*' at the end of the list, but it's hard to enforce that + * in the grammar, so do it here. + */ +static List * +check_indirection(List *indirection) +{ + ListCell *l; + + foreach(l, indirection) + { + if (IsA(lfirst(l), A_Star)) + { + if (lnext(l) != NULL) + yyerror("improper use of \"*\""); + } + } + return indirection; +} + /* extractArgTypes() * Given a list of FunctionParameter nodes, extract a list of just the * argument types (TypeNames) for input parameters only. This is what diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index 7097ef5058..5285f0ba3d 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.177 2008/08/28 23:09:47 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.178 2008/08/30 01:39:14 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1181,7 +1181,8 @@ findTargetlistEntry(ParseState *pstate, Node *node, List **tlist, int clause) *---------- */ if (IsA(node, ColumnRef) && - list_length(((ColumnRef *) node)->fields) == 1) + list_length(((ColumnRef *) node)->fields) == 1 && + IsA(linitial(((ColumnRef *) node)->fields), String)) { char *name = strVal(linitial(((ColumnRef *) node)->fields)); int location = ((ColumnRef *) node)->location; diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 3c14cf1b52..8496e7291c 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.232 2008/08/28 23:09:47 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.233 2008/08/30 01:39:14 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -334,6 +334,13 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection) if (IsA(n, A_Indices)) subscripts = lappend(subscripts, n); + else if (IsA(n, A_Star)) + { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("row expansion via \"*\" is not supported here"), + parser_errposition(pstate, exprLocation(basenode)))); + } else { Assert(IsA(n, String)); @@ -403,10 +410,14 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) { case 1: { - char *name = strVal(linitial(cref->fields)); + Node *field1 = (Node *) linitial(cref->fields); + char *name1; + + Assert(IsA(field1, String)); + name1 = strVal(field1); /* Try to identify as an unqualified column */ - node = colNameToVar(pstate, name, false, cref->location); + node = colNameToVar(pstate, name1, false, cref->location); if (node == NULL) { @@ -419,7 +430,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) * have used VALUE as a column name in the past.) */ if (pstate->p_value_substitute != NULL && - strcmp(name, "value") == 0) + strcmp(name1, "value") == 0) { node = (Node *) copyObject(pstate->p_value_substitute); @@ -442,32 +453,40 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) * PostQUEL-inspired syntax. The preferred form now is * "rel.*". */ - if (refnameRangeTblEntry(pstate, NULL, name, + if (refnameRangeTblEntry(pstate, NULL, name1, &levels_up) != NULL) - node = transformWholeRowRef(pstate, NULL, name, + node = transformWholeRowRef(pstate, NULL, name1, cref->location); else ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("column \"%s\" does not exist", - name), + name1), parser_errposition(pstate, cref->location))); } break; } case 2: { - char *name1 = strVal(linitial(cref->fields)); - char *name2 = strVal(lsecond(cref->fields)); + Node *field1 = (Node *) linitial(cref->fields); + Node *field2 = (Node *) lsecond(cref->fields); + char *name1; + char *name2; + + Assert(IsA(field1, String)); + name1 = strVal(field1); /* Whole-row reference? */ - if (strcmp(name2, "*") == 0) + if (IsA(field2, A_Star)) { node = transformWholeRowRef(pstate, NULL, name1, cref->location); break; } + Assert(IsA(field2, String)); + name2 = strVal(field2); + /* Try to identify as a once-qualified column */ node = qualifiedNameToVar(pstate, NULL, name1, name2, true, cref->location); @@ -490,18 +509,29 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) } case 3: { - char *name1 = strVal(linitial(cref->fields)); - char *name2 = strVal(lsecond(cref->fields)); - char *name3 = strVal(lthird(cref->fields)); + Node *field1 = (Node *) linitial(cref->fields); + Node *field2 = (Node *) lsecond(cref->fields); + Node *field3 = (Node *) lthird(cref->fields); + char *name1; + char *name2; + char *name3; + + Assert(IsA(field1, String)); + name1 = strVal(field1); + Assert(IsA(field2, String)); + name2 = strVal(field2); /* Whole-row reference? */ - if (strcmp(name3, "*") == 0) + if (IsA(field3, A_Star)) { node = transformWholeRowRef(pstate, name1, name2, cref->location); break; } + Assert(IsA(field3, String)); + name3 = strVal(field3); + /* Try to identify as a twice-qualified column */ node = qualifiedNameToVar(pstate, name1, name2, name3, true, cref->location); @@ -520,10 +550,21 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) } case 4: { - char *name1 = strVal(linitial(cref->fields)); - char *name2 = strVal(lsecond(cref->fields)); - char *name3 = strVal(lthird(cref->fields)); - char *name4 = strVal(lfourth(cref->fields)); + Node *field1 = (Node *) linitial(cref->fields); + Node *field2 = (Node *) lsecond(cref->fields); + Node *field3 = (Node *) lthird(cref->fields); + Node *field4 = (Node *) lfourth(cref->fields); + char *name1; + char *name2; + char *name3; + char *name4; + + Assert(IsA(field1, String)); + name1 = strVal(field1); + Assert(IsA(field2, String)); + name2 = strVal(field2); + Assert(IsA(field3, String)); + name3 = strVal(field3); /* * We check the catalog name and then ignore it. @@ -536,13 +577,16 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) parser_errposition(pstate, cref->location))); /* Whole-row reference? */ - if (strcmp(name4, "*") == 0) + if (IsA(field4, A_Star)) { node = transformWholeRowRef(pstate, name2, name3, cref->location); break; } + Assert(IsA(field4, String)); + name4 = strVal(field4); + /* Try to identify as a twice-qualified column */ node = qualifiedNameToVar(pstate, name2, name3, name4, true, cref->location); diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index c7758e9add..df87f3874c 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.162 2008/08/28 23:09:48 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.163 2008/08/30 01:39:14 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -109,14 +109,14 @@ transformTargetList(ParseState *pstate, List *targetlist) /* * Check for "something.*". Depending on the complexity of the - * "something", the star could appear as the last name in ColumnRef, + * "something", the star could appear as the last field in ColumnRef, * or as the last indirection item in A_Indirection. */ if (IsA(res->val, ColumnRef)) { ColumnRef *cref = (ColumnRef *) res->val; - if (strcmp(strVal(llast(cref->fields)), "*") == 0) + if (IsA(llast(cref->fields), A_Star)) { /* It is something.*, expand into multiple items */ p_target = list_concat(p_target, @@ -128,10 +128,8 @@ transformTargetList(ParseState *pstate, List *targetlist) else if (IsA(res->val, A_Indirection)) { A_Indirection *ind = (A_Indirection *) res->val; - Node *lastitem = llast(ind->indirection); - if (IsA(lastitem, String) && - strcmp(strVal(lastitem), "*") == 0) + if (IsA(llast(ind->indirection), A_Star)) { /* It is something.*, expand into multiple items */ p_target = list_concat(p_target, @@ -176,14 +174,14 @@ transformExpressionList(ParseState *pstate, List *exprlist) /* * Check for "something.*". Depending on the complexity of the - * "something", the star could appear as the last name in ColumnRef, + * "something", the star could appear as the last field in ColumnRef, * or as the last indirection item in A_Indirection. */ if (IsA(e, ColumnRef)) { ColumnRef *cref = (ColumnRef *) e; - if (strcmp(strVal(llast(cref->fields)), "*") == 0) + if (IsA(llast(cref->fields), A_Star)) { /* It is something.*, expand into multiple items */ result = list_concat(result, @@ -195,10 +193,8 @@ transformExpressionList(ParseState *pstate, List *exprlist) else if (IsA(e, A_Indirection)) { A_Indirection *ind = (A_Indirection *) e; - Node *lastitem = llast(ind->indirection); - if (IsA(lastitem, String) && - strcmp(strVal(lastitem), "*") == 0) + if (IsA(llast(ind->indirection), A_Star)) { /* It is something.*, expand into multiple items */ result = list_concat(result, @@ -560,6 +556,13 @@ transformAssignmentIndirection(ParseState *pstate, if (((A_Indices *) n)->lidx != NULL) isSlice = true; } + else if (IsA(n, A_Star)) + { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("row expansion via \"*\" is not supported here"), + parser_errposition(pstate, location))); + } else { FieldStore *fstore; @@ -809,7 +812,7 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos) * ExpandColumnRefStar() * Transforms foo.* into a list of expressions or targetlist entries. * - * This handles the case where '*' appears as the last or only name in a + * This handles the case where '*' appears as the last or only item in a * ColumnRef. The code is shared between the case of foo.* at the top level * in a SELECT target list (where we want TargetEntry nodes in the result) * and foo.* in a ROW() or VALUES() construct (where we want just bare @@ -830,13 +833,9 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, * (e.g., SELECT * FROM emp, dept) * * Since the grammar only accepts bare '*' at top level of SELECT, we - * need not handle the targetlist==false case here. However, we must - * test for it because the grammar currently fails to distinguish a - * quoted name "*" from a real asterisk. + * need not handle the targetlist==false case here. */ - if (!targetlist) - elog(ERROR, "invalid use of *"); - + Assert(targetlist); return ExpandAllTables(pstate); } else @@ -1226,7 +1225,7 @@ FigureColnameInternal(Node *node, char **name) { Node *i = lfirst(l); - if (strcmp(strVal(i), "*") != 0) + if (IsA(i, String)) fname = strVal(i); } if (fname) @@ -1242,13 +1241,12 @@ FigureColnameInternal(Node *node, char **name) char *fname = NULL; ListCell *l; - /* find last field name, if any, ignoring "*" */ + /* find last field name, if any, ignoring "*" and subscripts */ foreach(l, ind->indirection) { Node *i = lfirst(l); - if (IsA(i, String) && - strcmp(strVal(i), "*") != 0) + if (IsA(i, String)) fname = strVal(i); } if (fname) diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c index 78e210167d..7f6f161745 100644 --- a/src/backend/parser/parse_type.c +++ b/src/backend/parser/parse_type.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.97 2008/04/29 20:44:49 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.98 2008/08/30 01:39:14 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -305,7 +305,8 @@ typenameTypeMod(ParseState *pstate, const TypeName *typename, Type typ) { ColumnRef *cr = (ColumnRef *) tm; - if (list_length(cr->fields) == 1) + if (list_length(cr->fields) == 1 && + IsA(linitial(cr->fields), String)) cstr = strVal(linitial(cr->fields)); } if (!cstr) diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 1e419a7265..2f9078c2fb 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.296 2008/08/13 00:07:50 alvherre Exp $ + * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.297 2008/08/30 01:39:14 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -817,7 +817,10 @@ ProcessUtility(Node *parsetree, { UnlistenStmt *stmt = (UnlistenStmt *) parsetree; - Async_Unlisten(stmt->relation->relname); + if (stmt->relation) + Async_Unlisten(stmt->relation->relname); + else + Async_UnlistenAll(); } break; diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 78dd917ecf..7e3d67a241 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.210 2008/08/29 22:49:07 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.211 2008/08/30 01:39:14 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -324,6 +324,7 @@ typedef enum NodeTag T_ParamRef, T_A_Const, T_FuncCall, + T_A_Star, T_A_Indices, T_A_Indirection, T_A_ArrayExpr, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 6292ca237c..7eea317589 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -13,7 +13,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.372 2008/08/28 23:09:48 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.373 2008/08/30 01:39:14 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -178,8 +178,10 @@ typedef struct TypeName /* * ColumnRef - specifies a reference to a column, or possibly a whole tuple * - * The "fields" list must be nonempty; its last component may be "*" - * instead of a regular field name. + * The "fields" list must be nonempty. It can contain string Value nodes + * (representing names) and A_Star nodes (representing occurrence of a '*'). + * Currently, A_Star must appear only as the last list element --- the grammar + * is responsible for enforcing this! * * Note: any array subscripting or selection of fields from composite columns * is represented by an A_Indirection node above the ColumnRef. However, @@ -189,7 +191,7 @@ typedef struct TypeName typedef struct ColumnRef { NodeTag type; - List *fields; /* field names (list of Value strings) */ + List *fields; /* field names (Value strings) or A_Star */ int location; /* token location, or -1 if unknown */ } ColumnRef; @@ -271,35 +273,46 @@ typedef struct FuncCall } FuncCall; /* - * A_Indices - array reference or bounds ([lidx:uidx] or [uidx]) + * A_Star - '*' representing all columns of a table or compound field + * + * This can appear within ColumnRef.fields, A_Indirection.indirection, and + * ResTarget.indirection lists. + */ +typedef struct A_Star +{ + NodeTag type; +} A_Star; + +/* + * A_Indices - array subscript or slice bounds ([lidx:uidx] or [uidx]) */ typedef struct A_Indices { NodeTag type; - Node *lidx; /* could be NULL */ + Node *lidx; /* NULL if it's a single subscript */ Node *uidx; } A_Indices; /* * A_Indirection - select a field and/or array element from an expression * - * The indirection list can contain both A_Indices nodes (representing - * subscripting) and string Value nodes (representing field selection - * --- the string value is the name of the field to select). For example, - * a complex selection operation like + * The indirection list can contain A_Indices nodes (representing + * subscripting), string Value nodes (representing field selection --- the + * string value is the name of the field to select), and A_Star nodes + * (representing selection of all fields of a composite type). + * For example, a complex selection operation like * (foo).field1[42][7].field2 * would be represented with a single A_Indirection node having a 4-element * indirection list. * - * Note: as of Postgres 8.0, we don't support arrays of composite values, - * so cases in which a field select follows a subscript aren't actually - * semantically legal. However the parser is prepared to handle such. + * Currently, A_Star must appear only as the last list element --- the grammar + * is responsible for enforcing this! */ typedef struct A_Indirection { NodeTag type; Node *arg; /* the thing being selected from */ - List *indirection; /* subscripts and/or field names */ + List *indirection; /* subscripts and/or field names and/or * */ } A_Indirection; /* @@ -334,7 +347,7 @@ typedef struct ResTarget { NodeTag type; char *name; /* column name or NULL */ - List *indirection; /* subscripts and field names, or NIL */ + List *indirection; /* subscripts, field names, and '*', or NIL */ Node *val; /* the value expression to compute or assign */ int location; /* token location, or -1 if unknown */ } ResTarget; @@ -1739,7 +1752,7 @@ typedef struct NotifyStmt typedef struct ListenStmt { NodeTag type; - RangeVar *relation; /* qualified name to listen on */ + RangeVar *relation; /* name to listen on */ } ListenStmt; /* ---------------------- @@ -1749,7 +1762,7 @@ typedef struct ListenStmt typedef struct UnlistenStmt { NodeTag type; - RangeVar *relation; /* qualified name to unlisten on, or '*' */ + RangeVar *relation; /* name to unlisten on, or NULL for all */ } UnlistenStmt; /* ---------------------- -- 2.40.0