Commit 449a00fb authored by Tom Lane's avatar Tom Lane

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.
parent 6253f9de
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,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/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) ...@@ -2189,6 +2189,9 @@ makeRangeVarFromNameList(List *names)
* *
* This is used primarily to form error messages, and so we do not quote * This is used primarily to form error messages, and so we do not quote
* the list elements, for the sake of legibility. * 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 * char *
NameListToString(List *names) NameListToString(List *names)
...@@ -2200,9 +2203,18 @@ NameListToString(List *names) ...@@ -2200,9 +2203,18 @@ NameListToString(List *names)
foreach(l, names) foreach(l, names)
{ {
Node *name = (Node *) lfirst(l);
if (l != list_head(names)) if (l != list_head(names))
appendStringInfoChar(&string, '.'); 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; return string.data;
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,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/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) ...@@ -274,24 +274,16 @@ Async_Listen(const char *relname)
void void
Async_Unlisten(const char *relname) Async_Unlisten(const char *relname)
{ {
/* Handle specially the `unlisten "*"' command */
if ((!relname) || (*relname == '\0') || (strcmp(relname, "*") == 0))
{
Async_UnlistenAll();
}
else
{
if (Trace_notify) if (Trace_notify)
elog(DEBUG1, "Async_Unlisten(%s,%d)", relname, MyProcPid); elog(DEBUG1, "Async_Unlisten(%s,%d)", relname, MyProcPid);
queue_listen(LISTEN_UNLISTEN, relname); queue_listen(LISTEN_UNLISTEN, relname);
}
} }
/* /*
* Async_UnlistenAll * 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 void
Async_UnlistenAll(void) Async_UnlistenAll(void)
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,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/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) ...@@ -1697,6 +1697,14 @@ _copyFuncCall(FuncCall *from)
return newnode; return newnode;
} }
static A_Star *
_copyAStar(A_Star *from)
{
A_Star *newnode = makeNode(A_Star);
return newnode;
}
static A_Indices * static A_Indices *
_copyAIndices(A_Indices *from) _copyAIndices(A_Indices *from)
{ {
...@@ -3589,6 +3597,9 @@ copyObject(void *from) ...@@ -3589,6 +3597,9 @@ copyObject(void *from)
case T_FuncCall: case T_FuncCall:
retval = _copyFuncCall(from); retval = _copyFuncCall(from);
break; break;
case T_A_Star:
retval = _copyAStar(from);
break;
case T_A_Indices: case T_A_Indices:
retval = _copyAIndices(from); retval = _copyAIndices(from);
break; break;
......
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,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/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) ...@@ -1765,6 +1765,12 @@ _equalFuncCall(FuncCall *a, FuncCall *b)
return true; return true;
} }
static bool
_equalAStar(A_Star *a, A_Star *b)
{
return true;
}
static bool static bool
_equalAIndices(A_Indices *a, A_Indices *b) _equalAIndices(A_Indices *a, A_Indices *b)
{ {
...@@ -2531,6 +2537,9 @@ equal(void *a, void *b) ...@@ -2531,6 +2537,9 @@ equal(void *a, void *b)
case T_FuncCall: case T_FuncCall:
retval = _equalFuncCall(a, b); retval = _equalFuncCall(a, b);
break; break;
case T_A_Star:
retval = _equalAStar(a, b);
break;
case T_A_Indices: case T_A_Indices:
retval = _equalAIndices(a, b); retval = _equalAIndices(a, b);
break; break;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 * NOTES
* Every node type that can appear in stored rules' parsetrees *must* * Every node type that can appear in stored rules' parsetrees *must*
...@@ -1989,6 +1989,12 @@ _outAConst(StringInfo str, A_Const *node) ...@@ -1989,6 +1989,12 @@ _outAConst(StringInfo str, A_Const *node)
WRITE_LOCATION_FIELD(location); WRITE_LOCATION_FIELD(location);
} }
static void
_outA_Star(StringInfo str, A_Star *node)
{
WRITE_NODE_TYPE("A_STAR");
}
static void static void
_outA_Indices(StringInfo str, A_Indices *node) _outA_Indices(StringInfo str, A_Indices *node)
{ {
...@@ -2467,6 +2473,9 @@ _outNode(StringInfo str, void *obj) ...@@ -2467,6 +2473,9 @@ _outNode(StringInfo str, void *obj)
case T_A_Const: case T_A_Const:
_outAConst(str, obj); _outAConst(str, obj);
break; break;
case T_A_Star:
_outA_Star(str, obj);
break;
case T_A_Indices: case T_A_Indices:
_outA_Indices(str, obj); _outA_Indices(str, obj);
break; break;
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* *
* *
* IDENTIFICATION * 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 * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -89,7 +89,7 @@ static bool QueryIsRule = FALSE; ...@@ -89,7 +89,7 @@ static bool QueryIsRule = FALSE;
*/ */
/*#define __YYSCLASS*/ /*#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 *makeTypeCast(Node *arg, TypeName *typename, int location);
static Node *makeStringConst(char *str, int location); static Node *makeStringConst(char *str, int location);
static Node *makeStringConstCast(char *str, int location, TypeName *typename); static Node *makeStringConstCast(char *str, int location, TypeName *typename);
...@@ -102,6 +102,7 @@ static Node *makeBoolAConst(bool state, int location); ...@@ -102,6 +102,7 @@ static Node *makeBoolAConst(bool state, int location);
static FuncCall *makeOverlaps(List *largs, List *rargs, int location); static FuncCall *makeOverlaps(List *largs, List *rargs, int location);
static void check_qualified_name(List *names); static void check_qualified_name(List *names);
static List *check_func_name(List *names); static List *check_func_name(List *names);
static List *check_indirection(List *indirection);
static List *extractArgTypes(List *parameters); static List *extractArgTypes(List *parameters);
static SelectStmt *findLeftmostSelect(SelectStmt *node); static SelectStmt *findLeftmostSelect(SelectStmt *node);
static void insertSelectOptions(SelectStmt *stmt, static void insertSelectOptions(SelectStmt *stmt,
...@@ -5144,9 +5145,7 @@ UnlistenStmt: ...@@ -5144,9 +5145,7 @@ UnlistenStmt:
| UNLISTEN '*' | UNLISTEN '*'
{ {
UnlistenStmt *n = makeNode(UnlistenStmt); UnlistenStmt *n = makeNode(UnlistenStmt);
n->relation = makeNode(RangeVar); n->relation = NULL;
n->relation->relname = "*";
n->relation->schemaname = NULL;
$$ = (Node *)n; $$ = (Node *)n;
} }
; ;
...@@ -5999,7 +5998,7 @@ insert_column_item: ...@@ -5999,7 +5998,7 @@ insert_column_item:
{ {
$$ = makeNode(ResTarget); $$ = makeNode(ResTarget);
$$->name = $1; $$->name = $1;
$$->indirection = $2; $$->indirection = check_indirection($2);
$$->val = NULL; $$->val = NULL;
$$->location = @1; $$->location = @1;
} }
...@@ -6138,7 +6137,7 @@ set_target: ...@@ -6138,7 +6137,7 @@ set_target:
{ {
$$ = makeNode(ResTarget); $$ = makeNode(ResTarget);
$$->name = $1; $$->name = $1;
$$->indirection = $2; $$->indirection = check_indirection($2);
$$->val = NULL; /* upper production sets this */ $$->val = NULL; /* upper production sets this */
$$->location = @1; $$->location = @1;
} }
...@@ -7842,7 +7841,7 @@ c_expr: columnref { $$ = $1; } ...@@ -7842,7 +7841,7 @@ c_expr: columnref { $$ = $1; }
{ {
A_Indirection *n = makeNode(A_Indirection); A_Indirection *n = makeNode(A_Indirection);
n->arg = (Node *) p; n->arg = (Node *) p;
n->indirection = $2; n->indirection = check_indirection($2);
$$ = (Node *) n; $$ = (Node *) n;
} }
else else
...@@ -7854,7 +7853,7 @@ c_expr: columnref { $$ = $1; } ...@@ -7854,7 +7853,7 @@ c_expr: columnref { $$ = $1; }
{ {
A_Indirection *n = makeNode(A_Indirection); A_Indirection *n = makeNode(A_Indirection);
n->arg = $2; n->arg = $2;
n->indirection = $4; n->indirection = check_indirection($4);
$$ = (Node *)n; $$ = (Node *)n;
} }
else else
...@@ -8409,7 +8408,7 @@ xml_attribute_el: a_expr AS ColLabel ...@@ -8409,7 +8408,7 @@ xml_attribute_el: a_expr AS ColLabel
{ {
$$ = makeNode(ResTarget); $$ = makeNode(ResTarget);
$$->name = $3; $$->name = $3;
$$->indirection = NULL; $$->indirection = NIL;
$$->val = (Node *) $1; $$->val = (Node *) $1;
$$->location = @1; $$->location = @1;
} }
...@@ -8417,7 +8416,7 @@ xml_attribute_el: a_expr AS ColLabel ...@@ -8417,7 +8416,7 @@ xml_attribute_el: a_expr AS ColLabel
{ {
$$ = makeNode(ResTarget); $$ = makeNode(ResTarget);
$$->name = NULL; $$->name = NULL;
$$->indirection = NULL; $$->indirection = NIL;
$$->val = (Node *) $1; $$->val = (Node *) $1;
$$->location = @1; $$->location = @1;
} }
...@@ -8724,7 +8723,7 @@ indirection_el: ...@@ -8724,7 +8723,7 @@ indirection_el:
} }
| '.' '*' | '.' '*'
{ {
$$ = (Node *) makeString("*"); $$ = (Node *) makeNode(A_Star);
} }
| '[' a_expr ']' | '[' a_expr ']'
{ {
...@@ -8833,7 +8832,7 @@ target_el: a_expr AS ColLabel ...@@ -8833,7 +8832,7 @@ target_el: a_expr AS ColLabel
| '*' | '*'
{ {
ColumnRef *n = makeNode(ColumnRef); ColumnRef *n = makeNode(ColumnRef);
n->fields = list_make1(makeString("*")); n->fields = list_make1(makeNode(A_Star));
n->location = @1; n->location = @1;
$$ = makeNode(ResTarget); $$ = makeNode(ResTarget);
...@@ -9511,7 +9510,7 @@ SpecialRuleRelation: ...@@ -9511,7 +9510,7 @@ SpecialRuleRelation:
%% %%
static Node * 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 * Generate a ColumnRef node, with an A_Indirection node added if there
...@@ -9533,23 +9532,30 @@ makeColumnRef(char *relname, List *indirection, int location) ...@@ -9533,23 +9532,30 @@ makeColumnRef(char *relname, List *indirection, int location)
if (nfields == 0) if (nfields == 0)
{ {
/* easy case - all indirection goes to A_Indirection */ /* easy case - all indirection goes to A_Indirection */
c->fields = list_make1(makeString(relname)); c->fields = list_make1(makeString(colname));
i->indirection = indirection; i->indirection = check_indirection(indirection);
} }
else else
{ {
/* got to split the list in two */ /* 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); indirection = list_truncate(indirection, nfields);
c->fields = lcons(makeString(relname), indirection); c->fields = lcons(makeString(colname), indirection);
} }
i->arg = (Node *) c; i->arg = (Node *) c;
return (Node *) i; 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++; nfields++;
} }
/* No subscripting, so all indirection gets added to field list */ /* 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; return (Node *) c;
} }
...@@ -9712,8 +9718,6 @@ check_qualified_name(List *names) ...@@ -9712,8 +9718,6 @@ check_qualified_name(List *names)
{ {
if (!IsA(lfirst(i), String)) if (!IsA(lfirst(i), String))
yyerror("syntax error"); yyerror("syntax error");
else if (strcmp(strVal(lfirst(i)), "*") == 0)
yyerror("syntax error");
} }
} }
...@@ -9731,12 +9735,31 @@ check_func_name(List *names) ...@@ -9731,12 +9735,31 @@ check_func_name(List *names)
{ {
if (!IsA(lfirst(i), String)) if (!IsA(lfirst(i), String))
yyerror("syntax error"); yyerror("syntax error");
else if (strcmp(strVal(lfirst(i)), "*") == 0)
yyerror("syntax error");
} }
return names; 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() /* extractArgTypes()
* Given a list of FunctionParameter nodes, extract a list of just the * Given a list of FunctionParameter nodes, extract a list of just the
* argument types (TypeNames) for input parameters only. This is what * argument types (TypeNames) for input parameters only. This is what
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -1181,7 +1181,8 @@ findTargetlistEntry(ParseState *pstate, Node *node, List **tlist, int clause)
*---------- *----------
*/ */
if (IsA(node, ColumnRef) && 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)); char *name = strVal(linitial(((ColumnRef *) node)->fields));
int location = ((ColumnRef *) node)->location; int location = ((ColumnRef *) node)->location;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -334,6 +334,13 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
if (IsA(n, A_Indices)) if (IsA(n, A_Indices))
subscripts = lappend(subscripts, n); 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 else
{ {
Assert(IsA(n, String)); Assert(IsA(n, String));
...@@ -403,10 +410,14 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) ...@@ -403,10 +410,14 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
{ {
case 1: 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 */ /* Try to identify as an unqualified column */
node = colNameToVar(pstate, name, false, cref->location); node = colNameToVar(pstate, name1, false, cref->location);
if (node == NULL) if (node == NULL)
{ {
...@@ -419,7 +430,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) ...@@ -419,7 +430,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
* have used VALUE as a column name in the past.) * have used VALUE as a column name in the past.)
*/ */
if (pstate->p_value_substitute != NULL && if (pstate->p_value_substitute != NULL &&
strcmp(name, "value") == 0) strcmp(name1, "value") == 0)
{ {
node = (Node *) copyObject(pstate->p_value_substitute); node = (Node *) copyObject(pstate->p_value_substitute);
...@@ -442,32 +453,40 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) ...@@ -442,32 +453,40 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
* PostQUEL-inspired syntax. The preferred form now is * PostQUEL-inspired syntax. The preferred form now is
* "rel.*". * "rel.*".
*/ */
if (refnameRangeTblEntry(pstate, NULL, name, if (refnameRangeTblEntry(pstate, NULL, name1,
&levels_up) != NULL) &levels_up) != NULL)
node = transformWholeRowRef(pstate, NULL, name, node = transformWholeRowRef(pstate, NULL, name1,
cref->location); cref->location);
else else
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN), (errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("column \"%s\" does not exist", errmsg("column \"%s\" does not exist",
name), name1),
parser_errposition(pstate, cref->location))); parser_errposition(pstate, cref->location)));
} }
break; break;
} }
case 2: case 2:
{ {
char *name1 = strVal(linitial(cref->fields)); Node *field1 = (Node *) linitial(cref->fields);
char *name2 = strVal(lsecond(cref->fields)); Node *field2 = (Node *) lsecond(cref->fields);
char *name1;
char *name2;
Assert(IsA(field1, String));
name1 = strVal(field1);
/* Whole-row reference? */ /* Whole-row reference? */
if (strcmp(name2, "*") == 0) if (IsA(field2, A_Star))
{ {
node = transformWholeRowRef(pstate, NULL, name1, node = transformWholeRowRef(pstate, NULL, name1,
cref->location); cref->location);
break; break;
} }
Assert(IsA(field2, String));
name2 = strVal(field2);
/* Try to identify as a once-qualified column */ /* Try to identify as a once-qualified column */
node = qualifiedNameToVar(pstate, NULL, name1, name2, true, node = qualifiedNameToVar(pstate, NULL, name1, name2, true,
cref->location); cref->location);
...@@ -490,18 +509,29 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) ...@@ -490,18 +509,29 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
} }
case 3: case 3:
{ {
char *name1 = strVal(linitial(cref->fields)); Node *field1 = (Node *) linitial(cref->fields);
char *name2 = strVal(lsecond(cref->fields)); Node *field2 = (Node *) lsecond(cref->fields);
char *name3 = strVal(lthird(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? */ /* Whole-row reference? */
if (strcmp(name3, "*") == 0) if (IsA(field3, A_Star))
{ {
node = transformWholeRowRef(pstate, name1, name2, node = transformWholeRowRef(pstate, name1, name2,
cref->location); cref->location);
break; break;
} }
Assert(IsA(field3, String));
name3 = strVal(field3);
/* Try to identify as a twice-qualified column */ /* Try to identify as a twice-qualified column */
node = qualifiedNameToVar(pstate, name1, name2, name3, true, node = qualifiedNameToVar(pstate, name1, name2, name3, true,
cref->location); cref->location);
...@@ -520,10 +550,21 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) ...@@ -520,10 +550,21 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
} }
case 4: case 4:
{ {
char *name1 = strVal(linitial(cref->fields)); Node *field1 = (Node *) linitial(cref->fields);
char *name2 = strVal(lsecond(cref->fields)); Node *field2 = (Node *) lsecond(cref->fields);
char *name3 = strVal(lthird(cref->fields)); Node *field3 = (Node *) lthird(cref->fields);
char *name4 = strVal(lfourth(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. * We check the catalog name and then ignore it.
...@@ -536,13 +577,16 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) ...@@ -536,13 +577,16 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
parser_errposition(pstate, cref->location))); parser_errposition(pstate, cref->location)));
/* Whole-row reference? */ /* Whole-row reference? */
if (strcmp(name4, "*") == 0) if (IsA(field4, A_Star))
{ {
node = transformWholeRowRef(pstate, name2, name3, node = transformWholeRowRef(pstate, name2, name3,
cref->location); cref->location);
break; break;
} }
Assert(IsA(field4, String));
name4 = strVal(field4);
/* Try to identify as a twice-qualified column */ /* Try to identify as a twice-qualified column */
node = qualifiedNameToVar(pstate, name2, name3, name4, true, node = qualifiedNameToVar(pstate, name2, name3, name4, true,
cref->location); cref->location);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -109,14 +109,14 @@ transformTargetList(ParseState *pstate, List *targetlist)
/* /*
* Check for "something.*". Depending on the complexity of the * 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. * 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;
if (strcmp(strVal(llast(cref->fields)), "*") == 0) if (IsA(llast(cref->fields), A_Star))
{ {
/* It is something.*, expand into multiple items */ /* It is something.*, expand into multiple items */
p_target = list_concat(p_target, p_target = list_concat(p_target,
...@@ -128,10 +128,8 @@ transformTargetList(ParseState *pstate, List *targetlist) ...@@ -128,10 +128,8 @@ transformTargetList(ParseState *pstate, List *targetlist)
else if (IsA(res->val, A_Indirection)) else if (IsA(res->val, A_Indirection))
{ {
A_Indirection *ind = (A_Indirection *) res->val; A_Indirection *ind = (A_Indirection *) res->val;
Node *lastitem = llast(ind->indirection);
if (IsA(lastitem, String) && if (IsA(llast(ind->indirection), A_Star))
strcmp(strVal(lastitem), "*") == 0)
{ {
/* It is something.*, expand into multiple items */ /* It is something.*, expand into multiple items */
p_target = list_concat(p_target, p_target = list_concat(p_target,
...@@ -176,14 +174,14 @@ transformExpressionList(ParseState *pstate, List *exprlist) ...@@ -176,14 +174,14 @@ transformExpressionList(ParseState *pstate, List *exprlist)
/* /*
* Check for "something.*". Depending on the complexity of the * 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. * or as the last indirection item in A_Indirection.
*/ */
if (IsA(e, ColumnRef)) if (IsA(e, ColumnRef))
{ {
ColumnRef *cref = (ColumnRef *) e; 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 */ /* It is something.*, expand into multiple items */
result = list_concat(result, result = list_concat(result,
...@@ -195,10 +193,8 @@ transformExpressionList(ParseState *pstate, List *exprlist) ...@@ -195,10 +193,8 @@ transformExpressionList(ParseState *pstate, List *exprlist)
else if (IsA(e, A_Indirection)) else if (IsA(e, A_Indirection))
{ {
A_Indirection *ind = (A_Indirection *) e; A_Indirection *ind = (A_Indirection *) e;
Node *lastitem = llast(ind->indirection);
if (IsA(lastitem, String) && if (IsA(llast(ind->indirection), A_Star))
strcmp(strVal(lastitem), "*") == 0)
{ {
/* It is something.*, expand into multiple items */ /* It is something.*, expand into multiple items */
result = list_concat(result, result = list_concat(result,
...@@ -560,6 +556,13 @@ transformAssignmentIndirection(ParseState *pstate, ...@@ -560,6 +556,13 @@ transformAssignmentIndirection(ParseState *pstate,
if (((A_Indices *) n)->lidx != NULL) if (((A_Indices *) n)->lidx != NULL)
isSlice = true; 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 else
{ {
FieldStore *fstore; FieldStore *fstore;
...@@ -809,7 +812,7 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos) ...@@ -809,7 +812,7 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos)
* ExpandColumnRefStar() * ExpandColumnRefStar()
* Transforms foo.* into a list of expressions or targetlist entries. * 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 * 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) * 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 * and foo.* in a ROW() or VALUES() construct (where we want just bare
...@@ -830,13 +833,9 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, ...@@ -830,13 +833,9 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref,
* (e.g., SELECT * FROM emp, dept) * (e.g., SELECT * FROM emp, dept)
* *
* Since the grammar only accepts bare '*' at top level of SELECT, we * Since the grammar only accepts bare '*' at top level of SELECT, we
* need not handle the targetlist==false case here. However, we must * need not handle the targetlist==false case here.
* test for it because the grammar currently fails to distinguish a
* quoted name "*" from a real asterisk.
*/ */
if (!targetlist) Assert(targetlist);
elog(ERROR, "invalid use of *");
return ExpandAllTables(pstate); return ExpandAllTables(pstate);
} }
else else
...@@ -1226,7 +1225,7 @@ FigureColnameInternal(Node *node, char **name) ...@@ -1226,7 +1225,7 @@ FigureColnameInternal(Node *node, char **name)
{ {
Node *i = lfirst(l); Node *i = lfirst(l);
if (strcmp(strVal(i), "*") != 0) if (IsA(i, String))
fname = strVal(i); fname = strVal(i);
} }
if (fname) if (fname)
...@@ -1242,13 +1241,12 @@ FigureColnameInternal(Node *node, char **name) ...@@ -1242,13 +1241,12 @@ FigureColnameInternal(Node *node, char **name)
char *fname = NULL; char *fname = NULL;
ListCell *l; ListCell *l;
/* find last field name, if any, ignoring "*" */ /* find last field name, if any, ignoring "*" and subscripts */
foreach(l, ind->indirection) foreach(l, ind->indirection)
{ {
Node *i = lfirst(l); Node *i = lfirst(l);
if (IsA(i, String) && if (IsA(i, String))
strcmp(strVal(i), "*") != 0)
fname = strVal(i); fname = strVal(i);
} }
if (fname) if (fname)
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -305,7 +305,8 @@ typenameTypeMod(ParseState *pstate, const TypeName *typename, Type typ)
{ {
ColumnRef *cr = (ColumnRef *) tm; 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)); cstr = strVal(linitial(cr->fields));
} }
if (!cstr) if (!cstr)
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -817,7 +817,10 @@ ProcessUtility(Node *parsetree,
{ {
UnlistenStmt *stmt = (UnlistenStmt *) parsetree; UnlistenStmt *stmt = (UnlistenStmt *) parsetree;
if (stmt->relation)
Async_Unlisten(stmt->relation->relname); Async_Unlisten(stmt->relation->relname);
else
Async_UnlistenAll();
} }
break; break;
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 ...@@ -324,6 +324,7 @@ typedef enum NodeTag
T_ParamRef, T_ParamRef,
T_A_Const, T_A_Const,
T_FuncCall, T_FuncCall,
T_A_Star,
T_A_Indices, T_A_Indices,
T_A_Indirection, T_A_Indirection,
T_A_ArrayExpr, T_A_ArrayExpr,
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 ...@@ -178,8 +178,10 @@ typedef struct TypeName
/* /*
* ColumnRef - specifies a reference to a column, or possibly a whole tuple * ColumnRef - specifies a reference to a column, or possibly a whole tuple
* *
* The "fields" list must be nonempty; its last component may be "*" * The "fields" list must be nonempty. It can contain string Value nodes
* instead of a regular field name. * (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 * Note: any array subscripting or selection of fields from composite columns
* is represented by an A_Indirection node above the ColumnRef. However, * is represented by an A_Indirection node above the ColumnRef. However,
...@@ -189,7 +191,7 @@ typedef struct TypeName ...@@ -189,7 +191,7 @@ typedef struct TypeName
typedef struct ColumnRef typedef struct ColumnRef
{ {
NodeTag type; 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 */ int location; /* token location, or -1 if unknown */
} ColumnRef; } ColumnRef;
...@@ -271,35 +273,46 @@ typedef struct FuncCall ...@@ -271,35 +273,46 @@ typedef struct FuncCall
} 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 typedef struct A_Indices
{ {
NodeTag type; NodeTag type;
Node *lidx; /* could be NULL */ Node *lidx; /* NULL if it's a single subscript */
Node *uidx; Node *uidx;
} A_Indices; } A_Indices;
/* /*
* A_Indirection - select a field and/or array element from an expression * A_Indirection - select a field and/or array element from an expression
* *
* The indirection list can contain both A_Indices nodes (representing * The indirection list can contain A_Indices nodes (representing
* subscripting) and string Value nodes (representing field selection * subscripting), string Value nodes (representing field selection --- the
* --- the string value is the name of the field to select). For example, * string value is the name of the field to select), and A_Star nodes
* a complex selection operation like * (representing selection of all fields of a composite type).
* For example, a complex selection operation like
* (foo).field1[42][7].field2 * (foo).field1[42][7].field2
* would be represented with a single A_Indirection node having a 4-element * would be represented with a single A_Indirection node having a 4-element
* indirection list. * indirection list.
* *
* Note: as of Postgres 8.0, we don't support arrays of composite values, * Currently, A_Star must appear only as the last list element --- the grammar
* so cases in which a field select follows a subscript aren't actually * is responsible for enforcing this!
* semantically legal. However the parser is prepared to handle such.
*/ */
typedef struct A_Indirection typedef struct A_Indirection
{ {
NodeTag type; NodeTag type;
Node *arg; /* the thing being selected from */ 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; } A_Indirection;
/* /*
...@@ -334,7 +347,7 @@ typedef struct ResTarget ...@@ -334,7 +347,7 @@ typedef struct ResTarget
{ {
NodeTag type; NodeTag type;
char *name; /* column name or NULL */ 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 */ Node *val; /* the value expression to compute or assign */
int location; /* token location, or -1 if unknown */ int location; /* token location, or -1 if unknown */
} ResTarget; } ResTarget;
...@@ -1739,7 +1752,7 @@ typedef struct NotifyStmt ...@@ -1739,7 +1752,7 @@ typedef struct NotifyStmt
typedef struct ListenStmt typedef struct ListenStmt
{ {
NodeTag type; NodeTag type;
RangeVar *relation; /* qualified name to listen on */ RangeVar *relation; /* name to listen on */
} ListenStmt; } ListenStmt;
/* ---------------------- /* ----------------------
...@@ -1749,7 +1762,7 @@ typedef struct ListenStmt ...@@ -1749,7 +1762,7 @@ typedef struct ListenStmt
typedef struct UnlistenStmt typedef struct UnlistenStmt
{ {
NodeTag type; NodeTag type;
RangeVar *relation; /* qualified name to unlisten on, or '*' */ RangeVar *relation; /* name to unlisten on, or NULL for all */
} UnlistenStmt; } UnlistenStmt;
/* ---------------------- /* ----------------------
......
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