make all ereport in gram.y print out relative location
hi.
it would be better to make all ereport in gram.y print out the
relative location.
one benefit is quickly locating the error.
in insertSelectOptions, some ereport won't call parser_errposition.
To handle that
I added a location to struct SelectLimit, so we can catch the location
in the LIMIT/FETCH/OFFSET clause.
in insertSelectOptions the error is
errmsg("WITH TIES cannot be specified without
ORDER BY clause"),
errmsg("%s and %s options cannot be used
together","SKIP LOCKED", "WITH TIES"),
to capture the exact location, i use the following way:
| FETCH first_or_next select_fetch_first_value
row_or_rows WITH TIES
@@ -13222,6 +13247,7 @@ limit_clause:
n->limitOffset = NULL;
n->limitCount = $3;
n->limitOption = LIMIT_OPTION_WITH_TIES;
+ n->location = @5;
$$ = n;
but normally we do `n->location = @1;`
not sure my change in insertSelectOptions is correct.
For other places in gram.y, I guess my change is sane.
i manually checked, after my patch, all ereport function calls in
gram.y will print out the relative location.
Attachments:
v1-0001-add-cursor-position-in-various-ereport-function-i.patchapplication/x-patch; name=v1-0001-add-cursor-position-in-various-ereport-function-i.patchDownload
From c8f95f5b681b6871e76b25bc2619ad9cbcc3f688 Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Sat, 26 Oct 2024 23:04:22 +0800
Subject: [PATCH v1 1/1] add cursor position in various ereport function in
gram.y
inspired by this[1],
[1]: https://www.postgresql.org/message-id/3482022.1728839631%40sss.pgh.pa.us
but i am not so sure the handing in insertSelectOptions is correct.
discussion:
---
src/backend/parser/gram.y | 90 +++++++++++--------
src/include/nodes/parsenodes.h | 1 +
.../regress/expected/create_function_sql.out | 11 +++
src/test/regress/expected/create_table.out | 2 +
src/test/regress/expected/foreign_key.out | 4 +
src/test/regress/expected/limit.out | 4 +
src/test/regress/expected/sqljson.out | 4 +
src/test/regress/sql/create_function_sql.sql | 7 ++
src/test/regress/sql/foreign_key.sql | 1 +
9 files changed, 86 insertions(+), 38 deletions(-)
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index baca4059d2..33f4cdb0b0 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -115,6 +115,7 @@ typedef struct SelectLimit
Node *limitOffset;
Node *limitCount;
LimitOption limitOption;
+ ParseLoc location;
} SelectLimit;
/* Private struct for the result of group_clause production */
@@ -188,7 +189,7 @@ static Node *makeSQLValueFunction(SQLValueFunctionOp op, int32 typmod,
int location);
static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args,
List *args, int location);
-static List *mergeTableFuncParameters(List *func_args, List *columns);
+static List *mergeTableFuncParameters(List *func_args, List *columns, core_yyscan_t yyscanner);
static TypeName *TableFuncTypeName(List *columns);
static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_t yyscanner);
static RangeVar *makeRangeVarFromQualifiedName(char *name, List *namelist, int location,
@@ -199,7 +200,6 @@ 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 void preprocess_pubobj_list(List *pubobjspec_list,
core_yyscan_t yyscanner);
static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
@@ -3149,11 +3149,14 @@ 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(@4)));
+
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(@4)));
n->location = @3;
@@ -4532,7 +4535,18 @@ PartitionSpec: PARTITION BY ColId '(' part_params ')'
{
PartitionSpec *n = makeNode(PartitionSpec);
- n->strategy = parsePartitionStrategy($3);
+ if (!pg_strcasecmp($3, "list"))
+ n->strategy = PARTITION_STRATEGY_LIST;
+ else if (!pg_strcasecmp($3, "range"))
+ n->strategy = PARTITION_STRATEGY_RANGE;
+ else if (!pg_strcasecmp($3, "hash"))
+ n->strategy = PARTITION_STRATEGY_HASH;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("unrecognized partitioning strategy \"%s\"", $3),
+ parser_errposition(@3)));
+
n->partParams = $5;
n->location = @1;
@@ -5966,7 +5980,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(@2)));
n->isconstraint = true;
n->trigname = $5;
n->relation = $9;
@@ -6251,7 +6266,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(@2)));
$$ = NULL;
}
@@ -8294,7 +8310,7 @@ CreateFunctionStmt:
n->is_procedure = false;
n->replace = $2;
n->funcname = $4;
- n->parameters = mergeTableFuncParameters($5, $9);
+ n->parameters = mergeTableFuncParameters($5, $9, yyscanner);
n->returnType = TableFuncTypeName($9);
n->returnType->location = @7;
n->options = $11;
@@ -8427,6 +8443,7 @@ func_arg:
n->argType = $3;
n->mode = $1;
n->defexpr = NULL;
+ n->location = @1;
$$ = n;
}
| param_name arg_class func_type
@@ -8437,6 +8454,7 @@ func_arg:
n->argType = $3;
n->mode = $2;
n->defexpr = NULL;
+ n->location = @1;
$$ = n;
}
| param_name func_type
@@ -8447,6 +8465,7 @@ func_arg:
n->argType = $2;
n->mode = FUNC_PARAM_DEFAULT;
n->defexpr = NULL;
+ n->location = @1;
$$ = n;
}
| arg_class func_type
@@ -8457,6 +8476,7 @@ func_arg:
n->argType = $2;
n->mode = $1;
n->defexpr = NULL;
+ n->location = @1;
$$ = n;
}
| func_type
@@ -8467,6 +8487,7 @@ func_arg:
n->argType = $1;
n->mode = FUNC_PARAM_DEFAULT;
n->defexpr = NULL;
+ n->location = @1;
$$ = n;
}
;
@@ -8803,6 +8824,7 @@ table_func_column: param_name func_type
n->argType = $2;
n->mode = FUNC_PARAM_TABLE;
n->defexpr = NULL;
+ n->location = @1;
$$ = n;
}
;
@@ -13171,6 +13193,7 @@ select_limit:
n->limitOffset = $1;
n->limitCount = NULL;
n->limitOption = LIMIT_OPTION_COUNT;
+ n->location = @1;
$$ = n;
}
;
@@ -13188,6 +13211,7 @@ limit_clause:
n->limitOffset = NULL;
n->limitCount = $2;
n->limitOption = LIMIT_OPTION_COUNT;
+ n->location = @1;
$$ = n;
}
| LIMIT select_limit_value ',' select_offset_value
@@ -13213,6 +13237,7 @@ limit_clause:
n->limitOffset = NULL;
n->limitCount = $3;
n->limitOption = LIMIT_OPTION_COUNT;
+ n->location = @1;
$$ = n;
}
| FETCH first_or_next select_fetch_first_value row_or_rows WITH TIES
@@ -13222,6 +13247,7 @@ limit_clause:
n->limitOffset = NULL;
n->limitCount = $3;
n->limitOption = LIMIT_OPTION_WITH_TIES;
+ n->location = @5;
$$ = n;
}
| FETCH first_or_next row_or_rows ONLY
@@ -13231,6 +13257,7 @@ limit_clause:
n->limitOffset = NULL;
n->limitCount = makeIntConst(1, -1);
n->limitOption = LIMIT_OPTION_COUNT;
+ n->location = @1;
$$ = n;
}
| FETCH first_or_next row_or_rows WITH TIES
@@ -13240,6 +13267,7 @@ limit_clause:
n->limitOffset = NULL;
n->limitCount = makeIntConst(1, -1);
n->limitOption = LIMIT_OPTION_WITH_TIES;
+ n->location = @1;
$$ = n;
}
;
@@ -16952,8 +16980,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);
}
@@ -17455,7 +17484,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)));
n->limitOption = $9->limitOption;
}
n->lockingClause = $10;
@@ -18977,11 +19007,15 @@ insertSelectOptions(SelectStmt *stmt,
if (stmt->limitOption)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("multiple limit options not allowed")));
+ errmsg("multiple limit options not allowed"),
+ parser_errposition(limitClause->location)));
+
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->location)));
+
if (limitClause->limitOption == LIMIT_OPTION_WITH_TIES && stmt->lockingClause)
{
ListCell *lc;
@@ -18994,7 +19028,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->location)));
}
}
stmt->limitOption = limitClause->limitOption;
@@ -19187,7 +19222,7 @@ makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args,
* Merge the input and output parameters of a table function.
*/
static List *
-mergeTableFuncParameters(List *func_args, List *columns)
+mergeTableFuncParameters(List *func_args, List *columns, core_yyscan_t yyscanner)
{
ListCell *lc;
@@ -19201,7 +19236,8 @@ mergeTableFuncParameters(List *func_args, List *columns)
p->mode != FUNC_PARAM_VARIADIC)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("OUT and INOUT arguments aren't allowed in TABLE functions")));
+ errmsg("OUT and INOUT arguments aren't allowed in TABLE functions"),
+ parser_errposition(p->location)));
}
return list_concat(func_args, columns);
@@ -19417,28 +19453,6 @@ processCASbits(int cas_bits, int location, const char *constrType,
}
}
-/*
- * Parse a user-supplied partition strategy string into parse node
- * PartitionStrategy representation, or die trying.
- */
-static PartitionStrategy
-parsePartitionStrategy(char *strategy)
-{
- if (pg_strcasecmp(strategy, "list") == 0)
- return PARTITION_STRATEGY_LIST;
- else if (pg_strcasecmp(strategy, "range") == 0)
- return PARTITION_STRATEGY_RANGE;
- else if (pg_strcasecmp(strategy, "hash") == 0)
- return PARTITION_STRATEGY_HASH;
-
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("unrecognized partitioning strategy \"%s\"",
- strategy)));
- return PARTITION_STRATEGY_LIST; /* keep compiler quiet */
-
-}
-
/*
* Process pubobjspec_list to check for errors in any of the objects and
* convert PUBLICATIONOBJ_CONTINUATION into appropriate PublicationObjSpecType.
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index b40b661ec8..0d96db5638 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -3482,6 +3482,7 @@ typedef struct FunctionParameter
TypeName *argType; /* TypeName for parameter type */
FunctionParameterMode mode; /* IN/OUT/etc */
Node *defexpr; /* raw default expr, or NULL if not given */
+ ParseLoc location; /* token location, or -1 if unknown */
} FunctionParameter;
typedef struct AlterFunctionStmt
diff --git a/src/test/regress/expected/create_function_sql.out b/src/test/regress/expected/create_function_sql.out
index 50aca5940f..a44986afa3 100644
--- a/src/test/regress/expected/create_function_sql.out
+++ b/src/test/regress/expected/create_function_sql.out
@@ -49,6 +49,17 @@ SELECT functest_A_3();
f
(1 row)
+-- OUT and INOUT arguments aren't allowed in TABLE functions
+CREATE FUNCTION fn_intout(out c int)
+RETURNS TABLE(a int) LANGUAGE sql AS $BODY$ select 1 $BODY$;
+ERROR: OUT and INOUT arguments aren't allowed in TABLE functions
+LINE 1: CREATE FUNCTION fn_intout(out c int)
+ ^
+CREATE FUNCTION fn_intout(inout c int)
+RETURNS TABLE(a int) LANGUAGE sql AS $BODY$ select 1 $BODY$;
+ERROR: OUT and INOUT arguments aren't allowed in TABLE functions
+LINE 1: CREATE FUNCTION fn_intout(inout c int)
+ ^
--
-- IMMUTABLE | STABLE | VOLATILE
--
diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out
index c45e02d42f..57a24050ab 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/foreign_key.out b/src/test/regress/expected/foreign_key.out
index b73e7dced8..ed16dea924 100644
--- a/src/test/regress/expected/foreign_key.out
+++ b/src/test/regress/expected/foreign_key.out
@@ -2916,6 +2916,10 @@ $$;
CREATE TRIGGER trig_upd_pk AFTER UPDATE ON fkpart11.pk FOR EACH ROW EXECUTE FUNCTION fkpart11.print_row();
CREATE TRIGGER trig_del_pk AFTER DELETE ON fkpart11.pk FOR EACH ROW EXECUTE FUNCTION fkpart11.print_row();
CREATE TRIGGER trig_ins_pk AFTER INSERT ON fkpart11.pk FOR EACH ROW EXECUTE FUNCTION fkpart11.print_row();
+CREATE OR REPLACE CONSTRAINT TRIGGER trig_upd_fk_parted AFTER UPDATE ON fkpart11.fk_parted INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION fkpart11.print_row();
+ERROR: CREATE OR REPLACE CONSTRAINT TRIGGER is not supported
+LINE 1: CREATE OR REPLACE CONSTRAINT TRIGGER trig_upd_fk_parted AFTE...
+ ^
CREATE CONSTRAINT TRIGGER trig_upd_fk_parted AFTER UPDATE ON fkpart11.fk_parted INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION fkpart11.print_row();
CREATE CONSTRAINT TRIGGER trig_del_fk_parted AFTER DELETE ON fkpart11.fk_parted INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION fkpart11.print_row();
CREATE CONSTRAINT TRIGGER trig_ins_fk_parted AFTER INSERT ON fkpart11.fk_parted INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION fkpart11.print_row();
diff --git a/src/test/regress/expected/limit.out b/src/test/regress/expected/limit.out
index a2cd0f9f5b..f4267c002d 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 70721c9a5a..435d61dd4a 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
------------
diff --git a/src/test/regress/sql/create_function_sql.sql b/src/test/regress/sql/create_function_sql.sql
index 89e9af3a49..384a7c0c3a 100644
--- a/src/test/regress/sql/create_function_sql.sql
+++ b/src/test/regress/sql/create_function_sql.sql
@@ -35,6 +35,13 @@ SELECT functest_A_1('abcd', '2020-01-01');
SELECT functest_A_2(ARRAY['1', '2', '3']);
SELECT functest_A_3();
+-- OUT and INOUT arguments aren't allowed in TABLE functions
+CREATE FUNCTION fn_intout(out c int)
+RETURNS TABLE(a int) LANGUAGE sql AS $BODY$ select 1 $BODY$;
+
+CREATE FUNCTION fn_intout(inout c int)
+RETURNS TABLE(a int) LANGUAGE sql AS $BODY$ select 1 $BODY$;
+
--
-- IMMUTABLE | STABLE | VOLATILE
--
diff --git a/src/test/regress/sql/foreign_key.sql b/src/test/regress/sql/foreign_key.sql
index 9b2a6b6bff..393e8524f8 100644
--- a/src/test/regress/sql/foreign_key.sql
+++ b/src/test/regress/sql/foreign_key.sql
@@ -2079,6 +2079,7 @@ $$;
CREATE TRIGGER trig_upd_pk AFTER UPDATE ON fkpart11.pk FOR EACH ROW EXECUTE FUNCTION fkpart11.print_row();
CREATE TRIGGER trig_del_pk AFTER DELETE ON fkpart11.pk FOR EACH ROW EXECUTE FUNCTION fkpart11.print_row();
CREATE TRIGGER trig_ins_pk AFTER INSERT ON fkpart11.pk FOR EACH ROW EXECUTE FUNCTION fkpart11.print_row();
+CREATE OR REPLACE CONSTRAINT TRIGGER trig_upd_fk_parted AFTER UPDATE ON fkpart11.fk_parted INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION fkpart11.print_row();
CREATE CONSTRAINT TRIGGER trig_upd_fk_parted AFTER UPDATE ON fkpart11.fk_parted INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION fkpart11.print_row();
CREATE CONSTRAINT TRIGGER trig_del_fk_parted AFTER DELETE ON fkpart11.fk_parted INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION fkpart11.print_row();
CREATE CONSTRAINT TRIGGER trig_ins_fk_parted AFTER INSERT ON fkpart11.fk_parted INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION fkpart11.print_row();
--
2.34.1
Why remove parsePartitionStrategy? You could just add an "int location"
parameter to it.
--
Álvaro Herrera PostgreSQL Developer — https://www.EnterpriseDB.com/
"Los cuentos de hadas no dan al niño su primera idea sobre los monstruos.
Lo que le dan es su primera idea de la posible derrota del monstruo."
(G. K. Chesterton)
On Tue, Oct 29, 2024 at 10:49 PM Alvaro Herrera <alvherre@alvh.no-ip.org> wrote:
Why remove parsePartitionStrategy? You could just add an "int location"
parameter to it.
now changed to
static PartitionStrategy parsePartitionStrategy(char *strategy, int location,
core_yyscan_t yyscanner);
Attachments:
v2-0001-add-cursor-position-in-various-ereport-function-i.patchtext/x-patch; charset=US-ASCII; name=v2-0001-add-cursor-position-in-various-ereport-function-i.patchDownload
From 2ec4466576778e42d3936252c90fc1936082c640 Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Wed, 30 Oct 2024 09:53:15 +0800
Subject: [PATCH v2 1/1] add cursor position in various ereport function in
gram.y
inspired by this[1]
[1]: https://postgr.es/m/3482022.1728839631%40sss.pgh.pa.us
but i am not so sure the handing in insertSelectOptions is correct.
discussion: https://postgr.es/m/CACJufxEmONE3P2En=jopZy1m=cCCUs65M4+1o52MW5og9oaUPA@mail.gmail.com
---
src/backend/parser/gram.y | 66 +++++++++++++------
src/include/nodes/parsenodes.h | 1 +
.../regress/expected/create_function_sql.out | 11 ++++
src/test/regress/expected/create_table.out | 2 +
src/test/regress/expected/foreign_key.out | 4 ++
src/test/regress/expected/limit.out | 4 ++
src/test/regress/expected/sqljson.out | 4 ++
src/test/regress/sql/create_function_sql.sql | 7 ++
src/test/regress/sql/foreign_key.sql | 1 +
9 files changed, 81 insertions(+), 19 deletions(-)
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dd458182f0..d1cb8a84c4 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -111,6 +111,7 @@ typedef struct SelectLimit
Node *limitOffset;
Node *limitCount;
LimitOption limitOption;
+ ParseLoc location;
} SelectLimit;
/* Private struct for the result of group_clause production */
@@ -184,7 +185,7 @@ static Node *makeSQLValueFunction(SQLValueFunctionOp op, int32 typmod,
int location);
static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args,
List *args, int location);
-static List *mergeTableFuncParameters(List *func_args, List *columns);
+static List *mergeTableFuncParameters(List *func_args, List *columns, core_yyscan_t yyscanner);
static TypeName *TableFuncTypeName(List *columns);
static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_t yyscanner);
static RangeVar *makeRangeVarFromQualifiedName(char *name, List *namelist, int location,
@@ -195,7 +196,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 +3147,14 @@ 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(@4)));
+
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(@4)));
n->location = @3;
@@ -4528,7 +4533,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 +5967,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(@2)));
n->isconstraint = true;
n->trigname = $5;
n->relation = $9;
@@ -6247,7 +6253,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(@2)));
$$ = NULL;
}
@@ -8290,7 +8297,7 @@ CreateFunctionStmt:
n->is_procedure = false;
n->replace = $2;
n->funcname = $4;
- n->parameters = mergeTableFuncParameters($5, $9);
+ n->parameters = mergeTableFuncParameters($5, $9, yyscanner);
n->returnType = TableFuncTypeName($9);
n->returnType->location = @7;
n->options = $11;
@@ -8423,6 +8430,7 @@ func_arg:
n->argType = $3;
n->mode = $1;
n->defexpr = NULL;
+ n->location = @1;
$$ = n;
}
| param_name arg_class func_type
@@ -8433,6 +8441,7 @@ func_arg:
n->argType = $3;
n->mode = $2;
n->defexpr = NULL;
+ n->location = @1;
$$ = n;
}
| param_name func_type
@@ -8443,6 +8452,7 @@ func_arg:
n->argType = $2;
n->mode = FUNC_PARAM_DEFAULT;
n->defexpr = NULL;
+ n->location = @1;
$$ = n;
}
| arg_class func_type
@@ -8453,6 +8463,7 @@ func_arg:
n->argType = $2;
n->mode = $1;
n->defexpr = NULL;
+ n->location = @1;
$$ = n;
}
| func_type
@@ -8463,6 +8474,7 @@ func_arg:
n->argType = $1;
n->mode = FUNC_PARAM_DEFAULT;
n->defexpr = NULL;
+ n->location = @1;
$$ = n;
}
;
@@ -8799,6 +8811,7 @@ table_func_column: param_name func_type
n->argType = $2;
n->mode = FUNC_PARAM_TABLE;
n->defexpr = NULL;
+ n->location = @1;
$$ = n;
}
;
@@ -13167,6 +13180,7 @@ select_limit:
n->limitOffset = $1;
n->limitCount = NULL;
n->limitOption = LIMIT_OPTION_COUNT;
+ n->location = @1;
$$ = n;
}
;
@@ -13184,6 +13198,7 @@ limit_clause:
n->limitOffset = NULL;
n->limitCount = $2;
n->limitOption = LIMIT_OPTION_COUNT;
+ n->location = @1;
$$ = n;
}
| LIMIT select_limit_value ',' select_offset_value
@@ -13209,6 +13224,7 @@ limit_clause:
n->limitOffset = NULL;
n->limitCount = $3;
n->limitOption = LIMIT_OPTION_COUNT;
+ n->location = @1;
$$ = n;
}
| FETCH first_or_next select_fetch_first_value row_or_rows WITH TIES
@@ -13218,6 +13234,7 @@ limit_clause:
n->limitOffset = NULL;
n->limitCount = $3;
n->limitOption = LIMIT_OPTION_WITH_TIES;
+ n->location = @5;
$$ = n;
}
| FETCH first_or_next row_or_rows ONLY
@@ -13227,6 +13244,7 @@ limit_clause:
n->limitOffset = NULL;
n->limitCount = makeIntConst(1, -1);
n->limitOption = LIMIT_OPTION_COUNT;
+ n->location = @1;
$$ = n;
}
| FETCH first_or_next row_or_rows WITH TIES
@@ -13236,6 +13254,7 @@ limit_clause:
n->limitOffset = NULL;
n->limitCount = makeIntConst(1, -1);
n->limitOption = LIMIT_OPTION_WITH_TIES;
+ n->location = @1;
$$ = n;
}
;
@@ -16948,8 +16967,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);
}
@@ -17451,7 +17471,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)));
n->limitOption = $9->limitOption;
}
n->lockingClause = $10;
@@ -18973,11 +18994,15 @@ insertSelectOptions(SelectStmt *stmt,
if (stmt->limitOption)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("multiple limit options not allowed")));
+ errmsg("multiple limit options not allowed"),
+ parser_errposition(limitClause->location)));
+
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->location)));
+
if (limitClause->limitOption == LIMIT_OPTION_WITH_TIES && stmt->lockingClause)
{
ListCell *lc;
@@ -18990,7 +19015,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->location)));
}
}
stmt->limitOption = limitClause->limitOption;
@@ -19183,7 +19209,7 @@ makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args,
* Merge the input and output parameters of a table function.
*/
static List *
-mergeTableFuncParameters(List *func_args, List *columns)
+mergeTableFuncParameters(List *func_args, List *columns, core_yyscan_t yyscanner)
{
ListCell *lc;
@@ -19197,7 +19223,8 @@ mergeTableFuncParameters(List *func_args, List *columns)
p->mode != FUNC_PARAM_VARIADIC)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("OUT and INOUT arguments aren't allowed in TABLE functions")));
+ errmsg("OUT and INOUT arguments aren't allowed in TABLE functions"),
+ parser_errposition(p->location)));
}
return list_concat(func_args, columns);
@@ -19418,7 +19445,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;
@@ -19429,8 +19456,9 @@ 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/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index b40b661ec8..0d96db5638 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -3482,6 +3482,7 @@ typedef struct FunctionParameter
TypeName *argType; /* TypeName for parameter type */
FunctionParameterMode mode; /* IN/OUT/etc */
Node *defexpr; /* raw default expr, or NULL if not given */
+ ParseLoc location; /* token location, or -1 if unknown */
} FunctionParameter;
typedef struct AlterFunctionStmt
diff --git a/src/test/regress/expected/create_function_sql.out b/src/test/regress/expected/create_function_sql.out
index 50aca5940f..a44986afa3 100644
--- a/src/test/regress/expected/create_function_sql.out
+++ b/src/test/regress/expected/create_function_sql.out
@@ -49,6 +49,17 @@ SELECT functest_A_3();
f
(1 row)
+-- OUT and INOUT arguments aren't allowed in TABLE functions
+CREATE FUNCTION fn_intout(out c int)
+RETURNS TABLE(a int) LANGUAGE sql AS $BODY$ select 1 $BODY$;
+ERROR: OUT and INOUT arguments aren't allowed in TABLE functions
+LINE 1: CREATE FUNCTION fn_intout(out c int)
+ ^
+CREATE FUNCTION fn_intout(inout c int)
+RETURNS TABLE(a int) LANGUAGE sql AS $BODY$ select 1 $BODY$;
+ERROR: OUT and INOUT arguments aren't allowed in TABLE functions
+LINE 1: CREATE FUNCTION fn_intout(inout c int)
+ ^
--
-- IMMUTABLE | STABLE | VOLATILE
--
diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out
index c45e02d42f..57a24050ab 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/foreign_key.out b/src/test/regress/expected/foreign_key.out
index b73e7dced8..ed16dea924 100644
--- a/src/test/regress/expected/foreign_key.out
+++ b/src/test/regress/expected/foreign_key.out
@@ -2916,6 +2916,10 @@ $$;
CREATE TRIGGER trig_upd_pk AFTER UPDATE ON fkpart11.pk FOR EACH ROW EXECUTE FUNCTION fkpart11.print_row();
CREATE TRIGGER trig_del_pk AFTER DELETE ON fkpart11.pk FOR EACH ROW EXECUTE FUNCTION fkpart11.print_row();
CREATE TRIGGER trig_ins_pk AFTER INSERT ON fkpart11.pk FOR EACH ROW EXECUTE FUNCTION fkpart11.print_row();
+CREATE OR REPLACE CONSTRAINT TRIGGER trig_upd_fk_parted AFTER UPDATE ON fkpart11.fk_parted INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION fkpart11.print_row();
+ERROR: CREATE OR REPLACE CONSTRAINT TRIGGER is not supported
+LINE 1: CREATE OR REPLACE CONSTRAINT TRIGGER trig_upd_fk_parted AFTE...
+ ^
CREATE CONSTRAINT TRIGGER trig_upd_fk_parted AFTER UPDATE ON fkpart11.fk_parted INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION fkpart11.print_row();
CREATE CONSTRAINT TRIGGER trig_del_fk_parted AFTER DELETE ON fkpart11.fk_parted INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION fkpart11.print_row();
CREATE CONSTRAINT TRIGGER trig_ins_fk_parted AFTER INSERT ON fkpart11.fk_parted INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION fkpart11.print_row();
diff --git a/src/test/regress/expected/limit.out b/src/test/regress/expected/limit.out
index a2cd0f9f5b..f4267c002d 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 70721c9a5a..435d61dd4a 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
------------
diff --git a/src/test/regress/sql/create_function_sql.sql b/src/test/regress/sql/create_function_sql.sql
index 89e9af3a49..384a7c0c3a 100644
--- a/src/test/regress/sql/create_function_sql.sql
+++ b/src/test/regress/sql/create_function_sql.sql
@@ -35,6 +35,13 @@ SELECT functest_A_1('abcd', '2020-01-01');
SELECT functest_A_2(ARRAY['1', '2', '3']);
SELECT functest_A_3();
+-- OUT and INOUT arguments aren't allowed in TABLE functions
+CREATE FUNCTION fn_intout(out c int)
+RETURNS TABLE(a int) LANGUAGE sql AS $BODY$ select 1 $BODY$;
+
+CREATE FUNCTION fn_intout(inout c int)
+RETURNS TABLE(a int) LANGUAGE sql AS $BODY$ select 1 $BODY$;
+
--
-- IMMUTABLE | STABLE | VOLATILE
--
diff --git a/src/test/regress/sql/foreign_key.sql b/src/test/regress/sql/foreign_key.sql
index 9b2a6b6bff..393e8524f8 100644
--- a/src/test/regress/sql/foreign_key.sql
+++ b/src/test/regress/sql/foreign_key.sql
@@ -2079,6 +2079,7 @@ $$;
CREATE TRIGGER trig_upd_pk AFTER UPDATE ON fkpart11.pk FOR EACH ROW EXECUTE FUNCTION fkpart11.print_row();
CREATE TRIGGER trig_del_pk AFTER DELETE ON fkpart11.pk FOR EACH ROW EXECUTE FUNCTION fkpart11.print_row();
CREATE TRIGGER trig_ins_pk AFTER INSERT ON fkpart11.pk FOR EACH ROW EXECUTE FUNCTION fkpart11.print_row();
+CREATE OR REPLACE CONSTRAINT TRIGGER trig_upd_fk_parted AFTER UPDATE ON fkpart11.fk_parted INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION fkpart11.print_row();
CREATE CONSTRAINT TRIGGER trig_upd_fk_parted AFTER UPDATE ON fkpart11.fk_parted INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION fkpart11.print_row();
CREATE CONSTRAINT TRIGGER trig_del_fk_parted AFTER DELETE ON fkpart11.fk_parted INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION fkpart11.print_row();
CREATE CONSTRAINT TRIGGER trig_ins_fk_parted AFTER INSERT ON fkpart11.fk_parted INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION fkpart11.print_row();
--
2.34.1
jian he <jian.universality@gmail.com> writes:
now changed to
static PartitionStrategy parsePartitionStrategy(char *strategy, int location,
core_yyscan_t yyscanner);
I can take a look at this, since it's basically a followup
to 774171c4f.
regards, tom lane