diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index c96505f8b4e..89fdb94c237 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -105,12 +105,15 @@ typedef struct ImportQual List *table_names; } 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 { Node *limitOffset; 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; /* 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, bool *deferrable, bool *initdeferred, bool *not_valid, 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, core_yyscan_t yyscanner); static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); @@ -3145,11 +3149,13 @@ PartitionBoundSpec: if (n->modulus == -1) ereport(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) ereport(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; @@ -4528,7 +4534,7 @@ PartitionSpec: PARTITION BY ColId '(' part_params ')' { PartitionSpec *n = makeNode(PartitionSpec); - n->strategy = parsePartitionStrategy($3); + n->strategy = parsePartitionStrategy($3, @3, yyscanner); n->partParams = $5; n->location = @1; @@ -5962,7 +5968,8 @@ CreateTrigStmt: if (n->replace) /* not supported, see CreateTrigger */ ereport(ERROR, (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->trigname = $5; n->relation = $9; @@ -6247,7 +6254,8 @@ CreateAssertionStmt: { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("CREATE ASSERTION is not yet implemented"))); + errmsg("CREATE ASSERTION is not yet implemented"), + parser_errposition(@1))); $$ = NULL; } @@ -13156,11 +13164,13 @@ select_limit: { $$ = $1; ($$)->limitOffset = $2; + ($$)->offsetLoc = @2; } | offset_clause limit_clause { $$ = $2; ($$)->limitOffset = $1; + ($$)->offsetLoc = @1; } | limit_clause { @@ -13173,6 +13183,9 @@ select_limit: n->limitOffset = $1; n->limitCount = NULL; n->limitOption = LIMIT_OPTION_COUNT; + n->offsetLoc = @1; + n->countLoc = -1; + n->optionLoc = -1; $$ = n; } ; @@ -13190,6 +13203,9 @@ limit_clause: n->limitOffset = NULL; n->limitCount = $2; n->limitOption = LIMIT_OPTION_COUNT; + n->offsetLoc = -1; + n->countLoc = @1; + n->optionLoc = -1; $$ = n; } | LIMIT select_limit_value ',' select_offset_value @@ -13215,6 +13231,9 @@ limit_clause: n->limitOffset = NULL; n->limitCount = $3; n->limitOption = LIMIT_OPTION_COUNT; + n->offsetLoc = -1; + n->countLoc = @1; + n->optionLoc = -1; $$ = n; } | FETCH first_or_next select_fetch_first_value row_or_rows WITH TIES @@ -13224,6 +13243,9 @@ limit_clause: n->limitOffset = NULL; n->limitCount = $3; n->limitOption = LIMIT_OPTION_WITH_TIES; + n->offsetLoc = -1; + n->countLoc = @1; + n->optionLoc = @5; $$ = n; } | FETCH first_or_next row_or_rows ONLY @@ -13233,6 +13255,9 @@ limit_clause: n->limitOffset = NULL; n->limitCount = makeIntConst(1, -1); n->limitOption = LIMIT_OPTION_COUNT; + n->offsetLoc = -1; + n->countLoc = @1; + n->optionLoc = -1; $$ = n; } | FETCH first_or_next row_or_rows WITH TIES @@ -13242,6 +13267,9 @@ limit_clause: n->limitOffset = NULL; n->limitCount = makeIntConst(1, -1); n->limitOption = LIMIT_OPTION_WITH_TIES; + n->offsetLoc = -1; + n->countLoc = @1; + n->optionLoc = @4; $$ = n; } ; @@ -16954,8 +16982,9 @@ json_format_clause: encoding = JS_ENC_UTF32; else ereport(ERROR, - errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("unrecognized JSON encoding: %s", $4)); + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("unrecognized JSON encoding: %s", $4), + parser_errposition(@4))); $$ = (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) ereport(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->lockingClause = $10; @@ -18962,7 +18992,7 @@ insertSelectOptions(SelectStmt *stmt, ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("multiple OFFSET clauses not allowed"), - parser_errposition(exprLocation(limitClause->limitOffset)))); + parser_errposition(limitClause->offsetLoc))); stmt->limitOffset = limitClause->limitOffset; } if (limitClause && limitClause->limitCount) @@ -18971,19 +19001,18 @@ insertSelectOptions(SelectStmt *stmt, ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("multiple LIMIT clauses not allowed"), - parser_errposition(exprLocation(limitClause->limitCount)))); + parser_errposition(limitClause->countLoc))); stmt->limitCount = limitClause->limitCount; } if (limitClause) { - if (stmt->limitOption) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("multiple limit options not allowed"))); + /* If there was a conflict, we must have detected it above */ + Assert(!stmt->limitOption); if (!stmt->sortClause && limitClause->limitOption == LIMIT_OPTION_WITH_TIES) ereport(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) { ListCell *lc; @@ -18996,7 +19025,8 @@ insertSelectOptions(SelectStmt *stmt, ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), 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; @@ -19425,7 +19455,7 @@ processCASbits(int cas_bits, int location, const char *constrType, * PartitionStrategy representation, or die trying. */ static PartitionStrategy -parsePartitionStrategy(char *strategy) +parsePartitionStrategy(char *strategy, int location, core_yyscan_t yyscanner) { if (pg_strcasecmp(strategy, "list") == 0) return PARTITION_STRATEGY_LIST; @@ -19436,8 +19466,8 @@ parsePartitionStrategy(char *strategy) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("unrecognized partitioning strategy \"%s\"", - strategy))); + errmsg("unrecognized partitioning strategy \"%s\"", strategy), + parser_errposition(location))); return PARTITION_STRATEGY_LIST; /* keep compiler quiet */ } diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index c45e02d42f9..57a24050ab8 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -198,6 +198,8 @@ CREATE TABLE partitioned ( a int ) PARTITION BY MAGIC (a); ERROR: unrecognized partitioning strategy "magic" +LINE 3: ) PARTITION BY MAGIC (a); + ^ -- specified column must be present in the table CREATE TABLE partitioned ( a int diff --git a/src/test/regress/expected/limit.out b/src/test/regress/expected/limit.out index a2cd0f9f5b8..f4267c002d7 100644 --- a/src/test/regress/expected/limit.out +++ b/src/test/regress/expected/limit.out @@ -624,11 +624,15 @@ SELECT thousand FROM onek WHERE thousand < 5 ORDER BY thousand FETCH FIRST 1 ROW WITH TIES FOR UPDATE SKIP LOCKED; 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 SELECT ''::text AS two, unique1, unique2, stringu1 FROM onek WHERE unique1 > 50 FETCH FIRST 2 ROW WITH TIES; ERROR: WITH TIES cannot be specified without ORDER BY clause +LINE 3: FETCH FIRST 2 ROW WITH TIES; + ^ -- test ruleutils 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; diff --git a/src/test/regress/expected/sqljson.out b/src/test/regress/expected/sqljson.out index 70721c9a5ad..435d61dd4a3 100644 --- a/src/test/regress/expected/sqljson.out +++ b/src/test/regress/expected/sqljson.out @@ -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); ERROR: unrecognized JSON encoding: invalid_encoding +LINE 1: ...T JSON_OBJECT(RETURNING text FORMAT JSON ENCODING INVALID_EN... + ^ SELECT JSON_OBJECT(RETURNING bytea); 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); ERROR: unrecognized JSON encoding: invalid_encoding +LINE 1: ...CT JSON_ARRAY(RETURNING text FORMAT JSON ENCODING INVALID_EN... + ^ SELECT JSON_ARRAY(RETURNING bytea); json_array ------------