mirror of
https://github.com/postgres/postgres.git
synced 2025-04-21 12:05:57 +03:00
Make all ereport() calls within gram.y provide error locations.
This patch responds to a comment that I (tgl) made in the discussion leading up to 774171c4f, that really all errors occurring during raw parsing should provide error cursors. Syntax errors reported by Bison will have one, and most of the handwritten ereport's in gram.y already provide one, but there were a few stragglers. (It is not claimed that this handles every failure reachable during raw parsing --- out-of-memory is an obvious exception. But this makes a good start on cases that are likely to occur.) While we're at it, clean up the reported positions for errors associated with LIMIT/OFFSET clauses. Previously we were relying on applying exprLocation() to the contained expressions, but that leads to slightly odd cursor placement, e.g. regression=# (select * from foo limit 10) limit 10; ERROR: multiple LIMIT clauses not allowed LINE 1: (select * from foo limit 10) limit 10; ^ We can afford to keep a little more state in the transient SelectLimit structs in order to make that better. Jian He and Tom Lane (extracted from a larger patch by Jian, with some additional work by me) Discussion: https://postgr.es/m/CACJufxEmONE3P2En=jopZy1m=cCCUs65M4+1o52MW5og9oaUPA@mail.gmail.com
This commit is contained in:
parent
89e51abcb2
commit
2d8bff603c
@ -105,12 +105,15 @@ typedef struct ImportQual
|
|||||||
List *table_names;
|
List *table_names;
|
||||||
} ImportQual;
|
} ImportQual;
|
||||||
|
|
||||||
/* Private struct for the result of opt_select_limit production */
|
/* Private struct for the result of select_limit & limit_clause productions */
|
||||||
typedef struct SelectLimit
|
typedef struct SelectLimit
|
||||||
{
|
{
|
||||||
Node *limitOffset;
|
Node *limitOffset;
|
||||||
Node *limitCount;
|
Node *limitCount;
|
||||||
LimitOption limitOption;
|
LimitOption limitOption; /* indicates presence of WITH TIES */
|
||||||
|
ParseLoc offsetLoc; /* location of OFFSET token, if present */
|
||||||
|
ParseLoc countLoc; /* location of LIMIT/FETCH token, if present */
|
||||||
|
ParseLoc optionLoc; /* location of WITH TIES, if present */
|
||||||
} SelectLimit;
|
} SelectLimit;
|
||||||
|
|
||||||
/* Private struct for the result of group_clause production */
|
/* Private struct for the result of group_clause production */
|
||||||
@ -195,7 +198,8 @@ static void SplitColQualList(List *qualList,
|
|||||||
static void processCASbits(int cas_bits, int location, const char *constrType,
|
static void processCASbits(int cas_bits, int location, const char *constrType,
|
||||||
bool *deferrable, bool *initdeferred, bool *not_valid,
|
bool *deferrable, bool *initdeferred, bool *not_valid,
|
||||||
bool *no_inherit, core_yyscan_t yyscanner);
|
bool *no_inherit, core_yyscan_t yyscanner);
|
||||||
static PartitionStrategy parsePartitionStrategy(char *strategy);
|
static PartitionStrategy parsePartitionStrategy(char *strategy, int location,
|
||||||
|
core_yyscan_t yyscanner);
|
||||||
static void preprocess_pubobj_list(List *pubobjspec_list,
|
static void preprocess_pubobj_list(List *pubobjspec_list,
|
||||||
core_yyscan_t yyscanner);
|
core_yyscan_t yyscanner);
|
||||||
static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
|
static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
|
||||||
@ -3145,11 +3149,13 @@ PartitionBoundSpec:
|
|||||||
if (n->modulus == -1)
|
if (n->modulus == -1)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
errmsg("modulus for hash partition must be specified")));
|
errmsg("modulus for hash partition must be specified"),
|
||||||
|
parser_errposition(@3)));
|
||||||
if (n->remainder == -1)
|
if (n->remainder == -1)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
errmsg("remainder for hash partition must be specified")));
|
errmsg("remainder for hash partition must be specified"),
|
||||||
|
parser_errposition(@3)));
|
||||||
|
|
||||||
n->location = @3;
|
n->location = @3;
|
||||||
|
|
||||||
@ -4528,7 +4534,7 @@ PartitionSpec: PARTITION BY ColId '(' part_params ')'
|
|||||||
{
|
{
|
||||||
PartitionSpec *n = makeNode(PartitionSpec);
|
PartitionSpec *n = makeNode(PartitionSpec);
|
||||||
|
|
||||||
n->strategy = parsePartitionStrategy($3);
|
n->strategy = parsePartitionStrategy($3, @3, yyscanner);
|
||||||
n->partParams = $5;
|
n->partParams = $5;
|
||||||
n->location = @1;
|
n->location = @1;
|
||||||
|
|
||||||
@ -5962,7 +5968,8 @@ CreateTrigStmt:
|
|||||||
if (n->replace) /* not supported, see CreateTrigger */
|
if (n->replace) /* not supported, see CreateTrigger */
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
errmsg("CREATE OR REPLACE CONSTRAINT TRIGGER is not supported")));
|
errmsg("CREATE OR REPLACE CONSTRAINT TRIGGER is not supported"),
|
||||||
|
parser_errposition(@1)));
|
||||||
n->isconstraint = true;
|
n->isconstraint = true;
|
||||||
n->trigname = $5;
|
n->trigname = $5;
|
||||||
n->relation = $9;
|
n->relation = $9;
|
||||||
@ -6247,7 +6254,8 @@ CreateAssertionStmt:
|
|||||||
{
|
{
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
errmsg("CREATE ASSERTION is not yet implemented")));
|
errmsg("CREATE ASSERTION is not yet implemented"),
|
||||||
|
parser_errposition(@1)));
|
||||||
|
|
||||||
$$ = NULL;
|
$$ = NULL;
|
||||||
}
|
}
|
||||||
@ -13156,11 +13164,13 @@ select_limit:
|
|||||||
{
|
{
|
||||||
$$ = $1;
|
$$ = $1;
|
||||||
($$)->limitOffset = $2;
|
($$)->limitOffset = $2;
|
||||||
|
($$)->offsetLoc = @2;
|
||||||
}
|
}
|
||||||
| offset_clause limit_clause
|
| offset_clause limit_clause
|
||||||
{
|
{
|
||||||
$$ = $2;
|
$$ = $2;
|
||||||
($$)->limitOffset = $1;
|
($$)->limitOffset = $1;
|
||||||
|
($$)->offsetLoc = @1;
|
||||||
}
|
}
|
||||||
| limit_clause
|
| limit_clause
|
||||||
{
|
{
|
||||||
@ -13173,6 +13183,9 @@ select_limit:
|
|||||||
n->limitOffset = $1;
|
n->limitOffset = $1;
|
||||||
n->limitCount = NULL;
|
n->limitCount = NULL;
|
||||||
n->limitOption = LIMIT_OPTION_COUNT;
|
n->limitOption = LIMIT_OPTION_COUNT;
|
||||||
|
n->offsetLoc = @1;
|
||||||
|
n->countLoc = -1;
|
||||||
|
n->optionLoc = -1;
|
||||||
$$ = n;
|
$$ = n;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
@ -13190,6 +13203,9 @@ limit_clause:
|
|||||||
n->limitOffset = NULL;
|
n->limitOffset = NULL;
|
||||||
n->limitCount = $2;
|
n->limitCount = $2;
|
||||||
n->limitOption = LIMIT_OPTION_COUNT;
|
n->limitOption = LIMIT_OPTION_COUNT;
|
||||||
|
n->offsetLoc = -1;
|
||||||
|
n->countLoc = @1;
|
||||||
|
n->optionLoc = -1;
|
||||||
$$ = n;
|
$$ = n;
|
||||||
}
|
}
|
||||||
| LIMIT select_limit_value ',' select_offset_value
|
| LIMIT select_limit_value ',' select_offset_value
|
||||||
@ -13215,6 +13231,9 @@ limit_clause:
|
|||||||
n->limitOffset = NULL;
|
n->limitOffset = NULL;
|
||||||
n->limitCount = $3;
|
n->limitCount = $3;
|
||||||
n->limitOption = LIMIT_OPTION_COUNT;
|
n->limitOption = LIMIT_OPTION_COUNT;
|
||||||
|
n->offsetLoc = -1;
|
||||||
|
n->countLoc = @1;
|
||||||
|
n->optionLoc = -1;
|
||||||
$$ = n;
|
$$ = n;
|
||||||
}
|
}
|
||||||
| FETCH first_or_next select_fetch_first_value row_or_rows WITH TIES
|
| FETCH first_or_next select_fetch_first_value row_or_rows WITH TIES
|
||||||
@ -13224,6 +13243,9 @@ limit_clause:
|
|||||||
n->limitOffset = NULL;
|
n->limitOffset = NULL;
|
||||||
n->limitCount = $3;
|
n->limitCount = $3;
|
||||||
n->limitOption = LIMIT_OPTION_WITH_TIES;
|
n->limitOption = LIMIT_OPTION_WITH_TIES;
|
||||||
|
n->offsetLoc = -1;
|
||||||
|
n->countLoc = @1;
|
||||||
|
n->optionLoc = @5;
|
||||||
$$ = n;
|
$$ = n;
|
||||||
}
|
}
|
||||||
| FETCH first_or_next row_or_rows ONLY
|
| FETCH first_or_next row_or_rows ONLY
|
||||||
@ -13233,6 +13255,9 @@ limit_clause:
|
|||||||
n->limitOffset = NULL;
|
n->limitOffset = NULL;
|
||||||
n->limitCount = makeIntConst(1, -1);
|
n->limitCount = makeIntConst(1, -1);
|
||||||
n->limitOption = LIMIT_OPTION_COUNT;
|
n->limitOption = LIMIT_OPTION_COUNT;
|
||||||
|
n->offsetLoc = -1;
|
||||||
|
n->countLoc = @1;
|
||||||
|
n->optionLoc = -1;
|
||||||
$$ = n;
|
$$ = n;
|
||||||
}
|
}
|
||||||
| FETCH first_or_next row_or_rows WITH TIES
|
| FETCH first_or_next row_or_rows WITH TIES
|
||||||
@ -13242,6 +13267,9 @@ limit_clause:
|
|||||||
n->limitOffset = NULL;
|
n->limitOffset = NULL;
|
||||||
n->limitCount = makeIntConst(1, -1);
|
n->limitCount = makeIntConst(1, -1);
|
||||||
n->limitOption = LIMIT_OPTION_WITH_TIES;
|
n->limitOption = LIMIT_OPTION_WITH_TIES;
|
||||||
|
n->offsetLoc = -1;
|
||||||
|
n->countLoc = @1;
|
||||||
|
n->optionLoc = @4;
|
||||||
$$ = n;
|
$$ = n;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
@ -16954,8 +16982,9 @@ json_format_clause:
|
|||||||
encoding = JS_ENC_UTF32;
|
encoding = JS_ENC_UTF32;
|
||||||
else
|
else
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||||
errmsg("unrecognized JSON encoding: %s", $4));
|
errmsg("unrecognized JSON encoding: %s", $4),
|
||||||
|
parser_errposition(@4)));
|
||||||
|
|
||||||
$$ = (Node *) makeJsonFormat(JS_FORMAT_JSON, encoding, @1);
|
$$ = (Node *) makeJsonFormat(JS_FORMAT_JSON, encoding, @1);
|
||||||
}
|
}
|
||||||
@ -17457,7 +17486,8 @@ PLpgSQL_Expr: opt_distinct_clause opt_target_list
|
|||||||
$9->limitOption == LIMIT_OPTION_WITH_TIES)
|
$9->limitOption == LIMIT_OPTION_WITH_TIES)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
errmsg("WITH TIES cannot be specified without ORDER BY clause")));
|
errmsg("WITH TIES cannot be specified without ORDER BY clause"),
|
||||||
|
parser_errposition($9->optionLoc)));
|
||||||
n->limitOption = $9->limitOption;
|
n->limitOption = $9->limitOption;
|
||||||
}
|
}
|
||||||
n->lockingClause = $10;
|
n->lockingClause = $10;
|
||||||
@ -18962,7 +18992,7 @@ insertSelectOptions(SelectStmt *stmt,
|
|||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
errmsg("multiple OFFSET clauses not allowed"),
|
errmsg("multiple OFFSET clauses not allowed"),
|
||||||
parser_errposition(exprLocation(limitClause->limitOffset))));
|
parser_errposition(limitClause->offsetLoc)));
|
||||||
stmt->limitOffset = limitClause->limitOffset;
|
stmt->limitOffset = limitClause->limitOffset;
|
||||||
}
|
}
|
||||||
if (limitClause && limitClause->limitCount)
|
if (limitClause && limitClause->limitCount)
|
||||||
@ -18971,19 +19001,18 @@ insertSelectOptions(SelectStmt *stmt,
|
|||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
errmsg("multiple LIMIT clauses not allowed"),
|
errmsg("multiple LIMIT clauses not allowed"),
|
||||||
parser_errposition(exprLocation(limitClause->limitCount))));
|
parser_errposition(limitClause->countLoc)));
|
||||||
stmt->limitCount = limitClause->limitCount;
|
stmt->limitCount = limitClause->limitCount;
|
||||||
}
|
}
|
||||||
if (limitClause)
|
if (limitClause)
|
||||||
{
|
{
|
||||||
if (stmt->limitOption)
|
/* If there was a conflict, we must have detected it above */
|
||||||
ereport(ERROR,
|
Assert(!stmt->limitOption);
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
||||||
errmsg("multiple limit options not allowed")));
|
|
||||||
if (!stmt->sortClause && limitClause->limitOption == LIMIT_OPTION_WITH_TIES)
|
if (!stmt->sortClause && limitClause->limitOption == LIMIT_OPTION_WITH_TIES)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
errmsg("WITH TIES cannot be specified without ORDER BY clause")));
|
errmsg("WITH TIES cannot be specified without ORDER BY clause"),
|
||||||
|
parser_errposition(limitClause->optionLoc)));
|
||||||
if (limitClause->limitOption == LIMIT_OPTION_WITH_TIES && stmt->lockingClause)
|
if (limitClause->limitOption == LIMIT_OPTION_WITH_TIES && stmt->lockingClause)
|
||||||
{
|
{
|
||||||
ListCell *lc;
|
ListCell *lc;
|
||||||
@ -18996,7 +19025,8 @@ insertSelectOptions(SelectStmt *stmt,
|
|||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
errmsg("%s and %s options cannot be used together",
|
errmsg("%s and %s options cannot be used together",
|
||||||
"SKIP LOCKED", "WITH TIES")));
|
"SKIP LOCKED", "WITH TIES"),
|
||||||
|
parser_errposition(limitClause->optionLoc)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stmt->limitOption = limitClause->limitOption;
|
stmt->limitOption = limitClause->limitOption;
|
||||||
@ -19425,7 +19455,7 @@ processCASbits(int cas_bits, int location, const char *constrType,
|
|||||||
* PartitionStrategy representation, or die trying.
|
* PartitionStrategy representation, or die trying.
|
||||||
*/
|
*/
|
||||||
static PartitionStrategy
|
static PartitionStrategy
|
||||||
parsePartitionStrategy(char *strategy)
|
parsePartitionStrategy(char *strategy, int location, core_yyscan_t yyscanner)
|
||||||
{
|
{
|
||||||
if (pg_strcasecmp(strategy, "list") == 0)
|
if (pg_strcasecmp(strategy, "list") == 0)
|
||||||
return PARTITION_STRATEGY_LIST;
|
return PARTITION_STRATEGY_LIST;
|
||||||
@ -19436,8 +19466,8 @@ parsePartitionStrategy(char *strategy)
|
|||||||
|
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||||
errmsg("unrecognized partitioning strategy \"%s\"",
|
errmsg("unrecognized partitioning strategy \"%s\"", strategy),
|
||||||
strategy)));
|
parser_errposition(location)));
|
||||||
return PARTITION_STRATEGY_LIST; /* keep compiler quiet */
|
return PARTITION_STRATEGY_LIST; /* keep compiler quiet */
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -198,6 +198,8 @@ CREATE TABLE partitioned (
|
|||||||
a int
|
a int
|
||||||
) PARTITION BY MAGIC (a);
|
) PARTITION BY MAGIC (a);
|
||||||
ERROR: unrecognized partitioning strategy "magic"
|
ERROR: unrecognized partitioning strategy "magic"
|
||||||
|
LINE 3: ) PARTITION BY MAGIC (a);
|
||||||
|
^
|
||||||
-- specified column must be present in the table
|
-- specified column must be present in the table
|
||||||
CREATE TABLE partitioned (
|
CREATE TABLE partitioned (
|
||||||
a int
|
a int
|
||||||
|
@ -624,11 +624,15 @@ SELECT thousand
|
|||||||
FROM onek WHERE thousand < 5
|
FROM onek WHERE thousand < 5
|
||||||
ORDER BY thousand FETCH FIRST 1 ROW WITH TIES FOR UPDATE SKIP LOCKED;
|
ORDER BY thousand FETCH FIRST 1 ROW WITH TIES FOR UPDATE SKIP LOCKED;
|
||||||
ERROR: SKIP LOCKED and WITH TIES options cannot be used together
|
ERROR: SKIP LOCKED and WITH TIES options cannot be used together
|
||||||
|
LINE 3: ORDER BY thousand FETCH FIRST 1 ROW WITH TIES FOR UPDATE S...
|
||||||
|
^
|
||||||
-- should fail
|
-- should fail
|
||||||
SELECT ''::text AS two, unique1, unique2, stringu1
|
SELECT ''::text AS two, unique1, unique2, stringu1
|
||||||
FROM onek WHERE unique1 > 50
|
FROM onek WHERE unique1 > 50
|
||||||
FETCH FIRST 2 ROW WITH TIES;
|
FETCH FIRST 2 ROW WITH TIES;
|
||||||
ERROR: WITH TIES cannot be specified without ORDER BY clause
|
ERROR: WITH TIES cannot be specified without ORDER BY clause
|
||||||
|
LINE 3: FETCH FIRST 2 ROW WITH TIES;
|
||||||
|
^
|
||||||
-- test ruleutils
|
-- test ruleutils
|
||||||
CREATE VIEW limit_thousand_v_1 AS SELECT thousand FROM onek WHERE thousand < 995
|
CREATE VIEW limit_thousand_v_1 AS SELECT thousand FROM onek WHERE thousand < 995
|
||||||
ORDER BY thousand FETCH FIRST 5 ROWS WITH TIES OFFSET 10;
|
ORDER BY thousand FETCH FIRST 5 ROWS WITH TIES OFFSET 10;
|
||||||
|
@ -337,6 +337,8 @@ LINE 1: SELECT JSON_OBJECT(RETURNING text FORMAT JSON ENCODING UTF8)...
|
|||||||
^
|
^
|
||||||
SELECT JSON_OBJECT(RETURNING text FORMAT JSON ENCODING INVALID_ENCODING);
|
SELECT JSON_OBJECT(RETURNING text FORMAT JSON ENCODING INVALID_ENCODING);
|
||||||
ERROR: unrecognized JSON encoding: invalid_encoding
|
ERROR: unrecognized JSON encoding: invalid_encoding
|
||||||
|
LINE 1: ...T JSON_OBJECT(RETURNING text FORMAT JSON ENCODING INVALID_EN...
|
||||||
|
^
|
||||||
SELECT JSON_OBJECT(RETURNING bytea);
|
SELECT JSON_OBJECT(RETURNING bytea);
|
||||||
json_object
|
json_object
|
||||||
-------------
|
-------------
|
||||||
@ -620,6 +622,8 @@ LINE 1: SELECT JSON_ARRAY(RETURNING text FORMAT JSON ENCODING UTF8);
|
|||||||
^
|
^
|
||||||
SELECT JSON_ARRAY(RETURNING text FORMAT JSON ENCODING INVALID_ENCODING);
|
SELECT JSON_ARRAY(RETURNING text FORMAT JSON ENCODING INVALID_ENCODING);
|
||||||
ERROR: unrecognized JSON encoding: invalid_encoding
|
ERROR: unrecognized JSON encoding: invalid_encoding
|
||||||
|
LINE 1: ...CT JSON_ARRAY(RETURNING text FORMAT JSON ENCODING INVALID_EN...
|
||||||
|
^
|
||||||
SELECT JSON_ARRAY(RETURNING bytea);
|
SELECT JSON_ARRAY(RETURNING bytea);
|
||||||
json_array
|
json_array
|
||||||
------------
|
------------
|
||||||
|
Loading…
x
Reference in New Issue
Block a user