diff options
Diffstat (limited to 'src/backend')
31 files changed, 921 insertions, 453 deletions
diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y index da76d76d9d2..8019b244ae8 100644 --- a/src/backend/bootstrap/bootparse.y +++ b/src/backend/bootstrap/bootparse.y @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/bootstrap/bootparse.y,v 1.92 2008/05/09 23:32:04 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/bootstrap/bootparse.y,v 1.93 2008/09/01 20:42:43 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -247,7 +247,7 @@ Boot_DeclareIndexStmt: { do_start(); - DefineIndex(makeRangeVar(NULL, LexIDStr($6)), + DefineIndex(makeRangeVar(NULL, LexIDStr($6), -1), LexIDStr($3), $4, LexIDStr($8), @@ -265,7 +265,7 @@ Boot_DeclareUniqueIndexStmt: { do_start(); - DefineIndex(makeRangeVar(NULL, LexIDStr($7)), + DefineIndex(makeRangeVar(NULL, LexIDStr($7), -1), LexIDStr($4), $5, LexIDStr($9), diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index 25eba1406fb..0689fb1f1aa 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/bootstrap/bootstrap.c,v 1.244 2008/06/24 17:58:27 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/bootstrap/bootstrap.c,v 1.245 2008/09/01 20:42:43 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -637,7 +637,7 @@ boot_openrel(char *relname) elog(DEBUG4, "open relation %s, attrsize %d", relname, (int) ATTRIBUTE_TUPLE_SIZE); - boot_reldesc = heap_openrv(makeRangeVar(NULL, relname), NoLock); + boot_reldesc = heap_openrv(makeRangeVar(NULL, relname, -1), NoLock); numattr = boot_reldesc->rd_rel->relnatts; for (i = 0; i < numattr; i++) { diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c index 7f0ea88c438..de827f74b2f 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.110 2008/08/30 01:39:13 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.111 2008/09/01 20:42:43 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -2156,7 +2156,7 @@ QualifiedNameGetCreationNamespace(List *names, char **objname_p) RangeVar * makeRangeVarFromNameList(List *names) { - RangeVar *rel = makeRangeVar(NULL, NULL); + RangeVar *rel = makeRangeVar(NULL, NULL, -1); switch (list_length(names)) { diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index 0171d1f7338..d4f04346c94 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/toasting.c,v 1.10 2008/05/09 23:32:04 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/toasting.c,v 1.11 2008/09/01 20:42:43 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -73,7 +73,7 @@ BootstrapToastTable(char *relName, Oid toastOid, Oid toastIndexOid) { Relation rel; - rel = heap_openrv(makeRangeVar(NULL, relName), AccessExclusiveLock); + rel = heap_openrv(makeRangeVar(NULL, relName, -1), AccessExclusiveLock); /* Note: during bootstrap may see uncataloged relation */ if (rel->rd_rel->relkind != RELKIND_RELATION && diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 6f2303f62ce..8b7b1015955 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.264 2008/08/28 23:09:45 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.265 2008/09/01 20:42:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -5112,7 +5112,8 @@ createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint, * Reconstruct a RangeVar for my relation (not passed in, unfortunately). */ myRel = makeRangeVar(get_namespace_name(RelationGetNamespace(rel)), - pstrdup(RelationGetRelationName(rel))); + pstrdup(RelationGetRelationName(rel)), + -1); /* Make changes-so-far visible */ CommandCounterIncrement(); diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 44f13df3614..3461056c576 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.236 2008/07/18 20:26:06 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.237 2008/09/01 20:42:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -655,7 +655,7 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid) else { /* Work around ancient pg_dump bug that omitted constrrel */ - fkcon->pktable = makeRangeVar(NULL, pk_table_name); + fkcon->pktable = makeRangeVar(NULL, pk_table_name, -1); } } else @@ -667,7 +667,7 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid) else { /* Work around ancient pg_dump bug that omitted constrrel */ - atstmt->relation = makeRangeVar(NULL, fk_table_name); + atstmt->relation = makeRangeVar(NULL, fk_table_name, -1); } } atstmt->cmds = list_make1(atcmd); diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 67a25500dac..6e2028142b9 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.403 2008/08/30 01:39:13 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.404 2008/09/01 20:42:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -722,6 +722,7 @@ _copyRangeVar(RangeVar *from) COPY_SCALAR_FIELD(inhOpt); COPY_SCALAR_FIELD(istemp); COPY_NODE_FIELD(alias); + COPY_LOCATION_FIELD(location); return newnode; } @@ -1773,10 +1774,11 @@ _copySortBy(SortBy *from) { SortBy *newnode = makeNode(SortBy); + COPY_NODE_FIELD(node); COPY_SCALAR_FIELD(sortby_dir); COPY_SCALAR_FIELD(sortby_nulls); COPY_NODE_FIELD(useOp); - COPY_NODE_FIELD(node); + COPY_LOCATION_FIELD(location); return newnode; } @@ -2406,7 +2408,7 @@ _copyNotifyStmt(NotifyStmt *from) { NotifyStmt *newnode = makeNode(NotifyStmt); - COPY_NODE_FIELD(relation); + COPY_STRING_FIELD(conditionname); return newnode; } @@ -2416,7 +2418,7 @@ _copyListenStmt(ListenStmt *from) { ListenStmt *newnode = makeNode(ListenStmt); - COPY_NODE_FIELD(relation); + COPY_STRING_FIELD(conditionname); return newnode; } @@ -2426,7 +2428,7 @@ _copyUnlistenStmt(UnlistenStmt *from) { UnlistenStmt *newnode = makeNode(UnlistenStmt); - COPY_NODE_FIELD(relation); + COPY_STRING_FIELD(conditionname); return newnode; } diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 298d03aa9ba..f0939b4777d 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.330 2008/08/30 01:39:13 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.331 2008/09/01 20:42:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -106,6 +106,7 @@ _equalRangeVar(RangeVar *a, RangeVar *b) COMPARE_SCALAR_FIELD(inhOpt); COMPARE_SCALAR_FIELD(istemp); COMPARE_NODE_FIELD(alias); + COMPARE_LOCATION_FIELD(location); return true; } @@ -1230,7 +1231,7 @@ _equalRuleStmt(RuleStmt *a, RuleStmt *b) static bool _equalNotifyStmt(NotifyStmt *a, NotifyStmt *b) { - COMPARE_NODE_FIELD(relation); + COMPARE_STRING_FIELD(conditionname); return true; } @@ -1238,7 +1239,7 @@ _equalNotifyStmt(NotifyStmt *a, NotifyStmt *b) static bool _equalListenStmt(ListenStmt *a, ListenStmt *b) { - COMPARE_NODE_FIELD(relation); + COMPARE_STRING_FIELD(conditionname); return true; } @@ -1246,7 +1247,7 @@ _equalListenStmt(ListenStmt *a, ListenStmt *b) static bool _equalUnlistenStmt(UnlistenStmt *a, UnlistenStmt *b) { - COMPARE_NODE_FIELD(relation); + COMPARE_STRING_FIELD(conditionname); return true; } @@ -1837,10 +1838,11 @@ _equalTypeCast(TypeCast *a, TypeCast *b) static bool _equalSortBy(SortBy *a, SortBy *b) { + COMPARE_NODE_FIELD(node); COMPARE_SCALAR_FIELD(sortby_dir); COMPARE_SCALAR_FIELD(sortby_nulls); COMPARE_NODE_FIELD(useOp); - COMPARE_NODE_FIELD(node); + COMPARE_LOCATION_FIELD(location); return true; } diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c index 5ab853c37bf..42539f6f97b 100644 --- a/src/backend/nodes/makefuncs.c +++ b/src/backend/nodes/makefuncs.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/makefuncs.c,v 1.59 2008/08/28 23:09:46 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/makefuncs.c,v 1.60 2008/09/01 20:42:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -266,7 +266,7 @@ makeRelabelType(Expr *arg, Oid rtype, int32 rtypmod, CoercionForm rformat) * creates a RangeVar node (rather oversimplified case) */ RangeVar * -makeRangeVar(char *schemaname, char *relname) +makeRangeVar(char *schemaname, char *relname, int location) { RangeVar *r = makeNode(RangeVar); @@ -276,6 +276,7 @@ makeRangeVar(char *schemaname, char *relname) r->inhOpt = INH_DEFAULT; r->istemp = false; r->alias = NULL; + r->location = location; return r; } diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c index fa1bb347b90..a0616415666 100644 --- a/src/backend/nodes/nodeFuncs.c +++ b/src/backend/nodes/nodeFuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/nodeFuncs.c,v 1.31 2008/08/28 23:09:46 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/nodeFuncs.c,v 1.32 2008/09/01 20:42:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -613,6 +613,9 @@ exprLocation(Node *expr) return -1; switch (nodeTag(expr)) { + case T_RangeVar: + loc = ((RangeVar *) expr)->location; + break; case T_Var: loc = ((Var *) expr)->location; break; @@ -789,6 +792,10 @@ exprLocation(Node *expr) /* just use argument's location */ loc = exprLocation((Node *) ((TargetEntry *) expr)->expr); break; + case T_IntoClause: + /* use the contained RangeVar's location --- close enough */ + loc = exprLocation((Node *) ((IntoClause *) expr)->rel); + break; case T_List: { /* report location of first list member that has a location */ @@ -852,6 +859,10 @@ exprLocation(Node *expr) loc = leftmostLoc(loc, tc->location); } break; + case T_SortBy: + /* just use argument's location (ignore operator, if any) */ + loc = exprLocation(((SortBy *) expr)->node); + break; case T_TypeName: loc = ((TypeName *) expr)->location; break; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 882dd0f9f14..15ced48445a 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.337 2008/08/30 01:39:14 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.338 2008/09/01 20:42:44 tgl Exp $ * * NOTES * Every node type that can appear in stored rules' parsetrees *must* @@ -667,6 +667,7 @@ _outRangeVar(StringInfo str, RangeVar *node) WRITE_ENUM_FIELD(inhOpt, InhOption); WRITE_BOOL_FIELD(istemp); WRITE_NODE_FIELD(alias); + WRITE_LOCATION_FIELD(location); } static void @@ -1609,7 +1610,7 @@ _outNotifyStmt(StringInfo str, NotifyStmt *node) { WRITE_NODE_TYPE("NOTIFY"); - WRITE_NODE_FIELD(relation); + WRITE_STRING_FIELD(conditionname); } static void @@ -2038,10 +2039,11 @@ _outSortBy(StringInfo str, SortBy *node) { WRITE_NODE_TYPE("SORTBY"); + WRITE_NODE_FIELD(node); WRITE_ENUM_FIELD(sortby_dir, SortByDir); WRITE_ENUM_FIELD(sortby_nulls, SortByNulls); WRITE_NODE_FIELD(useOp); - WRITE_NODE_FIELD(node); + WRITE_LOCATION_FIELD(location); } static void diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index ee363502f69..1eca85d0b60 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.213 2008/08/28 23:09:46 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.214 2008/09/01 20:42:44 tgl Exp $ * * NOTES * Path and Plan nodes do not have any readfuncs support, because we @@ -179,7 +179,7 @@ _readNotifyStmt(void) { READ_LOCALS(NotifyStmt); - READ_NODE_FIELD(relation); + READ_STRING_FIELD(conditionname); READ_DONE(); } @@ -278,6 +278,7 @@ _readRangeVar(void) READ_ENUM_FIELD(inhOpt, InhOption); READ_BOOL_FIELD(istemp); READ_NODE_FIELD(alias); + READ_LOCATION_FIELD(location); READ_DONE(); } diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index 8f025ac3a42..e6cd2f26b95 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/util/plancat.c,v 1.150 2008/08/25 22:42:33 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/util/plancat.c,v 1.151 2008/09/01 20:42:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -733,7 +733,7 @@ build_physical_tlist(PlannerInfo *root, RelOptInfo *rel) break; case RTE_FUNCTION: - expandRTE(rte, varno, 0, true /* include dropped */ , + expandRTE(rte, varno, 0, -1, true /* include dropped */ , NULL, &colvars); foreach(l, colvars) { @@ -758,7 +758,7 @@ build_physical_tlist(PlannerInfo *root, RelOptInfo *rel) break; case RTE_VALUES: - expandRTE(rte, varno, 0, false /* dropped not applicable */ , + expandRTE(rte, varno, 0, -1, false /* dropped not applicable */ , NULL, &colvars); foreach(l, colvars) { diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c index e9fd15e6f3f..748a3cb93ac 100644 --- a/src/backend/optimizer/util/var.c +++ b/src/backend/optimizer/util/var.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/util/var.c,v 1.78 2008/08/28 23:09:46 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/util/var.c,v 1.79 2008/09/01 20:42:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -30,10 +30,16 @@ typedef struct typedef struct { - int varno; - int varattno; + int var_location; int sublevels_up; -} contain_var_reference_context; +} locate_var_of_level_context; + +typedef struct +{ + int var_location; + int relid; + int sublevels_up; +} locate_var_of_relation_context; typedef struct { @@ -56,11 +62,12 @@ typedef struct static bool pull_varnos_walker(Node *node, pull_varnos_context *context); static bool pull_varattnos_walker(Node *node, Bitmapset **varattnos); -static bool contain_var_reference_walker(Node *node, - contain_var_reference_context *context); static bool contain_var_clause_walker(Node *node, void *context); static bool contain_vars_of_level_walker(Node *node, int *sublevels_up); -static bool contain_vars_above_level_walker(Node *node, int *sublevels_up); +static bool locate_var_of_level_walker(Node *node, + locate_var_of_level_context *context); +static bool locate_var_of_relation_walker(Node *node, + locate_var_of_relation_context *context); static bool find_minimum_var_level_walker(Node *node, find_minimum_var_level_context *context); static bool pull_var_clause_walker(Node *node, @@ -136,6 +143,7 @@ pull_varnos_walker(Node *node, pull_varnos_context *context) (void *) context); } + /* * pull_varattnos * Find all the distinct attribute numbers present in an expression tree, @@ -179,69 +187,6 @@ pull_varattnos_walker(Node *node, Bitmapset **varattnos) /* - * contain_var_reference - * - * Detect whether a parsetree contains any references to a specified - * attribute of a specified rtable entry. - * - * NOTE: this is used on not-yet-planned expressions. It may therefore find - * bare SubLinks, and if so it needs to recurse into them to look for uplevel - * references to the desired rtable entry! But when we find a completed - * SubPlan, we only need to look at the parameters passed to the subplan. - */ -bool -contain_var_reference(Node *node, int varno, int varattno, int levelsup) -{ - contain_var_reference_context context; - - context.varno = varno; - context.varattno = varattno; - context.sublevels_up = levelsup; - - /* - * Must be prepared to start with a Query or a bare expression tree; if - * it's a Query, we don't want to increment sublevels_up. - */ - return query_or_expression_tree_walker(node, - contain_var_reference_walker, - (void *) &context, - 0); -} - -static bool -contain_var_reference_walker(Node *node, - contain_var_reference_context *context) -{ - if (node == NULL) - return false; - if (IsA(node, Var)) - { - Var *var = (Var *) node; - - if (var->varno == context->varno && - var->varattno == context->varattno && - var->varlevelsup == context->sublevels_up) - return true; - return false; - } - if (IsA(node, Query)) - { - /* Recurse into RTE subquery or not-yet-planned sublink subquery */ - bool result; - - context->sublevels_up++; - result = query_tree_walker((Query *) node, - contain_var_reference_walker, - (void *) context, 0); - context->sublevels_up--; - return result; - } - return expression_tree_walker(node, contain_var_reference_walker, - (void *) context); -} - - -/* * contain_var_clause * Recursively scan a clause to discover whether it contains any Var nodes * (of the current query level). @@ -273,6 +218,7 @@ contain_var_clause_walker(Node *node, void *context) return expression_tree_walker(node, contain_var_clause_walker, context); } + /* * contain_vars_of_level * Recursively scan a clause to discover whether it contains any Var nodes @@ -328,53 +274,146 @@ contain_vars_of_level_walker(Node *node, int *sublevels_up) (void *) sublevels_up); } + /* - * contain_vars_above_level - * Recursively scan a clause to discover whether it contains any Var nodes - * above the specified query level. (For example, pass zero to detect - * all nonlocal Vars.) + * locate_var_of_level + * Find the parse location of any Var of the specified query level. * - * Returns true if any such Var found. + * Returns -1 if no such Var is in the querytree, or if they all have + * unknown parse location. (The former case is probably caller error, + * but we don't bother to distinguish it from the latter case.) * * Will recurse into sublinks. Also, may be invoked directly on a Query. + * + * Note: it might seem appropriate to merge this functionality into + * contain_vars_of_level, but that would complicate that function's API. + * Currently, the only uses of this function are for error reporting, + * and so shaving cycles probably isn't very important. */ -bool -contain_vars_above_level(Node *node, int levelsup) +int +locate_var_of_level(Node *node, int levelsup) { - int sublevels_up = levelsup; + locate_var_of_level_context context; - return query_or_expression_tree_walker(node, - contain_vars_above_level_walker, - (void *) &sublevels_up, + context.var_location = -1; /* in case we find nothing */ + context.sublevels_up = levelsup; + + (void) query_or_expression_tree_walker(node, + locate_var_of_level_walker, + (void *) &context, 0); + + return context.var_location; } static bool -contain_vars_above_level_walker(Node *node, int *sublevels_up) +locate_var_of_level_walker(Node *node, + locate_var_of_level_context *context) { if (node == NULL) return false; if (IsA(node, Var)) { - if (((Var *) node)->varlevelsup > *sublevels_up) + Var *var = (Var *) node; + + if (var->varlevelsup == context->sublevels_up && + var->location >= 0) + { + context->var_location = var->location; return true; /* abort tree traversal and return true */ + } + return false; + } + if (IsA(node, CurrentOfExpr)) + { + /* since CurrentOfExpr doesn't carry location, nothing we can do */ + return false; } if (IsA(node, Query)) { /* Recurse into subselects */ bool result; - (*sublevels_up)++; + context->sublevels_up++; result = query_tree_walker((Query *) node, - contain_vars_above_level_walker, - (void *) sublevels_up, + locate_var_of_level_walker, + (void *) context, 0); - (*sublevels_up)--; + context->sublevels_up--; return result; } return expression_tree_walker(node, - contain_vars_above_level_walker, - (void *) sublevels_up); + locate_var_of_level_walker, + (void *) context); +} + + +/* + * locate_var_of_relation + * Find the parse location of any Var of the specified relation. + * + * Returns -1 if no such Var is in the querytree, or if they all have + * unknown parse location. + * + * Will recurse into sublinks. Also, may be invoked directly on a Query. + */ +int +locate_var_of_relation(Node *node, int relid, int levelsup) +{ + locate_var_of_relation_context context; + + context.var_location = -1; /* in case we find nothing */ + context.relid = relid; + context.sublevels_up = levelsup; + + (void) query_or_expression_tree_walker(node, + locate_var_of_relation_walker, + (void *) &context, + 0); + + return context.var_location; +} + +static bool +locate_var_of_relation_walker(Node *node, + locate_var_of_relation_context *context) +{ + if (node == NULL) + return false; + if (IsA(node, Var)) + { + Var *var = (Var *) node; + + if (var->varno == context->relid && + var->varlevelsup == context->sublevels_up && + var->location >= 0) + { + context->var_location = var->location; + return true; /* abort tree traversal and return true */ + } + return false; + } + if (IsA(node, CurrentOfExpr)) + { + /* since CurrentOfExpr doesn't carry location, nothing we can do */ + return false; + } + if (IsA(node, Query)) + { + /* Recurse into subselects */ + bool result; + + context->sublevels_up++; + result = query_tree_walker((Query *) node, + locate_var_of_relation_walker, + (void *) context, + 0); + context->sublevels_up--; + return result; + } + return expression_tree_walker(node, + locate_var_of_relation_walker, + (void *) context); } diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index b513d61c071..18585b860b4 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -17,7 +17,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.378 2008/08/28 23:09:47 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.379 2008/09/01 20:42:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -36,16 +36,10 @@ #include "parser/parse_relation.h" #include "parser/parse_target.h" #include "parser/parsetree.h" +#include "rewrite/rewriteManip.h" #include "utils/rel.h" -typedef struct -{ - Oid *paramTypes; - int numParams; -} check_parameter_resolution_context; - - static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt); static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt); static List *transformInsertRow(ParseState *pstate, List *exprlist, @@ -62,9 +56,9 @@ static Query *transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt); static Query *transformExplainStmt(ParseState *pstate, ExplainStmt *stmt); -static void transformLockingClause(Query *qry, LockingClause *lc); -static bool check_parameter_resolution_walker(Node *node, - check_parameter_resolution_context *context); +static void transformLockingClause(ParseState *pstate, + Query *qry, LockingClause *lc); +static bool check_parameter_resolution_walker(Node *node, ParseState *pstate); /* @@ -122,21 +116,15 @@ parse_analyze_varparams(Node *parseTree, const char *sourceText, query = transformStmt(pstate, parseTree); + /* make sure all is well with parameter types */ + if (pstate->p_numparams > 0) + check_parameter_resolution_walker((Node *) query, pstate); + *paramTypes = pstate->p_paramtypes; *numParams = pstate->p_numparams; free_parsestate(pstate); - /* make sure all is well with parameter types */ - if (*numParams > 0) - { - check_parameter_resolution_context context; - - context.paramTypes = *paramTypes; - context.numParams = *numParams; - check_parameter_resolution_walker((Node *) query, &context); - } - return query; } @@ -383,13 +371,16 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) free_parsestate(sub_pstate); /* The grammar should have produced a SELECT, but it might have INTO */ - Assert(IsA(selectQuery, Query)); - Assert(selectQuery->commandType == CMD_SELECT); - Assert(selectQuery->utilityStmt == NULL); + if (!IsA(selectQuery, Query) || + selectQuery->commandType != CMD_SELECT || + selectQuery->utilityStmt != NULL) + elog(ERROR, "unexpected non-SELECT command in INSERT ... SELECT"); if (selectQuery->intoClause) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("INSERT ... SELECT cannot specify INTO"))); + errmsg("INSERT ... SELECT cannot specify INTO"), + parser_errposition(pstate, + exprLocation((Node *) selectQuery->intoClause)))); /* * Make the source be a subquery in the INSERT's rangetable, and add @@ -477,7 +468,9 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("VALUES lists must all be the same length"))); + errmsg("VALUES lists must all be the same length"), + parser_errposition(pstate, + exprLocation((Node *) sublist)))); } /* Prepare row for assignment to target table */ @@ -496,7 +489,9 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) if (pstate->p_joinlist != NIL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("VALUES must not contain table references"))); + errmsg("VALUES must not contain table references"), + parser_errposition(pstate, + locate_var_of_level((Node *) exprsLists, 0)))); /* * Another thing we can't currently support is NEW/OLD references in @@ -509,7 +504,9 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("VALUES must not contain OLD or NEW references"), - errhint("Use SELECT ... UNION ALL ... instead."))); + errhint("Use SELECT ... UNION ALL ... instead."), + parser_errposition(pstate, + locate_var_of_level((Node *) exprsLists, 0)))); /* * Generate the VALUES RTE @@ -524,7 +521,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) /* * Generate list of Vars referencing the RTE */ - expandRTE(rte, rtr->rtindex, 0, false, NULL, &exprList); + expandRTE(rte, rtr->rtindex, 0, -1, false, NULL, &exprList); } else { @@ -603,7 +600,9 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) if (pstate->p_hasAggs) ereport(ERROR, (errcode(ERRCODE_GROUPING_ERROR), - errmsg("cannot use aggregate function in VALUES"))); + errmsg("cannot use aggregate function in VALUES"), + parser_errposition(pstate, + locate_agg_of_level((Node *) qry, 0)))); return qry; } @@ -633,12 +632,18 @@ transformInsertRow(ParseState *pstate, List *exprlist, if (list_length(exprlist) > list_length(icolumns)) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("INSERT has more expressions than target columns"))); + errmsg("INSERT has more expressions than target columns"), + parser_errposition(pstate, + exprLocation(list_nth(exprlist, + list_length(icolumns)))))); if (stmtcols != NIL && list_length(exprlist) < list_length(icolumns)) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("INSERT has more target columns than expressions"))); + errmsg("INSERT has more target columns than expressions"), + parser_errposition(pstate, + exprLocation(list_nth(icolumns, + list_length(exprlist)))))); /* * Prepare columns for assignment to target table. @@ -770,7 +775,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) foreach(l, stmt->lockingClause) { - transformLockingClause(qry, (LockingClause *) lfirst(l)); + transformLockingClause(pstate, qry, (LockingClause *) lfirst(l)); } return qry; @@ -838,7 +843,9 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("VALUES lists must all be the same length"))); + errmsg("VALUES lists must all be the same length"), + parser_errposition(pstate, + exprLocation((Node *) sublist)))); } exprsLists = lappend(exprsLists, sublist); @@ -902,7 +909,7 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) * Generate a targetlist as though expanding "*" */ Assert(pstate->p_next_resno == 1); - qry->targetList = expandRelAttrs(pstate, rte, rtr->rtindex, 0); + qry->targetList = expandRelAttrs(pstate, rte, rtr->rtindex, 0, -1); /* * The grammar allows attaching ORDER BY, LIMIT, and FOR UPDATE to a @@ -940,7 +947,9 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) if (list_length(pstate->p_joinlist) != 1) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("VALUES must not contain table references"))); + errmsg("VALUES must not contain table references"), + parser_errposition(pstate, + locate_var_of_level((Node *) newExprsLists, 0)))); /* * Another thing we can't currently support is NEW/OLD references in rules @@ -953,7 +962,9 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("VALUES must not contain OLD or NEW references"), - errhint("Use SELECT ... UNION ALL ... instead."))); + errhint("Use SELECT ... UNION ALL ... instead."), + parser_errposition(pstate, + locate_var_of_level((Node *) newExprsLists, 0)))); qry->rtable = pstate->p_rtable; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); @@ -963,7 +974,9 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) if (pstate->p_hasAggs) ereport(ERROR, (errcode(ERRCODE_GROUPING_ERROR), - errmsg("cannot use aggregate function in VALUES"))); + errmsg("cannot use aggregate function in VALUES"), + parser_errposition(pstate, + locate_agg_of_level((Node *) newExprsLists, 0)))); return qry; } @@ -1155,7 +1168,9 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("invalid UNION/INTERSECT/EXCEPT ORDER BY clause"), errdetail("Only result column names can be used, not expressions or functions."), - errhint("Add the expression/function to every SELECT, or move the UNION into a FROM clause."))); + errhint("Add the expression/function to every SELECT, or move the UNION into a FROM clause."), + parser_errposition(pstate, + exprLocation(list_nth(qry->targetList, tllen))))); qry->limitOffset = transformLimitClause(pstate, limitOffset, "OFFSET"); @@ -1185,7 +1200,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) foreach(l, lockingClause) { - transformLockingClause(qry, (LockingClause *) lfirst(l)); + transformLockingClause(pstate, qry, (LockingClause *) lfirst(l)); } return qry; @@ -1198,9 +1213,9 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) * In addition to returning the transformed node, we return a list of * expression nodes showing the type, typmod, and location (for error messages) * of each output column of the set-op node. This is used only during the - * internal recursion of this function. We use SetToDefault nodes for - * this purpose, since they carry exactly the fields needed, but any other - * expression node type would do as well. + * internal recursion of this function. At the upper levels we use + * SetToDefault nodes for this purpose, since they carry exactly the fields + * needed, but any other expression node type would do as well. */ static Node * transformSetOperationTree(ParseState *pstate, SelectStmt *stmt, @@ -1216,7 +1231,10 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt, if (stmt->intoClause) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("INTO is only allowed on first SELECT of UNION/INTERSECT/EXCEPT"))); + errmsg("INTO is only allowed on first SELECT of UNION/INTERSECT/EXCEPT"), + parser_errposition(pstate, + exprLocation((Node *) stmt->intoClause)))); + /* We don't support FOR UPDATE/SHARE with set ops at the moment. */ if (stmt->lockingClause) ereport(ERROR, @@ -1273,25 +1291,21 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt, if (contain_vars_of_level((Node *) selectQuery, 1)) ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), - errmsg("UNION/INTERSECT/EXCEPT member statement cannot refer to other relations of same query level"))); + errmsg("UNION/INTERSECT/EXCEPT member statement cannot refer to other relations of same query level"), + parser_errposition(pstate, + locate_var_of_level((Node *) selectQuery, 1)))); } /* - * Extract information about the result columns. + * Extract a list of the result expressions for upper-level checking. */ *colInfo = NIL; foreach(tl, selectQuery->targetList) { TargetEntry *tle = (TargetEntry *) lfirst(tl); - SetToDefault *cinfo; - if (tle->resjunk) - continue; - cinfo = makeNode(SetToDefault); - cinfo->typeId = exprType((Node *) tle->expr); - cinfo->typeMod = exprTypmod((Node *) tle->expr); - cinfo->location = exprLocation((Node *) tle->expr); - *colInfo = lappend(*colInfo, cinfo); + if (!tle->resjunk) + *colInfo = lappend(*colInfo, tle->expr); } /* @@ -1356,12 +1370,12 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt, op->groupClauses = NIL; forboth(lci, lcolinfo, rci, rcolinfo) { - SetToDefault *lcolinfo = (SetToDefault *) lfirst(lci); - SetToDefault *rcolinfo = (SetToDefault *) lfirst(rci); - Oid lcoltype = lcolinfo->typeId; - Oid rcoltype = rcolinfo->typeId; - int32 lcoltypmod = lcolinfo->typeMod; - int32 rcoltypmod = rcolinfo->typeMod; + Node *lcolinfo = (Node *) lfirst(lci); + Node *rcolinfo = (Node *) lfirst(rci); + Oid lcoltype = exprType(lcolinfo); + Oid rcoltype = exprType(rcolinfo); + int32 lcoltypmod = exprTypmod(lcolinfo); + int32 rcoltypmod = exprTypmod(rcolinfo); Node *bestexpr; SetToDefault *rescolinfo; Oid rescoltype; @@ -1379,18 +1393,16 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt, rescoltypmod = -1; /* verify the coercions are actually possible */ - if (lcoltype != UNKNOWNOID) - (void) coerce_to_common_type(pstate, (Node *) lcolinfo, - rescoltype, context); - if (rcoltype != UNKNOWNOID) - (void) coerce_to_common_type(pstate, (Node *) rcolinfo, - rescoltype, context); + (void) coerce_to_common_type(pstate, lcolinfo, + rescoltype, context); + (void) coerce_to_common_type(pstate, rcolinfo, + rescoltype, context); /* emit results */ rescolinfo = makeNode(SetToDefault); rescolinfo->typeId = rescoltype; rescolinfo->typeMod = rescoltypmod; - rescolinfo->location = ((SetToDefault *) bestexpr)->location; + rescolinfo->location = exprLocation(bestexpr); *colInfo = lappend(*colInfo, rescolinfo); op->colTypes = lappend_oid(op->colTypes, rescoltype); @@ -1406,12 +1418,18 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt, SortGroupClause *grpcl = makeNode(SortGroupClause); Oid sortop; Oid eqop; + ParseCallbackState pcbstate; + + setup_parser_errposition_callback(&pcbstate, pstate, + rescolinfo->location); /* determine the eqop and optional sortop */ get_sort_group_operators(rescoltype, false, true, false, &sortop, &eqop, NULL); + cancel_parser_errposition_callback(&pcbstate); + /* we don't have a tlist yet, so can't assign sortgrouprefs */ grpcl->tleSortGroupRef = 0; grpcl->eqop = eqop; @@ -1510,7 +1528,9 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) if (pstate->p_hasAggs) ereport(ERROR, (errcode(ERRCODE_GROUPING_ERROR), - errmsg("cannot use aggregate function in UPDATE"))); + errmsg("cannot use aggregate function in UPDATE"), + parser_errposition(pstate, + locate_agg_of_level((Node *) qry, 0)))); /* * Now we are done with SELECT-like processing, and can get on with @@ -1607,13 +1627,28 @@ transformReturningList(ParseState *pstate, List *returningList) if (pstate->p_hasAggs) ereport(ERROR, (errcode(ERRCODE_GROUPING_ERROR), - errmsg("cannot use aggregate function in RETURNING"))); + errmsg("cannot use aggregate function in RETURNING"), + parser_errposition(pstate, + locate_agg_of_level((Node *) rlist, 0)))); /* no new relation references please */ if (list_length(pstate->p_rtable) != length_rtable) + { + int vlocation = -1; + int relid; + + /* try to locate such a reference to point to */ + for (relid = length_rtable + 1; relid <= list_length(pstate->p_rtable); relid++) + { + vlocation = locate_var_of_relation((Node *) rlist, relid, 0); + if (vlocation >= 0) + break; + } ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("RETURNING cannot contain references to other relations"))); + errmsg("RETURNING cannot contain references to other relations"), + parser_errposition(pstate, vlocation))); + } /* mark column origins */ markTargetListOrigins(pstate, rlist); @@ -1653,16 +1688,19 @@ transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt) result = transformStmt(pstate, stmt->query); + /* Grammar should not have allowed anything but SELECT */ if (!IsA(result, Query) || result->commandType != CMD_SELECT || result->utilityStmt != NULL) - elog(ERROR, "unexpected non-SELECT command in cursor statement"); + elog(ERROR, "unexpected non-SELECT command in DECLARE CURSOR"); /* But we must explicitly disallow DECLARE CURSOR ... SELECT INTO */ if (result->intoClause) ereport(ERROR, (errcode(ERRCODE_INVALID_CURSOR_DEFINITION), - errmsg("DECLARE CURSOR cannot specify INTO"))); + errmsg("DECLARE CURSOR cannot specify INTO"), + parser_errposition(pstate, + exprLocation((Node *) result->intoClause)))); /* FOR UPDATE and WITH HOLD are not compatible */ if (result->rowMarks != NIL && (stmt->options & CURSOR_OPT_HOLD)) @@ -1761,10 +1799,10 @@ CheckSelectLocking(Query *qry) * This basically involves replacing names by integer relids. * * NB: if you need to change this, see also markQueryForLocking() - * in rewriteHandler.c. + * in rewriteHandler.c, and isLockedRel() in parse_relation.c. */ static void -transformLockingClause(Query *qry, LockingClause *lc) +transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc) { List *lockedRels = lc->lockedRels; ListCell *l; @@ -1801,7 +1839,7 @@ transformLockingClause(Query *qry, LockingClause *lc) * FOR UPDATE/SHARE of subquery is propagated to all of * subquery's rels */ - transformLockingClause(rte->subquery, allrels); + transformLockingClause(pstate, rte->subquery, allrels); break; default: /* ignore JOIN, SPECIAL, FUNCTION RTEs */ @@ -1814,7 +1852,14 @@ transformLockingClause(Query *qry, LockingClause *lc) /* just the named tables */ foreach(l, lockedRels) { - char *relname = strVal(lfirst(l)); + RangeVar *thisrel = (RangeVar *) lfirst(l); + + /* For simplicity we insist on unqualified alias names here */ + if (thisrel->catalogname || thisrel->schemaname) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("SELECT FOR UPDATE/SHARE must specify unqualified relation names"), + parser_errposition(pstate, thisrel->location))); i = 0; foreach(rt, qry->rtable) @@ -1822,7 +1867,7 @@ transformLockingClause(Query *qry, LockingClause *lc) RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt); ++i; - if (strcmp(rte->eref->aliasname, relname) == 0) + if (strcmp(rte->eref->aliasname, thisrel->relname) == 0) { switch (rte->rtekind) { @@ -1837,27 +1882,31 @@ transformLockingClause(Query *qry, LockingClause *lc) * FOR UPDATE/SHARE of subquery is propagated to * all of subquery's rels */ - transformLockingClause(rte->subquery, allrels); + transformLockingClause(pstate, rte->subquery, allrels); break; case RTE_JOIN: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SELECT FOR UPDATE/SHARE cannot be applied to a join"))); + errmsg("SELECT FOR UPDATE/SHARE cannot be applied to a join"), + parser_errposition(pstate, thisrel->location))); break; case RTE_SPECIAL: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SELECT FOR UPDATE/SHARE cannot be applied to NEW or OLD"))); + errmsg("SELECT FOR UPDATE/SHARE cannot be applied to NEW or OLD"), + parser_errposition(pstate, thisrel->location))); break; case RTE_FUNCTION: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SELECT FOR UPDATE/SHARE cannot be applied to a function"))); + errmsg("SELECT FOR UPDATE/SHARE cannot be applied to a function"), + parser_errposition(pstate, thisrel->location))); break; case RTE_VALUES: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SELECT FOR UPDATE/SHARE cannot be applied to VALUES"))); + errmsg("SELECT FOR UPDATE/SHARE cannot be applied to VALUES"), + parser_errposition(pstate, thisrel->location))); break; default: elog(ERROR, "unrecognized RTE type: %d", @@ -1871,7 +1920,8 @@ transformLockingClause(Query *qry, LockingClause *lc) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_TABLE), errmsg("relation \"%s\" in FOR UPDATE/SHARE clause not found in FROM clause", - relname))); + thisrel->relname), + parser_errposition(pstate, thisrel->location))); } } } @@ -1919,8 +1969,7 @@ applyLockingClause(Query *qry, Index rtindex, bool forUpdate, bool noWait) * and yet other instances seen later might have gotten coerced. */ static bool -check_parameter_resolution_walker(Node *node, - check_parameter_resolution_context *context) +check_parameter_resolution_walker(Node *node, ParseState *pstate) { if (node == NULL) return false; @@ -1933,16 +1982,18 @@ check_parameter_resolution_walker(Node *node, int paramno = param->paramid; if (paramno <= 0 || /* shouldn't happen, but... */ - paramno > context->numParams) + paramno > pstate->p_numparams) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_PARAMETER), - errmsg("there is no parameter $%d", paramno))); + errmsg("there is no parameter $%d", paramno), + parser_errposition(pstate, param->location))); - if (param->paramtype != context->paramTypes[paramno - 1]) + if (param->paramtype != pstate->p_paramtypes[paramno - 1]) ereport(ERROR, (errcode(ERRCODE_AMBIGUOUS_PARAMETER), errmsg("could not determine data type of parameter $%d", - paramno))); + paramno), + parser_errposition(pstate, param->location))); } return false; } @@ -1951,8 +2002,8 @@ check_parameter_resolution_walker(Node *node, /* Recurse into RTE subquery or not-yet-planned sublink subquery */ return query_tree_walker((Query *) node, check_parameter_resolution_walker, - (void *) context, 0); + (void *) pstate, 0); } return expression_tree_walker(node, check_parameter_resolution_walker, - (void *) context); + (void *) pstate); } diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 78e81fbf562..26a3198d37b 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.620 2008/08/30 01:39:14 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.621 2008/09/01 20:42:44 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -55,6 +55,7 @@ #include "catalog/namespace.h" #include "commands/defrem.h" #include "nodes/makefuncs.h" +#include "nodes/nodeFuncs.h" #include "parser/gramparse.h" #include "storage/lmgr.h" #include "utils/date.h" @@ -1215,7 +1216,8 @@ zone_value: if (($3 & ~(INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE))) != 0) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("time zone interval must be HOUR or HOUR TO MINUTE"))); + errmsg("time zone interval must be HOUR or HOUR TO MINUTE"), + scanner_errposition(@3))); t->typmods = list_make1(makeIntConst($3, @3)); } $$ = makeStringConstCast($2, @2, t); @@ -1227,7 +1229,8 @@ zone_value: && (($6 & ~(INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE))) != 0)) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("time zone interval must be HOUR or HOUR TO MINUTE"))); + errmsg("time zone interval must be HOUR or HOUR TO MINUTE"), + scanner_errposition(@6))); t->typmods = list_make2(makeIntConst($6, @6), makeIntConst($3, @3)); $$ = makeStringConstCast($5, @5, t); @@ -2286,7 +2289,8 @@ key_match: MATCH FULL { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("MATCH PARTIAL not yet implemented"))); + errmsg("MATCH PARTIAL not yet implemented"), + scanner_errposition(@1))); $$ = FKCONSTR_MATCH_PARTIAL; } | MATCH SIMPLE @@ -2378,7 +2382,8 @@ CreateAsStmt: if (n->intoClause != NULL) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("CREATE TABLE AS cannot specify INTO"))); + errmsg("CREATE TABLE AS cannot specify INTO"), + scanner_errposition(exprLocation((Node *) n->intoClause)))); $4->rel->istemp = $2; n->intoClause = $4; $$ = $6; @@ -2799,7 +2804,8 @@ ConstraintAttributeSpec: if ($1 == 0 && $2 != 0) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("constraint declared INITIALLY DEFERRED must be DEFERRABLE"))); + errmsg("constraint declared INITIALLY DEFERRED must be DEFERRABLE"), + scanner_errposition(@1))); $$ = $1 | $2; } | ConstraintTimeSpec @@ -2814,7 +2820,8 @@ ConstraintAttributeSpec: if ($2 == 0 && $1 != 0) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("constraint declared INITIALLY DEFERRED must be DEFERRABLE"))); + errmsg("constraint declared INITIALLY DEFERRED must be DEFERRABLE"), + scanner_errposition(@1))); $$ = $1 | $2; } | /*EMPTY*/ @@ -2986,9 +2993,11 @@ DefineStmt: ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("improper qualified name (too many dotted names): %s", - NameListToString($3)))); + NameListToString($3)), + scanner_errposition(@3))); break; } + r->location = @3; n->typevar = r; n->coldeflist = $6; $$ = (Node *)n; @@ -3128,12 +3137,12 @@ opclass_item: n->number = $2; $$ = (Node *) n; } - | OPERATOR Iconst any_operator '(' oper_argtypes ')' opt_recheck + | OPERATOR Iconst any_operator oper_argtypes opt_recheck { CreateOpClassItem *n = makeNode(CreateOpClassItem); n->itemtype = OPCLASS_ITEM_OPERATOR; n->name = $3; - n->args = $5; + n->args = $4; n->number = $2; $$ = (Node *) n; } @@ -3178,7 +3187,8 @@ opt_recheck: RECHECK ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("RECHECK is no longer supported"), - errhint("Update your data type."))); + errhint("Update your data type."), + scanner_errposition(@1))); $$ = TRUE; } | /*EMPTY*/ { $$ = FALSE; } @@ -3445,14 +3455,13 @@ CommentStmt: n->comment = $7; $$ = (Node *) n; } - | COMMENT ON OPERATOR any_operator '(' oper_argtypes ')' - IS comment_text + | COMMENT ON OPERATOR any_operator oper_argtypes IS comment_text { CommentStmt *n = makeNode(CommentStmt); n->objtype = OBJECT_OPERATOR; n->objname = $4; - n->objargs = $6; - n->comment = $9; + n->objargs = $5; + n->comment = $7; $$ = (Node *) n; } | COMMENT ON CONSTRAINT name ON any_name IS comment_text @@ -4088,8 +4097,8 @@ opt_class: any_name { $$ = $1; } ; opt_asc_desc: ASC { $$ = SORTBY_ASC; } - | DESC { $$ = SORTBY_DESC; } - | /*EMPTY*/ { $$ = SORTBY_DEFAULT; } + | DESC { $$ = SORTBY_DESC; } + | /*EMPTY*/ { $$ = SORTBY_DEFAULT; } ; opt_nulls_order: NULLS_FIRST { $$ = SORTBY_NULLS_FIRST; } @@ -4464,42 +4473,43 @@ RemoveAggrStmt: ; RemoveOperStmt: - DROP OPERATOR any_operator '(' oper_argtypes ')' opt_drop_behavior + DROP OPERATOR any_operator oper_argtypes opt_drop_behavior { RemoveFuncStmt *n = makeNode(RemoveFuncStmt); n->kind = OBJECT_OPERATOR; n->name = $3; - n->args = $5; - n->behavior = $7; + n->args = $4; + n->behavior = $5; n->missing_ok = false; $$ = (Node *)n; } - | DROP OPERATOR IF_P EXISTS any_operator '(' oper_argtypes ')' opt_drop_behavior + | DROP OPERATOR IF_P EXISTS any_operator oper_argtypes opt_drop_behavior { RemoveFuncStmt *n = makeNode(RemoveFuncStmt); n->kind = OBJECT_OPERATOR; n->name = $5; - n->args = $7; - n->behavior = $9; + n->args = $6; + n->behavior = $7; n->missing_ok = true; $$ = (Node *)n; } ; oper_argtypes: - Typename + '(' Typename ')' { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("missing argument"), - errhint("Use NONE to denote the missing argument of a unary operator."))); + errhint("Use NONE to denote the missing argument of a unary operator."), + scanner_errposition(@3))); } - | Typename ',' Typename - { $$ = list_make2($1, $3); } - | NONE ',' Typename /* left unary */ - { $$ = list_make2(NULL, $3); } - | Typename ',' NONE /* right unary */ - { $$ = list_make2($1, NULL); } + | '(' Typename ',' Typename ')' + { $$ = list_make2($2, $4); } + | '(' NONE ',' Typename ')' /* left unary */ + { $$ = list_make2(NULL, $4); } + | '(' Typename ',' NONE ')' /* right unary */ + { $$ = list_make2($2, NULL); } ; any_operator: @@ -4939,13 +4949,13 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleId n->newowner = $7; $$ = (Node *)n; } - | ALTER OPERATOR any_operator '(' oper_argtypes ')' OWNER TO RoleId + | ALTER OPERATOR any_operator oper_argtypes OWNER TO RoleId { AlterOwnerStmt *n = makeNode(AlterOwnerStmt); n->objectType = OBJECT_OPERATOR; n->object = $3; - n->objarg = $5; - n->newowner = $9; + n->objarg = $4; + n->newowner = $7; $$ = (Node *)n; } | ALTER OPERATOR CLASS any_name USING access_method OWNER TO RoleId @@ -5108,7 +5118,7 @@ DropRuleStmt: /***************************************************************************** * * QUERY: - * NOTIFY <qualified_name> can appear both in rule bodies and + * NOTIFY <identifier> can appear both in rule bodies and * as a query-level command * *****************************************************************************/ @@ -5116,9 +5126,7 @@ DropRuleStmt: NotifyStmt: NOTIFY ColId { NotifyStmt *n = makeNode(NotifyStmt); - n->relation = makeNode(RangeVar); - n->relation->relname = $2; - n->relation->schemaname = NULL; + n->conditionname = $2; $$ = (Node *)n; } ; @@ -5126,9 +5134,7 @@ NotifyStmt: NOTIFY ColId ListenStmt: LISTEN ColId { ListenStmt *n = makeNode(ListenStmt); - n->relation = makeNode(RangeVar); - n->relation->relname = $2; - n->relation->schemaname = NULL; + n->conditionname = $2; $$ = (Node *)n; } ; @@ -5137,15 +5143,13 @@ UnlistenStmt: UNLISTEN ColId { UnlistenStmt *n = makeNode(UnlistenStmt); - n->relation = makeNode(RangeVar); - n->relation->relname = $2; - n->relation->schemaname = NULL; + n->conditionname = $2; $$ = (Node *)n; } | UNLISTEN '*' { UnlistenStmt *n = makeNode(UnlistenStmt); - n->relation = NULL; + n->conditionname = NULL; $$ = (Node *)n; } ; @@ -6119,7 +6123,8 @@ multiple_set_clause: if (list_length($2) != list_length($5)) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("number of columns does not match number of values"))); + errmsg("number of columns does not match number of values"), + scanner_errposition(@1))); forboth(col_cell, $2, val_cell, $5) { ResTarget *res_col = (ResTarget *) lfirst(col_cell); @@ -6419,6 +6424,7 @@ sortby: a_expr USING qual_all_Op opt_nulls_order $$->sortby_dir = SORTBY_USING; $$->sortby_nulls = $4; $$->useOp = $3; + $$->location = @3; } | a_expr opt_asc_desc opt_nulls_order { @@ -6427,6 +6433,7 @@ sortby: a_expr USING qual_all_Op opt_nulls_order $$->sortby_dir = $2; $$->sortby_nulls = $3; $$->useOp = NIL; + $$->location = -1; /* no operator */ } ; @@ -6446,7 +6453,8 @@ select_limit: ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("LIMIT #,# syntax is not supported"), - errhint("Use separate LIMIT and OFFSET clauses."))); + errhint("Use separate LIMIT and OFFSET clauses."), + scanner_errposition(@1))); } ; @@ -6514,7 +6522,7 @@ for_locking_item: ; locked_rels_list: - OF name_list { $$ = $2; } + OF qualified_name_list { $$ = $2; } | /* EMPTY */ { $$ = NIL; } ; @@ -6629,12 +6637,14 @@ table_ref: relation_expr ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("VALUES in FROM must have an alias"), - errhint("For example, FROM (VALUES ...) [AS] foo."))); + errhint("For example, FROM (VALUES ...) [AS] foo."), + scanner_errposition(@1))); else ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("subquery in FROM must have an alias"), - errhint("For example, FROM (SELECT ...) [AS] foo."))); + errhint("For example, FROM (SELECT ...) [AS] foo."), + scanner_errposition(@1))); $$ = NULL; } | select_with_parens alias_clause @@ -7089,7 +7099,8 @@ opt_float: '(' Iconst ')' if ($2 < 1) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("precision for type float must be at least 1 bit"))); + errmsg("precision for type float must be at least 1 bit"), + scanner_errposition(@2))); else if ($2 <= 24) $$ = SystemTypeName("float4"); else if ($2 <= 53) @@ -7097,7 +7108,8 @@ opt_float: '(' Iconst ')' else ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("precision for type float must be less than 54 bits"))); + errmsg("precision for type float must be less than 54 bits"), + scanner_errposition(@2))); } | /*EMPTY*/ { @@ -7734,7 +7746,8 @@ a_expr: c_expr { $$ = $1; } */ ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("UNIQUE predicate is not yet implemented"))); + errmsg("UNIQUE predicate is not yet implemented"), + scanner_errposition(@1))); } | a_expr IS DOCUMENT_P %prec IS { @@ -8874,6 +8887,7 @@ qualified_name: $$->catalogname = NULL; $$->schemaname = NULL; $$->relname = $1; + $$->location = @1; } | relation_name indirection { @@ -8895,9 +8909,11 @@ qualified_name: ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("improper qualified name (too many dotted names): %s", - NameListToString(lcons(makeString($1), $2))))); + NameListToString(lcons(makeString($1), $2))), + scanner_errposition(@1))); break; } + $$->location = @1; } ; @@ -9494,7 +9510,8 @@ SpecialRuleRelation: else ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("OLD used in query that is not in a rule"))); + errmsg("OLD used in query that is not in a rule"), + scanner_errposition(@1))); } | NEW { @@ -9503,7 +9520,8 @@ SpecialRuleRelation: else ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("NEW used in query that is not in a rule"))); + errmsg("NEW used in query that is not in a rule"), + scanner_errposition(@1))); } ; @@ -9689,13 +9707,15 @@ makeOverlaps(List *largs, List *rargs, int location) else if (list_length(largs) != 2) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("wrong number of parameters on left side of OVERLAPS expression"))); + errmsg("wrong number of parameters on left side of OVERLAPS expression"), + scanner_errposition(location))); if (list_length(rargs) == 1) rargs = lappend(rargs, rargs); else if (list_length(rargs) != 2) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("wrong number of parameters on right side of OVERLAPS expression"))); + errmsg("wrong number of parameters on right side of OVERLAPS expression"), + scanner_errposition(location))); n->args = list_concat(largs, rargs); n->agg_star = FALSE; n->agg_distinct = FALSE; @@ -9813,7 +9833,8 @@ insertSelectOptions(SelectStmt *stmt, if (stmt->sortClause) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("multiple ORDER BY clauses not allowed"))); + errmsg("multiple ORDER BY clauses not allowed"), + scanner_errposition(exprLocation((Node *) sortClause)))); stmt->sortClause = sortClause; } /* We can handle multiple locking clauses, though */ @@ -9823,7 +9844,8 @@ insertSelectOptions(SelectStmt *stmt, if (stmt->limitOffset) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("multiple OFFSET clauses not allowed"))); + errmsg("multiple OFFSET clauses not allowed"), + scanner_errposition(exprLocation(limitOffset)))); stmt->limitOffset = limitOffset; } if (limitCount) @@ -9831,7 +9853,8 @@ insertSelectOptions(SelectStmt *stmt, if (stmt->limitCount) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("multiple LIMIT clauses not allowed"))); + errmsg("multiple LIMIT clauses not allowed"), + scanner_errposition(exprLocation(limitCount)))); stmt->limitCount = limitCount; } } diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c index 215557396f9..d85f64c7abc 100644 --- a/src/backend/parser/parse_agg.c +++ b/src/backend/parser/parse_agg.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_agg.c,v 1.82 2008/08/28 23:09:47 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_agg.c,v 1.83 2008/09/01 20:42:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -70,7 +70,9 @@ transformAggregateCall(ParseState *pstate, Aggref *agg) if (checkExprHasAggs((Node *) agg->args)) ereport(ERROR, (errcode(ERRCODE_GROUPING_ERROR), - errmsg("aggregate function calls cannot be nested"))); + errmsg("aggregate function calls cannot be nested"), + parser_errposition(pstate, + locate_agg_of_level((Node *) agg->args, 0)))); } if (min_varlevel < 0) @@ -117,11 +119,15 @@ parseCheckAggregates(ParseState *pstate, Query *qry) if (checkExprHasAggs(qry->jointree->quals)) ereport(ERROR, (errcode(ERRCODE_GROUPING_ERROR), - errmsg("aggregates not allowed in WHERE clause"))); + errmsg("aggregates not allowed in WHERE clause"), + parser_errposition(pstate, + locate_agg_of_level(qry->jointree->quals, 0)))); if (checkExprHasAggs((Node *) qry->jointree->fromlist)) ereport(ERROR, (errcode(ERRCODE_GROUPING_ERROR), - errmsg("aggregates not allowed in JOIN conditions"))); + errmsg("aggregates not allowed in JOIN conditions"), + parser_errposition(pstate, + locate_agg_of_level((Node *) qry->jointree->fromlist, 0)))); /* * No aggregates allowed in GROUP BY clauses, either. @@ -140,7 +146,9 @@ parseCheckAggregates(ParseState *pstate, Query *qry) if (checkExprHasAggs(expr)) ereport(ERROR, (errcode(ERRCODE_GROUPING_ERROR), - errmsg("aggregates not allowed in GROUP BY clause"))); + errmsg("aggregates not allowed in GROUP BY clause"), + parser_errposition(pstate, + locate_agg_of_level(expr, 0)))); groupClauses = lcons(expr, groupClauses); } @@ -327,13 +335,14 @@ check_ungrouped_columns_walker(Node *node, ereport(ERROR, (errcode(ERRCODE_GROUPING_ERROR), errmsg("column \"%s.%s\" must appear in the GROUP BY clause or be used in an aggregate function", - rte->eref->aliasname, attname))); + rte->eref->aliasname, attname), + parser_errposition(context->pstate, var->location))); else ereport(ERROR, (errcode(ERRCODE_GROUPING_ERROR), errmsg("subquery uses ungrouped column \"%s.%s\" from outer query", - rte->eref->aliasname, attname))); - + rte->eref->aliasname, attname), + parser_errposition(context->pstate, var->location))); } if (IsA(node, Query)) diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index 5285f0ba3d9..2f547f29be6 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.178 2008/08/30 01:39:14 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.179 2008/09/01 20:42:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -66,12 +66,13 @@ static Node *buildMergedJoinVar(ParseState *pstate, JoinType jointype, Var *l_colvar, Var *r_colvar); static TargetEntry *findTargetlistEntry(ParseState *pstate, Node *node, List **tlist, int clause); +static int get_matching_location(int sortgroupref, + List *sortgrouprefs, List *exprs); static List *addTargetToSortList(ParseState *pstate, TargetEntry *tle, - List *sortlist, List *targetlist, - SortByDir sortby_dir, SortByNulls sortby_nulls, - List *sortby_opname, bool resolveUnknown); + List *sortlist, List *targetlist, SortBy *sortby, + bool resolveUnknown); static List *addTargetToGroupList(ParseState *pstate, TargetEntry *tle, - List *grouplist, List *targetlist, + List *grouplist, List *targetlist, int location, bool resolveUnknown); @@ -163,7 +164,8 @@ setTargetTable(ParseState *pstate, RangeVar *relation, * free_parsestate() will eventually do the corresponding heap_close(), * but *not* release the lock. */ - pstate->p_target_relation = heap_openrv(relation, RowExclusiveLock); + pstate->p_target_relation = parserOpenTable(pstate, relation, + RowExclusiveLock); /* * Now build an RTE. @@ -390,7 +392,9 @@ transformJoinOnClause(ParseState *pstate, JoinExpr *j, ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), errmsg("JOIN/ON clause refers to \"%s\", which is not part of JOIN", - rt_fetch(varno, pstate->p_rtable)->eref->aliasname))); + rt_fetch(varno, pstate->p_rtable)->eref->aliasname), + parser_errposition(pstate, + locate_var_of_relation(result, varno, 0)))); } bms_free(clause_varnos); @@ -431,12 +435,11 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r) /* * We require user to supply an alias for a subselect, per SQL92. To relax * this, we'd have to be prepared to gin up a unique alias for an - * unlabeled subselect. + * unlabeled subselect. (This is just elog, not ereport, because the + * grammar should have enforced it already.) */ if (r->alias == NULL) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("subquery in FROM must have an alias"))); + elog(ERROR, "subquery in FROM must have an alias"); /* * Analyze and transform the subquery. @@ -447,13 +450,16 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r) * Check that we got something reasonable. Many of these conditions are * impossible given restrictions of the grammar, but check 'em anyway. */ - if (query->commandType != CMD_SELECT || + if (!IsA(query, Query) || + query->commandType != CMD_SELECT || query->utilityStmt != NULL) - elog(ERROR, "expected SELECT query from subquery in FROM"); - if (query->intoClause != NULL) + elog(ERROR, "unexpected non-SELECT command in subquery in FROM"); + if (query->intoClause) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("subquery in FROM cannot have SELECT INTO"))); + errmsg("subquery in FROM cannot have SELECT INTO"), + parser_errposition(pstate, + exprLocation((Node *) query->intoClause)))); /* * The subquery cannot make use of any variables from FROM items created @@ -473,7 +479,9 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r) if (contain_vars_of_level((Node *) query, 1)) ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), - errmsg("subquery in FROM cannot refer to other relations of same query level"))); + errmsg("subquery in FROM cannot refer to other relations of same query level"), + parser_errposition(pstate, + locate_var_of_level((Node *) query, 1)))); } /* @@ -522,7 +530,9 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r) if (contain_vars_of_level(funcexpr, 0)) ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), - errmsg("function expression in FROM cannot refer to other relations of same query level"))); + errmsg("function expression in FROM cannot refer to other relations of same query level"), + parser_errposition(pstate, + locate_var_of_level(funcexpr, 0)))); } /* @@ -534,7 +544,9 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r) if (checkExprHasAggs(funcexpr)) ereport(ERROR, (errcode(ERRCODE_GROUPING_ERROR), - errmsg("cannot use aggregate function in function expression in FROM"))); + errmsg("cannot use aggregate function in function expression in FROM"), + parser_errposition(pstate, + locate_agg_of_level(funcexpr, 0)))); } /* @@ -709,9 +721,9 @@ transformFromClauseItem(ParseState *pstate, Node *n, * * Note: expandRTE returns new lists, safe for me to modify */ - expandRTE(l_rte, l_rtindex, 0, false, + expandRTE(l_rte, l_rtindex, 0, -1, false, &l_colnames, &l_colvars); - expandRTE(r_rte, r_rtindex, 0, false, + expandRTE(r_rte, r_rtindex, 0, -1, false, &r_colnames, &r_colvars); /* @@ -1109,7 +1121,9 @@ transformLimitClause(ParseState *pstate, Node *clause, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), /* translator: %s is name of a SQL construct, eg LIMIT */ errmsg("argument of %s must not contain variables", - constructName))); + constructName), + parser_errposition(pstate, + locate_var_of_level(qual, 0)))); } if (checkExprHasAggs(qual)) { @@ -1117,7 +1131,9 @@ transformLimitClause(ParseState *pstate, Node *clause, (errcode(ERRCODE_GROUPING_ERROR), /* translator: %s is name of a SQL construct, eg LIMIT */ errmsg("argument of %s must not contain aggregates", - constructName))); + constructName), + parser_errposition(pstate, + locate_agg_of_level(qual, 0)))); } return qual; @@ -1365,6 +1381,7 @@ transformGroupClause(ParseState *pstate, List *grouplist, if (!found) result = addTargetToGroupList(pstate, tle, result, *targetlist, + exprLocation(gexpr), true); } @@ -1396,10 +1413,7 @@ transformSortClause(ParseState *pstate, targetlist, ORDER_CLAUSE); sortlist = addTargetToSortList(pstate, tle, - sortlist, *targetlist, - sortby->sortby_dir, - sortby->sortby_nulls, - sortby->useOp, + sortlist, *targetlist, sortby, resolveUnknown); } @@ -1450,7 +1464,9 @@ transformDistinctClause(ParseState *pstate, if (tle->resjunk) ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), - errmsg("for SELECT DISTINCT, ORDER BY expressions must appear in select list"))); + errmsg("for SELECT DISTINCT, ORDER BY expressions must appear in select list"), + parser_errposition(pstate, + exprLocation((Node *) tle->expr)))); result = lappend(result, copyObject(scl)); } @@ -1466,6 +1482,7 @@ transformDistinctClause(ParseState *pstate, continue; /* ignore junk */ result = addTargetToGroupList(pstate, tle, result, *targetlist, + exprLocation((Node *) tle->expr), true); } @@ -1490,28 +1507,29 @@ transformDistinctOnClause(ParseState *pstate, List *distinctlist, List **targetlist, List *sortClause) { List *result = NIL; - ListCell *slitem; - ListCell *dlitem; - Bitmapset *refnos = NULL; - int sortgroupref; + List *sortgrouprefs = NIL; bool skipped_sortitem; + ListCell *lc; + ListCell *lc2; /* * Add all the DISTINCT ON expressions to the tlist (if not already * present, they are added as resjunk items). Assign sortgroupref - * numbers to them, and form a bitmapset of these numbers. (A - * bitmapset is convenient here because we don't care about order - * and we can discard duplicates.) + * numbers to them, and make a list of these numbers. (NB: we rely + * below on the sortgrouprefs list being one-for-one with the original + * distinctlist. Also notice that we could have duplicate DISTINCT ON + * expressions and hence duplicate entries in sortgrouprefs.) */ - foreach(dlitem, distinctlist) + foreach(lc, distinctlist) { - Node *dexpr = (Node *) lfirst(dlitem); + Node *dexpr = (Node *) lfirst(lc); + int sortgroupref; TargetEntry *tle; tle = findTargetlistEntry(pstate, dexpr, targetlist, DISTINCT_ON_CLAUSE); sortgroupref = assignSortGroupRef(tle, *targetlist); - refnos = bms_add_member(refnos, sortgroupref); + sortgrouprefs = lappend_int(sortgrouprefs, sortgroupref); } /* @@ -1523,16 +1541,20 @@ transformDistinctOnClause(ParseState *pstate, List *distinctlist, * skipped an ORDER BY item that wasn't in DISTINCT ON. */ skipped_sortitem = false; - foreach(slitem, sortClause) + foreach(lc, sortClause) { - SortGroupClause *scl = (SortGroupClause *) lfirst(slitem); + SortGroupClause *scl = (SortGroupClause *) lfirst(lc); - if (bms_is_member(scl->tleSortGroupRef, refnos)) + if (list_member_int(sortgrouprefs, scl->tleSortGroupRef)) { if (skipped_sortitem) ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), - errmsg("SELECT DISTINCT ON expressions must match initial ORDER BY expressions"))); + errmsg("SELECT DISTINCT ON expressions must match initial ORDER BY expressions"), + parser_errposition(pstate, + get_matching_location(scl->tleSortGroupRef, + sortgrouprefs, + distinctlist)))); else result = lappend(result, copyObject(scl)); } @@ -1549,8 +1571,10 @@ transformDistinctOnClause(ParseState *pstate, List *distinctlist, * better to throw an error or warning here. But historically we've * allowed it, so keep doing so.) */ - while ((sortgroupref = bms_first_member(refnos)) >= 0) + forboth(lc, distinctlist, lc2, sortgrouprefs) { + Node *dexpr = (Node *) lfirst(lc); + int sortgroupref = lfirst_int(lc2); TargetEntry *tle = get_sortgroupref_tle(sortgroupref, *targetlist); if (targetIsInSortList(tle, InvalidOid, result)) @@ -1558,9 +1582,11 @@ transformDistinctOnClause(ParseState *pstate, List *distinctlist, if (skipped_sortitem) ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), - errmsg("SELECT DISTINCT ON expressions must match initial ORDER BY expressions"))); + errmsg("SELECT DISTINCT ON expressions must match initial ORDER BY expressions"), + parser_errposition(pstate, exprLocation(dexpr)))); result = addTargetToGroupList(pstate, tle, result, *targetlist, + exprLocation(dexpr), true); } @@ -1568,6 +1594,33 @@ transformDistinctOnClause(ParseState *pstate, List *distinctlist, } /* + * get_matching_location + * Get the exprLocation of the exprs member corresponding to the + * (first) member of sortgrouprefs that equals sortgroupref. + * + * This is used so that we can point at a troublesome DISTINCT ON entry. + * (Note that we need to use the original untransformed DISTINCT ON list + * item, as whatever TLE it corresponds to will very possibly have a + * parse location pointing to some matching entry in the SELECT list + * or ORDER BY list.) + */ +static int +get_matching_location(int sortgroupref, List *sortgrouprefs, List *exprs) +{ + ListCell *lcs; + ListCell *lce; + + forboth(lcs, sortgrouprefs, lce, exprs) + { + if (lfirst_int(lcs) == sortgroupref) + return exprLocation((Node *) lfirst(lce)); + } + /* if no match, caller blew it */ + elog(ERROR, "get_matching_location: no matching sortgroupref"); + return -1; /* keep compiler quiet */ +} + +/* * addTargetToSortList * If the given targetlist entry isn't already in the SortGroupClause * list, add it to the end of the list, using the given sort ordering @@ -1582,14 +1635,15 @@ transformDistinctOnClause(ParseState *pstate, List *distinctlist, */ static List * addTargetToSortList(ParseState *pstate, TargetEntry *tle, - List *sortlist, List *targetlist, - SortByDir sortby_dir, SortByNulls sortby_nulls, - List *sortby_opname, bool resolveUnknown) + List *sortlist, List *targetlist, SortBy *sortby, + bool resolveUnknown) { Oid restype = exprType((Node *) tle->expr); Oid sortop; Oid eqop; bool reverse; + int location; + ParseCallbackState pcbstate; /* if tlist item is an UNKNOWN literal, change it to TEXT */ if (restype == UNKNOWNOID && resolveUnknown) @@ -1602,8 +1656,21 @@ addTargetToSortList(ParseState *pstate, TargetEntry *tle, restype = TEXTOID; } + /* + * Rather than clutter the API of get_sort_group_operators and the other + * functions we're about to use, make use of error context callback to + * mark any error reports with a parse position. We point to the operator + * location if present, else to the expression being sorted. (NB: use + * the original untransformed expression here; the TLE entry might well + * point at a duplicate expression in the regular SELECT list.) + */ + location = sortby->location; + if (location < 0) + location = exprLocation(sortby->node); + setup_parser_errposition_callback(&pcbstate, pstate, location); + /* determine the sortop, eqop, and directionality */ - switch (sortby_dir) + switch (sortby->sortby_dir) { case SORTBY_DEFAULT: case SORTBY_ASC: @@ -1619,8 +1686,8 @@ addTargetToSortList(ParseState *pstate, TargetEntry *tle, reverse = true; break; case SORTBY_USING: - Assert(sortby_opname != NIL); - sortop = compatible_oper_opid(sortby_opname, + Assert(sortby->useOp != NIL); + sortop = compatible_oper_opid(sortby->useOp, restype, restype, false); @@ -1635,17 +1702,19 @@ addTargetToSortList(ParseState *pstate, TargetEntry *tle, ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("operator %s is not a valid ordering operator", - strVal(llast(sortby_opname))), + strVal(llast(sortby->useOp))), errhint("Ordering operators must be \"<\" or \">\" members of btree operator families."))); break; default: - elog(ERROR, "unrecognized sortby_dir: %d", sortby_dir); + elog(ERROR, "unrecognized sortby_dir: %d", sortby->sortby_dir); sortop = InvalidOid; /* keep compiler quiet */ eqop = InvalidOid; reverse = false; break; } + cancel_parser_errposition_callback(&pcbstate); + /* avoid making duplicate sortlist entries */ if (!targetIsInSortList(tle, sortop, sortlist)) { @@ -1656,7 +1725,7 @@ addTargetToSortList(ParseState *pstate, TargetEntry *tle, sortcl->eqop = eqop; sortcl->sortop = sortop; - switch (sortby_nulls) + switch (sortby->sortby_nulls) { case SORTBY_NULLS_DEFAULT: /* NULLS FIRST is default for DESC; other way for ASC */ @@ -1669,7 +1738,8 @@ addTargetToSortList(ParseState *pstate, TargetEntry *tle, sortcl->nulls_first = false; break; default: - elog(ERROR, "unrecognized sortby_nulls: %d", sortby_nulls); + elog(ERROR, "unrecognized sortby_nulls: %d", + sortby->sortby_nulls); break; } @@ -1690,6 +1760,11 @@ addTargetToSortList(ParseState *pstate, TargetEntry *tle, * the TLE is considered "already in the list" if it appears there with any * sorting semantics. * + * location is the parse location to be fingered in event of trouble. Note + * that we can't rely on exprLocation(tle->expr), because that might point + * to a SELECT item that matches the GROUP BY item; it'd be pretty confusing + * to report such a location. + * * If resolveUnknown is TRUE, convert TLEs of type UNKNOWN to TEXT. If not, * do nothing (which implies the search for an equality operator will fail). * pstate should be provided if resolveUnknown is TRUE, but can be NULL @@ -1699,7 +1774,7 @@ addTargetToSortList(ParseState *pstate, TargetEntry *tle, */ static List * addTargetToGroupList(ParseState *pstate, TargetEntry *tle, - List *grouplist, List *targetlist, + List *grouplist, List *targetlist, int location, bool resolveUnknown) { Oid restype = exprType((Node *) tle->expr); @@ -1721,12 +1796,17 @@ addTargetToGroupList(ParseState *pstate, TargetEntry *tle, if (!targetIsInSortList(tle, InvalidOid, grouplist)) { SortGroupClause *grpcl = makeNode(SortGroupClause); + ParseCallbackState pcbstate; + + setup_parser_errposition_callback(&pcbstate, pstate, location); /* determine the eqop and optional sortop */ get_sort_group_operators(restype, false, true, false, &sortop, &eqop, NULL); + cancel_parser_errposition_callback(&pcbstate); + grpcl->tleSortGroupRef = assignSortGroupRef(tle, targetlist); grpcl->eqop = eqop; grpcl->sortop = sortop; diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index cd9b7b0cfbe..efe45974c35 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.165 2008/08/28 23:09:47 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.166 2008/09/01 20:42:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -180,6 +180,7 @@ coerce_type(ParseState *pstate, Node *node, Oid baseTypeId; int32 baseTypeMod; Type targetType; + ParseCallbackState pcbstate; /* * If the target type is a domain, we want to call its base type's @@ -208,6 +209,12 @@ coerce_type(ParseState *pstate, Node *node, newcon->location = location; /* + * Set up to point at the constant's text if the input routine + * throws an error. + */ + setup_parser_errposition_callback(&pcbstate, pstate, con->location); + + /* * We pass typmod -1 to the input routine, primarily because existing * input routines follow implicit-coercion semantics for length * checks, which is not always what we want here. Any length @@ -223,6 +230,8 @@ coerce_type(ParseState *pstate, Node *node, else newcon->constvalue = stringTypeDatum(targetType, NULL, -1); + cancel_parser_errposition_callback(&pcbstate); + result = (Node *) newcon; /* If target is a domain, apply constraints. */ @@ -257,7 +266,8 @@ coerce_type(ParseState *pstate, Node *node, paramno > toppstate->p_numparams) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_PARAMETER), - errmsg("there is no parameter $%d", paramno))); + errmsg("there is no parameter $%d", paramno), + parser_errposition(pstate, param->location))); if (toppstate->p_paramtypes[paramno - 1] == UNKNOWNOID) { @@ -277,7 +287,8 @@ coerce_type(ParseState *pstate, Node *node, paramno), errdetail("%s versus %s", format_type_be(toppstate->p_paramtypes[paramno - 1]), - format_type_be(targetTypeId)))); + format_type_be(targetTypeId)), + parser_errposition(pstate, param->location))); } param->paramtype = targetTypeId; @@ -819,10 +830,11 @@ coerce_record_to_complex(ParseState *pstate, Node *node, { int rtindex = ((Var *) node)->varno; int sublevels_up = ((Var *) node)->varlevelsup; + int vlocation = ((Var *) node)->location; RangeTblEntry *rte; rte = GetRTEByRangeTablePosn(pstate, rtindex, sublevels_up); - expandRTE(rte, rtindex, sublevels_up, false, + expandRTE(rte, rtindex, sublevels_up, vlocation, false, NULL, &args); } else diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 8496e7291c1..1091d87473d 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.233 2008/08/30 01:39:14 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.234 2008/09/01 20:42:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -122,7 +122,7 @@ transformExpr(ParseState *pstate, Node *expr) A_Const *con = (A_Const *) expr; Value *val = &con->val; - result = (Node *) make_const(val, con->location); + result = (Node *) make_const(pstate, val, con->location); break; } @@ -454,6 +454,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) * "rel.*". */ if (refnameRangeTblEntry(pstate, NULL, name1, + cref->location, &levels_up) != NULL) node = transformWholeRowRef(pstate, NULL, name1, cref->location); @@ -621,7 +622,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) * return a pointer to it. */ static Oid * -find_param_type(ParseState *pstate, int paramno) +find_param_type(ParseState *pstate, int paramno, int location) { Oid *result; @@ -635,14 +636,15 @@ find_param_type(ParseState *pstate, int paramno) if (paramno <= 0) /* probably can't happen? */ ereport(ERROR, (errcode(ERRCODE_UNDEFINED_PARAMETER), - errmsg("there is no parameter $%d", paramno))); + errmsg("there is no parameter $%d", paramno), + parser_errposition(pstate, location))); if (paramno > pstate->p_numparams) { if (!pstate->p_variableparams) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_PARAMETER), - errmsg("there is no parameter $%d", - paramno))); + errmsg("there is no parameter $%d", paramno), + parser_errposition(pstate, location))); /* Okay to enlarge param array */ if (pstate->p_paramtypes) pstate->p_paramtypes = (Oid *) repalloc(pstate->p_paramtypes, @@ -672,7 +674,7 @@ static Node * transformParamRef(ParseState *pstate, ParamRef *pref) { int paramno = pref->number; - Oid *pptype = find_param_type(pstate, paramno); + Oid *pptype = find_param_type(pstate, paramno, pref->location); Param *param; param = makeNode(Param); @@ -1235,10 +1237,22 @@ transformSubLink(ParseState *pstate, SubLink *sublink) pstate->p_hasSubLinks = true; qtree = parse_sub_analyze(sublink->subselect, pstate); - if (qtree->commandType != CMD_SELECT || - qtree->utilityStmt != NULL || - qtree->intoClause != NULL) - elog(ERROR, "bad query in sub-select"); + + /* + * Check that we got something reasonable. Many of these conditions are + * impossible given restrictions of the grammar, but check 'em anyway. + */ + if (!IsA(qtree, Query) || + qtree->commandType != CMD_SELECT || + qtree->utilityStmt != NULL) + elog(ERROR, "unexpected non-SELECT command in SubLink"); + if (qtree->intoClause) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subquery cannot have SELECT INTO"), + parser_errposition(pstate, + exprLocation((Node *) qtree->intoClause)))); + sublink->subselect = (Node *) qtree; if (sublink->subLinkType == EXISTS_SUBLINK) @@ -1445,7 +1459,8 @@ transformArrayExpr(ParseState *pstate, A_ArrayExpr *a, ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("could not find element type for data type %s", - format_type_be(array_type)))); + format_type_be(array_type)), + parser_errposition(pstate, a->location))); } else { @@ -1455,7 +1470,8 @@ transformArrayExpr(ParseState *pstate, A_ArrayExpr *a, ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("could not find array type for data type %s", - format_type_be(element_type)))); + format_type_be(element_type)), + parser_errposition(pstate, a->location))); } coerce_hard = false; } @@ -1823,7 +1839,7 @@ transformCurrentOfExpr(ParseState *pstate, CurrentOfExpr *cexpr) /* If a parameter is used, it must be of type REFCURSOR */ if (cexpr->cursor_name == NULL) { - Oid *pptype = find_param_type(pstate, cexpr->cursor_param); + Oid *pptype = find_param_type(pstate, cexpr->cursor_param, -1); if (pstate->p_variableparams && *pptype == UNKNOWNOID) { @@ -1866,12 +1882,12 @@ transformWholeRowRef(ParseState *pstate, char *schemaname, char *relname, /* Look up the referenced RTE, creating it if needed */ - rte = refnameRangeTblEntry(pstate, schemaname, relname, + rte = refnameRangeTblEntry(pstate, schemaname, relname, location, &sublevels_up); if (rte == NULL) - rte = addImplicitRTE(pstate, makeRangeVar(schemaname, relname), - location); + rte = addImplicitRTE(pstate, + makeRangeVar(schemaname, relname, location)); vnum = RTERangeTablePosn(pstate, rte, &sublevels_up); diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index 31317800c1e..bbbc5fe7a94 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.206 2008/08/28 23:09:47 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.207 2008/09/01 20:42:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -270,7 +270,8 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("could not find array type for data type %s", - format_type_be(newa->element_typeid)))); + format_type_be(newa->element_typeid)), + parser_errposition(pstate, exprLocation((Node *) vargs)))); newa->multidims = false; newa->location = exprLocation((Node *) vargs); diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c index 1760e492854..532dbe319dd 100644 --- a/src/backend/parser/parse_node.c +++ b/src/backend/parser/parse_node.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_node.c,v 1.102 2008/08/28 23:09:47 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_node.c,v 1.103 2008/09/01 20:42:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -29,6 +29,9 @@ #include "utils/varbit.h" +static void pcb_error_callback(void *arg); + + /* * make_parsestate * Allocate and initialize a new ParseState. @@ -113,6 +116,62 @@ parser_errposition(ParseState *pstate, int location) /* + * setup_parser_errposition_callback + * Arrange for non-parser errors to report an error position + * + * Sometimes the parser calls functions that aren't part of the parser + * subsystem and can't reasonably be passed a ParseState; yet we would + * like any errors thrown in those functions to be tagged with a parse + * error location. Use this function to set up an error context stack + * entry that will accomplish that. Usage pattern: + * + * declare a local variable "ParseCallbackState pcbstate" + * ... + * setup_parser_errposition_callback(&pcbstate, pstate, location); + * call function that might throw error; + * cancel_parser_errposition_callback(&pcbstate); + */ +void +setup_parser_errposition_callback(ParseCallbackState *pcbstate, + ParseState *pstate, int location) +{ + /* Setup error traceback support for ereport() */ + pcbstate->pstate = pstate; + pcbstate->location = location; + pcbstate->errcontext.callback = pcb_error_callback; + pcbstate->errcontext.arg = (void *) pcbstate; + pcbstate->errcontext.previous = error_context_stack; + error_context_stack = &pcbstate->errcontext; +} + +/* + * Cancel a previously-set-up errposition callback. + */ +void +cancel_parser_errposition_callback(ParseCallbackState *pcbstate) +{ + /* Pop the error context stack */ + error_context_stack = pcbstate->errcontext.previous; +} + +/* + * Error context callback for inserting parser error location. + * + * Note that this will be called for *any* error occurring while the + * callback is installed. We avoid inserting an irrelevant error location + * if the error is a query cancel --- are there any other important cases? + */ +static void +pcb_error_callback(void *arg) +{ + ParseCallbackState *pcbstate = (ParseCallbackState *) arg; + + if (geterrcode() != ERRCODE_QUERY_CANCELED) + (void) parser_errposition(pcbstate->pstate, pcbstate->location); +} + + +/* * make_var * Build a Var node for an attribute identified by RTE and attrno */ @@ -344,14 +403,15 @@ transformArraySubscripts(ParseState *pstate, * too many examples that fail if we try. */ Const * -make_const(Value *value, int location) +make_const(ParseState *pstate, Value *value, int location) { + Const *con; Datum val; int64 val64; Oid typeid; int typelen; bool typebyval; - Const *con; + ParseCallbackState pcbstate; switch (nodeTag(value)) { @@ -392,10 +452,13 @@ make_const(Value *value, int location) } else { + /* arrange to report location if numeric_in() fails */ + setup_parser_errposition_callback(&pcbstate, pstate, location); val = DirectFunctionCall3(numeric_in, CStringGetDatum(strVal(value)), ObjectIdGetDatum(InvalidOid), Int32GetDatum(-1)); + cancel_parser_errposition_callback(&pcbstate); typeid = NUMERICOID; typelen = -1; /* variable len */ @@ -417,10 +480,13 @@ make_const(Value *value, int location) break; case T_BitString: + /* arrange to report location if bit_in() fails */ + setup_parser_errposition_callback(&pcbstate, pstate, location); val = DirectFunctionCall3(bit_in, CStringGetDatum(strVal(value)), ObjectIdGetDatum(InvalidOid), Int32GetDatum(-1)); + cancel_parser_errposition_callback(&pcbstate); typeid = BITOID; typelen = -1; typebyval = false; diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index f7eb825f5d0..6accd96f0da 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.134 2008/08/28 23:09:48 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.135 2008/09/01 20:42:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -36,20 +36,20 @@ bool add_missing_from; static RangeTblEntry *scanNameSpaceForRefname(ParseState *pstate, - const char *refname); -static RangeTblEntry *scanNameSpaceForRelid(ParseState *pstate, Oid relid); + const char *refname, int location); +static RangeTblEntry *scanNameSpaceForRelid(ParseState *pstate, Oid relid, + int location); static bool isLockedRel(ParseState *pstate, char *refname); static void expandRelation(Oid relid, Alias *eref, int rtindex, int sublevels_up, - bool include_dropped, + int location, bool include_dropped, List **colnames, List **colvars); static void expandTupleDesc(TupleDesc tupdesc, Alias *eref, int rtindex, int sublevels_up, - bool include_dropped, + int location, bool include_dropped, List **colnames, List **colvars); static int specialAttNum(const char *attname); -static void warnAutoRange(ParseState *pstate, RangeVar *relation, - int location); +static void warnAutoRange(ParseState *pstate, RangeVar *relation); /* @@ -77,6 +77,7 @@ RangeTblEntry * refnameRangeTblEntry(ParseState *pstate, const char *schemaname, const char *refname, + int location, int *sublevels_up) { Oid relId = InvalidOid; @@ -99,9 +100,9 @@ refnameRangeTblEntry(ParseState *pstate, RangeTblEntry *result; if (OidIsValid(relId)) - result = scanNameSpaceForRelid(pstate, relId); + result = scanNameSpaceForRelid(pstate, relId, location); else - result = scanNameSpaceForRefname(pstate, refname); + result = scanNameSpaceForRefname(pstate, refname, location); if (result) return result; @@ -122,7 +123,7 @@ refnameRangeTblEntry(ParseState *pstate, * if no match. Raise error if multiple matches. */ static RangeTblEntry * -scanNameSpaceForRefname(ParseState *pstate, const char *refname) +scanNameSpaceForRefname(ParseState *pstate, const char *refname, int location) { RangeTblEntry *result = NULL; ListCell *l; @@ -137,7 +138,8 @@ scanNameSpaceForRefname(ParseState *pstate, const char *refname) ereport(ERROR, (errcode(ERRCODE_AMBIGUOUS_ALIAS), errmsg("table reference \"%s\" is ambiguous", - refname))); + refname), + parser_errposition(pstate, location))); result = rte; } } @@ -154,7 +156,7 @@ scanNameSpaceForRefname(ParseState *pstate, const char *refname) * acts the way it does. */ static RangeTblEntry * -scanNameSpaceForRelid(ParseState *pstate, Oid relid) +scanNameSpaceForRelid(ParseState *pstate, Oid relid, int location) { RangeTblEntry *result = NULL; ListCell *l; @@ -172,7 +174,8 @@ scanNameSpaceForRelid(ParseState *pstate, Oid relid) ereport(ERROR, (errcode(ERRCODE_AMBIGUOUS_ALIAS), errmsg("table reference %u is ambiguous", - relid))); + relid), + parser_errposition(pstate, location))); result = rte; } } @@ -466,14 +469,15 @@ qualifiedNameToVar(ParseState *pstate, RangeTblEntry *rte; int sublevels_up; - rte = refnameRangeTblEntry(pstate, schemaname, refname, &sublevels_up); + rte = refnameRangeTblEntry(pstate, schemaname, refname, location, + &sublevels_up); if (rte == NULL) { if (!implicitRTEOK) return NULL; - rte = addImplicitRTE(pstate, makeRangeVar(schemaname, refname), - location); + rte = addImplicitRTE(pstate, + makeRangeVar(schemaname, refname, location)); } return scanRTEForColumn(pstate, rte, colname, location); @@ -608,6 +612,28 @@ buildScalarFunctionAlias(Node *funcexpr, char *funcname, } /* + * Open a table during parse analysis + * + * This is essentially just the same as heap_openrv(), except that it + * arranges to include the RangeVar's parse location in any resulting error. + * + * Note: properly, lockmode should be declared LOCKMODE not int, but that + * would require importing storage/lock.h into parse_relation.h. Since + * LOCKMODE is typedef'd as int anyway, that seems like overkill. + */ +Relation +parserOpenTable(ParseState *pstate, const RangeVar *relation, int lockmode) +{ + Relation rel; + ParseCallbackState pcbstate; + + setup_parser_errposition_callback(&pcbstate, pstate, relation->location); + rel = heap_openrv(relation, lockmode); + cancel_parser_errposition_callback(&pcbstate); + return rel; +} + +/* * Add an entry for a relation to the pstate's range table (p_rtable). * * If pstate is NULL, we just build an RTE and return it without adding it @@ -638,7 +664,7 @@ addRangeTableEntry(ParseState *pstate, * depending on whether we're doing SELECT FOR UPDATE/SHARE. */ lockmode = isLockedRel(pstate, refname) ? RowShareLock : AccessShareLock; - rel = heap_openrv(relation, lockmode); + rel = parserOpenTable(pstate, relation, lockmode); rte->relid = RelationGetRelid(rel); /* @@ -859,14 +885,16 @@ addRangeTableEntryForFunction(ParseState *pstate, if (functypclass != TYPEFUNC_RECORD) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("a column definition list is only allowed for functions returning \"record\""))); + errmsg("a column definition list is only allowed for functions returning \"record\""), + parser_errposition(pstate, exprLocation(funcexpr)))); } else { if (functypclass == TYPEFUNC_RECORD) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("a column definition list is required for functions returning \"record\""))); + errmsg("a column definition list is required for functions returning \"record\""), + parser_errposition(pstate, exprLocation(funcexpr)))); } if (functypclass == TYPEFUNC_COMPOSITE) @@ -901,7 +929,8 @@ addRangeTableEntryForFunction(ParseState *pstate, ereport(ERROR, (errcode(ERRCODE_INVALID_TABLE_DEFINITION), errmsg("column \"%s\" cannot be declared SETOF", - attrname))); + attrname), + parser_errposition(pstate, n->typename->location))); attrtype = typenameTypeId(pstate, n->typename, &attrtypmod); eref->colnames = lappend(eref->colnames, makeString(attrname)); rte->funccoltypes = lappend_oid(rte->funccoltypes, attrtype); @@ -912,7 +941,8 @@ addRangeTableEntryForFunction(ParseState *pstate, ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("function \"%s\" in FROM has unsupported return type %s", - funcname, format_type_be(funcrettype)))); + funcname, format_type_be(funcrettype)), + parser_errposition(pstate, exprLocation(funcexpr)))); /*---------- * Flags: @@ -1107,9 +1137,9 @@ isLockedRel(ParseState *pstate, char *refname) foreach(l2, lc->lockedRels) { - char *rname = strVal(lfirst(l2)); + RangeVar *thisrel = (RangeVar *) lfirst(l2); - if (strcmp(refname, rname) == 0) + if (strcmp(refname, thisrel->relname) == 0) return true; } } @@ -1150,12 +1180,12 @@ addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte, * a conflicting name. */ RangeTblEntry * -addImplicitRTE(ParseState *pstate, RangeVar *relation, int location) +addImplicitRTE(ParseState *pstate, RangeVar *relation) { RangeTblEntry *rte; /* issue warning or error as needed */ - warnAutoRange(pstate, relation, location); + warnAutoRange(pstate, relation); /* * Note that we set inFromCl true, so that the RTE will be listed @@ -1179,9 +1209,9 @@ addImplicitRTE(ParseState *pstate, RangeVar *relation, int location) * results. If include_dropped is TRUE then empty strings and NULL constants * (not Vars!) are returned for dropped columns. * - * rtindex and sublevels_up are the varno and varlevelsup values to use - * in the created Vars. Ordinarily rtindex should match the actual position - * of the RTE in its rangetable. + * rtindex, sublevels_up, and location are the varno, varlevelsup, and location + * values to use in the created Vars. Ordinarily rtindex should match the + * actual position of the RTE in its rangetable. * * The output lists go into *colnames and *colvars. * If only one of the two kinds of output list is needed, pass NULL for the @@ -1189,7 +1219,7 @@ addImplicitRTE(ParseState *pstate, RangeVar *relation, int location) */ void expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, - bool include_dropped, + int location, bool include_dropped, List **colnames, List **colvars) { int varattno; @@ -1203,7 +1233,8 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, { case RTE_RELATION: /* Ordinary relation RTE */ - expandRelation(rte->relid, rte->eref, rtindex, sublevels_up, + expandRelation(rte->relid, rte->eref, + rtindex, sublevels_up, location, include_dropped, colnames, colvars); break; case RTE_SUBQUERY: @@ -1239,6 +1270,7 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, exprType((Node *) te->expr), exprTypmod((Node *) te->expr), sublevels_up); + varnode->location = location; *colvars = lappend(*colvars, varnode); } @@ -1259,7 +1291,8 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, { /* Composite data type, e.g. a table's row type */ Assert(tupdesc); - expandTupleDesc(tupdesc, rte->eref, rtindex, sublevels_up, + expandTupleDesc(tupdesc, rte->eref, + rtindex, sublevels_up, location, include_dropped, colnames, colvars); } else if (functypclass == TYPEFUNC_SCALAR) @@ -1276,6 +1309,7 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, varnode = makeVar(rtindex, 1, funcrettype, -1, sublevels_up); + varnode->location = location; *colvars = lappend(*colvars, varnode); } @@ -1302,6 +1336,7 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, attrtype, attrtypmod, sublevels_up); + varnode->location = location; *colvars = lappend(*colvars, varnode); } } @@ -1343,6 +1378,7 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, exprType(col), exprTypmod(col), sublevels_up); + varnode->location = location; *colvars = lappend(*colvars, varnode); } } @@ -1401,6 +1437,7 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, exprType(avar), exprTypmod(avar), sublevels_up); + varnode->location = location; *colvars = lappend(*colvars, varnode); } @@ -1417,14 +1454,15 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, */ static void expandRelation(Oid relid, Alias *eref, int rtindex, int sublevels_up, - bool include_dropped, + int location, bool include_dropped, List **colnames, List **colvars) { Relation rel; /* Get the tupledesc and turn it over to expandTupleDesc */ rel = relation_open(relid, AccessShareLock); - expandTupleDesc(rel->rd_att, eref, rtindex, sublevels_up, include_dropped, + expandTupleDesc(rel->rd_att, eref, rtindex, sublevels_up, + location, include_dropped, colnames, colvars); relation_close(rel, AccessShareLock); } @@ -1435,7 +1473,7 @@ expandRelation(Oid relid, Alias *eref, int rtindex, int sublevels_up, static void expandTupleDesc(TupleDesc tupdesc, Alias *eref, int rtindex, int sublevels_up, - bool include_dropped, + int location, bool include_dropped, List **colnames, List **colvars) { int maxattrs = tupdesc->natts; @@ -1482,6 +1520,7 @@ expandTupleDesc(TupleDesc tupdesc, Alias *eref, varnode = makeVar(rtindex, attr->attnum, attr->atttypid, attr->atttypmod, sublevels_up); + varnode->location = location; *colvars = lappend(*colvars, varnode); } @@ -1491,15 +1530,15 @@ expandTupleDesc(TupleDesc tupdesc, Alias *eref, /* * expandRelAttrs - * Workhorse for "*" expansion: produce a list of targetentries - * for the attributes of the rte + * for the attributes of the RTE * * As with expandRTE, rtindex/sublevels_up determine the varno/varlevelsup - * fields of the Vars produced. pstate->p_next_resno determines the resnos - * assigned to the TLEs. + * fields of the Vars produced, and location sets their location. + * pstate->p_next_resno determines the resnos assigned to the TLEs. */ List * expandRelAttrs(ParseState *pstate, RangeTblEntry *rte, - int rtindex, int sublevels_up) + int rtindex, int sublevels_up, int location) { List *names, *vars; @@ -1507,7 +1546,7 @@ expandRelAttrs(ParseState *pstate, RangeTblEntry *rte, *var; List *te_list = NIL; - expandRTE(rte, rtindex, sublevels_up, false, + expandRTE(rte, rtindex, sublevels_up, location, false, &names, &vars); forboth(name, names, var, vars) @@ -1523,7 +1562,7 @@ expandRelAttrs(ParseState *pstate, RangeTblEntry *rte, te_list = lappend(te_list, te); } - Assert(name == NULL && var == NULL); /* lists not the same length? */ + Assert(name == NULL && var == NULL); /* lists not the same length? */ return te_list; } @@ -1966,7 +2005,7 @@ attnumTypeId(Relation rd, int attid) * a warning. */ static void -warnAutoRange(ParseState *pstate, RangeVar *relation, int location) +warnAutoRange(ParseState *pstate, RangeVar *relation) { RangeTblEntry *rte; int sublevels_up; @@ -1991,6 +2030,7 @@ warnAutoRange(ParseState *pstate, RangeVar *relation, int location) if (rte && rte->alias && strcmp(rte->eref->aliasname, relation->relname) != 0 && refnameRangeTblEntry(pstate, NULL, rte->eref->aliasname, + relation->location, &sublevels_up) == rte) badAlias = rte->eref->aliasname; @@ -2006,7 +2046,7 @@ warnAutoRange(ParseState *pstate, RangeVar *relation, int location) badAlias) : errhint("There is an entry for table \"%s\", but it cannot be referenced from this part of the query.", rte->eref->aliasname)), - parser_errposition(pstate, location))); + parser_errposition(pstate, relation->location))); else ereport(ERROR, (errcode(ERRCODE_UNDEFINED_TABLE), @@ -2015,7 +2055,7 @@ warnAutoRange(ParseState *pstate, RangeVar *relation, int location) relation->relname) : errmsg("missing FROM-clause entry for table \"%s\"", relation->relname)), - parser_errposition(pstate, location))); + parser_errposition(pstate, relation->location))); } else { @@ -2033,6 +2073,6 @@ warnAutoRange(ParseState *pstate, RangeVar *relation, int location) (rte ? errhint("There is an entry for table \"%s\", but it cannot be referenced from this part of the query.", rte->eref->aliasname) : 0)), - parser_errposition(pstate, location))); + parser_errposition(pstate, relation->location))); } } diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index df87f3874cc..20d91a142f5 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.163 2008/08/30 01:39:14 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.164 2008/09/01 20:42:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -45,7 +45,7 @@ static Node *transformAssignmentIndirection(ParseState *pstate, int location); static List *ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, bool targetlist); -static List *ExpandAllTables(ParseState *pstate); +static List *ExpandAllTables(ParseState *pstate, int location); static List *ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind, bool targetlist); static int FigureColnameInternal(Node *node, char **name); @@ -836,7 +836,7 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, * need not handle the targetlist==false case here. */ Assert(targetlist); - return ExpandAllTables(pstate); + return ExpandAllTables(pstate, cref->location); } else { @@ -889,11 +889,12 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, break; } - rte = refnameRangeTblEntry(pstate, schemaname, relname, + rte = refnameRangeTblEntry(pstate, schemaname, relname, cref->location, &sublevels_up); if (rte == NULL) - rte = addImplicitRTE(pstate, makeRangeVar(schemaname, relname), - cref->location); + rte = addImplicitRTE(pstate, + makeRangeVar(schemaname, relname, + cref->location)); /* Require read access --- see comments in setTargetTable() */ rte->requiredPerms |= ACL_SELECT; @@ -901,12 +902,13 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, rtindex = RTERangeTablePosn(pstate, rte, &sublevels_up); if (targetlist) - return expandRelAttrs(pstate, rte, rtindex, sublevels_up); + return expandRelAttrs(pstate, rte, rtindex, sublevels_up, + cref->location); else { List *vars; - expandRTE(rte, rtindex, sublevels_up, false, + expandRTE(rte, rtindex, sublevels_up, cref->location, false, NULL, &vars); return vars; } @@ -923,7 +925,7 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, * etc. */ static List * -ExpandAllTables(ParseState *pstate) +ExpandAllTables(ParseState *pstate, int location) { List *target = NIL; ListCell *l; @@ -932,7 +934,8 @@ ExpandAllTables(ParseState *pstate) if (!pstate->p_varnamespace) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("SELECT * with no tables specified is not valid"))); + errmsg("SELECT * with no tables specified is not valid"), + parser_errposition(pstate, location))); foreach(l, pstate->p_varnamespace) { @@ -943,7 +946,8 @@ ExpandAllTables(ParseState *pstate) rte->requiredPerms |= ACL_SELECT; target = list_concat(target, - expandRelAttrs(pstate, rte, rtindex, 0)); + expandRelAttrs(pstate, rte, rtindex, 0, + location)); } return target; @@ -1014,12 +1018,16 @@ ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind, ((Var *) expr)->varattno == InvalidAttrNumber) { Var *var = (Var *) expr; + Var *newvar; + + newvar = makeVar(var->varno, + i + 1, + att->atttypid, + att->atttypmod, + var->varlevelsup); + newvar->location = var->location; - fieldnode = (Node *) makeVar(var->varno, - i + 1, - att->atttypid, - att->atttypmod, - var->varlevelsup); + fieldnode = (Node *) newvar; } else { @@ -1088,7 +1096,7 @@ expandRecordVariable(ParseState *pstate, Var *var, int levelsup) *lvar; int i; - expandRTE(rte, var->varno, 0, false, + expandRTE(rte, var->varno, 0, var->location, false, &names, &vars); tupleDesc = CreateTemplateTupleDesc(list_length(vars), false); diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c index 7f6f1617456..960542029fd 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.98 2008/08/30 01:39:14 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.99 2008/09/01 20:42:45 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -69,7 +69,7 @@ LookupTypeName(ParseState *pstate, const TypeName *typename, else if (typename->pct_type) { /* Handle %TYPE reference to type of an existing field */ - RangeVar *rel = makeRangeVar(NULL, NULL); + RangeVar *rel = makeRangeVar(NULL, NULL, typename->location); char *field = NULL; Oid relid; AttrNumber attnum; @@ -122,7 +122,7 @@ LookupTypeName(ParseState *pstate, const TypeName *typename, /* this construct should never have an array indicator */ Assert(typename->arrayBounds == NIL); - /* emit nuisance notice */ + /* emit nuisance notice (intentionally not errposition'd) */ ereport(NOTICE, (errmsg("type reference %s converted to %s", TypeNameToString(typename), @@ -247,6 +247,7 @@ typenameTypeMod(ParseState *pstate, const TypeName *typename, Type typ) int n; ListCell *l; ArrayType *arrtypmod; + ParseCallbackState pcbstate; /* Return prespecified typmod if no typmod expressions */ if (typename->typmods == NIL) @@ -321,9 +322,14 @@ typenameTypeMod(ParseState *pstate, const TypeName *typename, Type typ) arrtypmod = construct_array(datums, n, CSTRINGOID, -2, false, 'c'); + /* arrange to report location if type's typmodin function fails */ + setup_parser_errposition_callback(&pcbstate, pstate, typename->location); + result = DatumGetInt32(OidFunctionCall1(typmodin, PointerGetDatum(arrtypmod))); + cancel_parser_errposition_callback(&pcbstate); + pfree(datums); pfree(arrtypmod); diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index ae3ddeb2023..4e0efe84bba 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -19,7 +19,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/backend/parser/parse_utilcmd.c,v 2.16 2008/08/28 23:09:48 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_utilcmd.c,v 2.17 2008/09/01 20:42:45 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -346,7 +346,7 @@ transformColumnDefinition(ParseState *pstate, CreateStmtContext *cxt, * TABLE. */ seqstmt = makeNode(CreateSeqStmt); - seqstmt->sequence = makeRangeVar(snamespace, sname); + seqstmt->sequence = makeRangeVar(snamespace, sname, -1); seqstmt->options = NIL; cxt->blist = lappend(cxt->blist, seqstmt); @@ -357,7 +357,7 @@ transformColumnDefinition(ParseState *pstate, CreateStmtContext *cxt, * done after this CREATE/ALTER TABLE. */ altseqstmt = makeNode(AlterSeqStmt); - altseqstmt->sequence = makeRangeVar(snamespace, sname); + altseqstmt->sequence = makeRangeVar(snamespace, sname, -1); attnamelist = list_make3(makeString(snamespace), makeString(cxt->relation->relname), makeString(column->colname)); @@ -548,7 +548,7 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt, bool including_indexes = false; ListCell *elem; - relation = heap_openrv(inhRelation->relation, AccessShareLock); + relation = parserOpenTable(pstate, inhRelation->relation, AccessShareLock); if (relation->rd_rel->relkind != RELKIND_RELATION) ereport(ERROR, diff --git a/src/backend/parser/scan.l b/src/backend/parser/scan.l index 804bfe71272..dec0669d8ba 100644 --- a/src/backend/parser/scan.l +++ b/src/backend/parser/scan.l @@ -24,7 +24,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/scan.l,v 1.145 2008/08/29 13:02:32 petere Exp $ + * $PostgreSQL: pgsql/src/backend/parser/scan.l,v 1.146 2008/09/01 20:42:45 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -77,7 +77,8 @@ static void addlit(char *ytext, int yleng); static void addlitchar(unsigned char ychar); static char *litbufdup(void); -static int lexer_errposition(void); +#define lexer_errposition() scanner_errposition(yylloc) + static void check_escape_warning(void); static void check_string_escape_warning(unsigned char ychar); @@ -756,22 +757,27 @@ other . %% /* - * lexer_errposition - * Report a lexical-analysis-time cursor position, if possible. + * scanner_errposition + * Report a lexer or grammar error cursor position, if possible. * * This is expected to be used within an ereport() call. The return value * is a dummy (always 0, in fact). * - * Note that this can only be used for messages from the lexer itself, - * since it depends on scanbuf to still be valid. + * Note that this can only be used for messages emitted during raw parsing + * (essentially, scan.l and gram.y), since it requires scanbuf to still be + * valid. */ -static int -lexer_errposition(void) +int +scanner_errposition(int location) { int pos; + Assert(scanbuf != NULL); /* else called from wrong place */ + if (location < 0) + return 0; /* no-op if location is unknown */ + /* Convert byte offset to character number */ - pos = pg_mbstrlen_with_len(scanbuf, yylloc) + 1; + pos = pg_mbstrlen_with_len(scanbuf, location) + 1; /* And pass it to the ereport mechanism */ return errposition(pos); } @@ -849,6 +855,7 @@ scanner_finish(void) { yy_delete_buffer(scanbufhandle); pfree(scanbuf); + scanbuf = NULL; } diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c index f75c1888aba..c8652ff5f2d 100644 --- a/src/backend/rewrite/rewriteManip.c +++ b/src/backend/rewrite/rewriteManip.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.112 2008/08/28 23:09:48 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.113 2008/09/01 20:42:45 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -28,8 +28,16 @@ typedef struct int sublevels_up; } contain_aggs_of_level_context; +typedef struct +{ + int agg_location; + int sublevels_up; +} locate_agg_of_level_context; + static bool contain_aggs_of_level_walker(Node *node, contain_aggs_of_level_context *context); +static bool locate_agg_of_level_walker(Node *node, + locate_agg_of_level_context *context); static bool checkExprHasSubLink_walker(Node *node, void *context); static Relids offset_relid_set(Relids relids, int offset); static Relids adjust_relid_set(Relids relids, int oldrelid, int newrelid); @@ -37,7 +45,8 @@ static Relids adjust_relid_set(Relids relids, int oldrelid, int newrelid); /* * checkExprHasAggs - - * Check if an expression contains an aggregate function call. + * Check if an expression contains an aggregate function call of the + * current query level. */ bool checkExprHasAggs(Node *node) @@ -102,6 +111,71 @@ contain_aggs_of_level_walker(Node *node, } /* + * locate_agg_of_level - + * Find the parse location of any aggregate of the specified query level. + * + * Returns -1 if no such agg is in the querytree, or if they all have + * unknown parse location. (The former case is probably caller error, + * but we don't bother to distinguish it from the latter case.) + * + * Note: it might seem appropriate to merge this functionality into + * contain_aggs_of_level, but that would complicate that function's API. + * Currently, the only uses of this function are for error reporting, + * and so shaving cycles probably isn't very important. + */ +int +locate_agg_of_level(Node *node, int levelsup) +{ + locate_agg_of_level_context context; + + context.agg_location = -1; /* in case we find nothing */ + context.sublevels_up = levelsup; + + /* + * Must be prepared to start with a Query or a bare expression tree; if + * it's a Query, we don't want to increment sublevels_up. + */ + (void) query_or_expression_tree_walker(node, + locate_agg_of_level_walker, + (void *) &context, + 0); + + return context.agg_location; +} + +static bool +locate_agg_of_level_walker(Node *node, + locate_agg_of_level_context *context) +{ + if (node == NULL) + return false; + if (IsA(node, Aggref)) + { + if (((Aggref *) node)->agglevelsup == context->sublevels_up && + ((Aggref *) node)->location >= 0) + { + context->agg_location = ((Aggref *) node)->location; + return true; /* abort the tree traversal and return true */ + } + /* else fall through to examine argument */ + } + if (IsA(node, Query)) + { + /* Recurse into subselects */ + bool result; + + context->sublevels_up++; + result = query_tree_walker((Query *) node, + locate_agg_of_level_walker, + (void *) context, 0); + context->sublevels_up--; + return result; + } + return expression_tree_walker(node, locate_agg_of_level_walker, + (void *) context); +} + +/* * checkExprHasSubLink - * Check if an expression contains a SubLink. */ @@ -980,7 +1054,7 @@ ResolveNew_mutator(Node *node, ResolveNew_context *context) * this is a JOIN), then omit dropped columns. */ expandRTE(context->target_rte, - this_varno, this_varlevelsup, + this_varno, this_varlevelsup, var->location, (var->vartype != RECORDOID), NULL, &fields); /* Adjust the generated per-field Vars... */ diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 2f9078c2fba..b867df5581f 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.297 2008/08/30 01:39:14 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.298 2008/09/01 20:42:45 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -801,7 +801,7 @@ ProcessUtility(Node *parsetree, { NotifyStmt *stmt = (NotifyStmt *) parsetree; - Async_Notify(stmt->relation->relname); + Async_Notify(stmt->conditionname); } break; @@ -809,7 +809,7 @@ ProcessUtility(Node *parsetree, { ListenStmt *stmt = (ListenStmt *) parsetree; - Async_Listen(stmt->relation->relname); + Async_Listen(stmt->conditionname); } break; @@ -817,8 +817,8 @@ ProcessUtility(Node *parsetree, { UnlistenStmt *stmt = (UnlistenStmt *) parsetree; - if (stmt->relation) - Async_Unlisten(stmt->relation->relname); + if (stmt->conditionname) + Async_Unlisten(stmt->conditionname); else Async_UnlistenAll(); } diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index bad6fcac694..66bbaaaa68d 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.281 2008/08/25 22:42:34 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.282 2008/09/01 20:42:45 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -2713,8 +2713,7 @@ get_utility_query_def(Query *query, deparse_context *context) appendContextKeyword(context, "", 0, PRETTYINDENT_STD, 1); appendStringInfo(buf, "NOTIFY %s", - quote_qualified_identifier(stmt->relation->schemaname, - stmt->relation->relname)); + quote_identifier(stmt->conditionname)); } else { diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c index 46b7853e50a..d2315fb1f6b 100644 --- a/src/backend/utils/error/elog.c +++ b/src/backend/utils/error/elog.c @@ -42,7 +42,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/error/elog.c,v 1.205 2008/07/09 15:56:49 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/error/elog.c,v 1.206 2008/09/01 20:42:45 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -880,6 +880,23 @@ internalerrquery(const char *query) } /* + * geterrcode --- return the currently set SQLSTATE error code + * + * This is only intended for use in error callback subroutines, since there + * is no other place outside elog.c where the concept is meaningful. + */ +int +geterrcode(void) +{ + ErrorData *edata = &errordata[errordata_stack_depth]; + + /* we don't bother incrementing recursion_depth */ + CHECK_STACK_DEPTH(); + + return edata->sqlerrcode; +} + +/* * geterrposition --- return the currently set error position (0 if none) * * This is only intended for use in error callback subroutines, since there |