1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-14 08:21:07 +03:00

Add a bunch of new error location reports to parse-analysis error messages.

There are still some weak spots around JOIN USING and relation alias lists,
but most errors reported within backend/parser/ now have locations.
This commit is contained in:
Tom Lane
2008-09-01 20:42:46 +00:00
parent 9ac4299163
commit b153c09209
103 changed files with 1877 additions and 485 deletions

View File

@ -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);
}