Skip to content

Commit 89e51ab

Browse files
committed
Add a parse location field to struct FunctionParameter.
This allows an error cursor to be supplied for a bunch of bad-function-definition errors that previously lacked one, or that cheated a bit by pointing at the contained type name when the error isn't really about that. Bump catversion from an abundance of caution --- I don't think this node type can actually appear in stored views/rules, but better safe than sorry. 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
1 parent b82c877 commit 89e51ab

File tree

17 files changed

+100
-24
lines changed

17 files changed

+100
-24
lines changed

src/backend/commands/functioncmds.c

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ interpret_function_parameter_list(ParseState *pstate,
232232
if (fpmode == FUNC_PARAM_DEFAULT)
233233
fpmode = FUNC_PARAM_IN;
234234

235-
typtup = LookupTypeName(NULL, t, NULL, false);
235+
typtup = LookupTypeName(pstate, t, NULL, false);
236236
if (typtup)
237237
{
238238
if (!((Form_pg_type) GETSTRUCT(typtup))->typisdefined)
@@ -242,18 +242,21 @@ interpret_function_parameter_list(ParseState *pstate,
242242
ereport(ERROR,
243243
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
244244
errmsg("SQL function cannot accept shell type %s",
245-
TypeNameToString(t))));
245+
TypeNameToString(t)),
246+
parser_errposition(pstate, t->location)));
246247
/* We don't allow creating aggregates on shell types either */
247248
else if (objtype == OBJECT_AGGREGATE)
248249
ereport(ERROR,
249250
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
250251
errmsg("aggregate cannot accept shell type %s",
251-
TypeNameToString(t))));
252+
TypeNameToString(t)),
253+
parser_errposition(pstate, t->location)));
252254
else
253255
ereport(NOTICE,
254256
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
255257
errmsg("argument type %s is only a shell",
256-
TypeNameToString(t))));
258+
TypeNameToString(t)),
259+
parser_errposition(pstate, t->location)));
257260
}
258261
toid = typeTypeId(typtup);
259262
ReleaseSysCache(typtup);
@@ -263,7 +266,8 @@ interpret_function_parameter_list(ParseState *pstate,
263266
ereport(ERROR,
264267
(errcode(ERRCODE_UNDEFINED_OBJECT),
265268
errmsg("type %s does not exist",
266-
TypeNameToString(t))));
269+
TypeNameToString(t)),
270+
parser_errposition(pstate, t->location)));
267271
toid = InvalidOid; /* keep compiler quiet */
268272
}
269273

@@ -276,15 +280,18 @@ interpret_function_parameter_list(ParseState *pstate,
276280
if (objtype == OBJECT_AGGREGATE)
277281
ereport(ERROR,
278282
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
279-
errmsg("aggregates cannot accept set arguments")));
283+
errmsg("aggregates cannot accept set arguments"),
284+
parser_errposition(pstate, fp->location)));
280285
else if (objtype == OBJECT_PROCEDURE)
281286
ereport(ERROR,
282287
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
283-
errmsg("procedures cannot accept set arguments")));
288+
errmsg("procedures cannot accept set arguments"),
289+
parser_errposition(pstate, fp->location)));
284290
else
285291
ereport(ERROR,
286292
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
287-
errmsg("functions cannot accept set arguments")));
293+
errmsg("functions cannot accept set arguments"),
294+
parser_errposition(pstate, fp->location)));
288295
}
289296

290297
/* handle input parameters */
@@ -294,7 +301,8 @@ interpret_function_parameter_list(ParseState *pstate,
294301
if (varCount > 0)
295302
ereport(ERROR,
296303
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
297-
errmsg("VARIADIC parameter must be the last input parameter")));
304+
errmsg("VARIADIC parameter must be the last input parameter"),
305+
parser_errposition(pstate, fp->location)));
298306
inTypes[inCount++] = toid;
299307
isinput = true;
300308
if (parameterTypes_list)
@@ -314,7 +322,8 @@ interpret_function_parameter_list(ParseState *pstate,
314322
if (varCount > 0)
315323
ereport(ERROR,
316324
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
317-
errmsg("VARIADIC parameter must be the last parameter")));
325+
errmsg("VARIADIC parameter must be the last parameter"),
326+
parser_errposition(pstate, fp->location)));
318327
/* Procedures with output parameters always return RECORD */
319328
*requiredResultType = RECORDOID;
320329
}
@@ -339,7 +348,8 @@ interpret_function_parameter_list(ParseState *pstate,
339348
if (!OidIsValid(get_element_type(toid)))
340349
ereport(ERROR,
341350
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
342-
errmsg("VARIADIC parameter must be an array")));
351+
errmsg("VARIADIC parameter must be an array"),
352+
parser_errposition(pstate, fp->location)));
343353
break;
344354
}
345355
}
@@ -385,7 +395,8 @@ interpret_function_parameter_list(ParseState *pstate,
385395
ereport(ERROR,
386396
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
387397
errmsg("parameter name \"%s\" used more than once",
388-
fp->name)));
398+
fp->name),
399+
parser_errposition(pstate, fp->location)));
389400
}
390401

391402
paramNames[i] = CStringGetTextDatum(fp->name);
@@ -402,7 +413,8 @@ interpret_function_parameter_list(ParseState *pstate,
402413
if (!isinput)
403414
ereport(ERROR,
404415
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
405-
errmsg("only input parameters can have default values")));
416+
errmsg("only input parameters can have default values"),
417+
parser_errposition(pstate, fp->location)));
406418

407419
def = transformExpr(pstate, fp->defexpr,
408420
EXPR_KIND_FUNCTION_DEFAULT);
@@ -417,7 +429,8 @@ interpret_function_parameter_list(ParseState *pstate,
417429
contain_var_clause(def))
418430
ereport(ERROR,
419431
(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
420-
errmsg("cannot use table references in parameter default value")));
432+
errmsg("cannot use table references in parameter default value"),
433+
parser_errposition(pstate, fp->location)));
421434

422435
/*
423436
* transformExpr() should have already rejected subqueries,
@@ -441,7 +454,8 @@ interpret_function_parameter_list(ParseState *pstate,
441454
if (isinput && have_defaults)
442455
ereport(ERROR,
443456
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
444-
errmsg("input parameters after one with a default value must also have defaults")));
457+
errmsg("input parameters after one with a default value must also have defaults"),
458+
parser_errposition(pstate, fp->location)));
445459

446460
/*
447461
* For procedures, we also can't allow OUT parameters after one
@@ -451,7 +465,8 @@ interpret_function_parameter_list(ParseState *pstate,
451465
if (objtype == OBJECT_PROCEDURE && have_defaults)
452466
ereport(ERROR,
453467
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
454-
errmsg("procedure OUT parameters cannot appear after one with a default value")));
468+
errmsg("procedure OUT parameters cannot appear after one with a default value"),
469+
parser_errposition(pstate, fp->location)));
455470
}
456471

457472
i++;

src/backend/nodes/nodeFuncs.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1723,8 +1723,7 @@ exprLocation(const Node *expr)
17231723
loc = ((const Constraint *) expr)->location;
17241724
break;
17251725
case T_FunctionParameter:
1726-
/* just use typename's location */
1727-
loc = exprLocation((Node *) ((const FunctionParameter *) expr)->argType);
1726+
loc = ((const FunctionParameter *) expr)->location;
17281727
break;
17291728
case T_XmlSerialize:
17301729
/* XMLSERIALIZE keyword should always be the first thing */

src/backend/parser/gram.y

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ static Node *makeSQLValueFunction(SQLValueFunctionOp op, int32 typmod,
184184
int location);
185185
static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args,
186186
List *args, int location);
187-
static List *mergeTableFuncParameters(List *func_args, List *columns);
187+
static List *mergeTableFuncParameters(List *func_args, List *columns, core_yyscan_t yyscanner);
188188
static TypeName *TableFuncTypeName(List *columns);
189189
static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_t yyscanner);
190190
static RangeVar *makeRangeVarFromQualifiedName(char *name, List *namelist, int location,
@@ -8290,7 +8290,7 @@ CreateFunctionStmt:
82908290
n->is_procedure = false;
82918291
n->replace = $2;
82928292
n->funcname = $4;
8293-
n->parameters = mergeTableFuncParameters($5, $9);
8293+
n->parameters = mergeTableFuncParameters($5, $9, yyscanner);
82948294
n->returnType = TableFuncTypeName($9);
82958295
n->returnType->location = @7;
82968296
n->options = $11;
@@ -8423,6 +8423,7 @@ func_arg:
84238423
n->argType = $3;
84248424
n->mode = $1;
84258425
n->defexpr = NULL;
8426+
n->location = @1;
84268427
$$ = n;
84278428
}
84288429
| param_name arg_class func_type
@@ -8433,6 +8434,7 @@ func_arg:
84338434
n->argType = $3;
84348435
n->mode = $2;
84358436
n->defexpr = NULL;
8437+
n->location = @1;
84368438
$$ = n;
84378439
}
84388440
| param_name func_type
@@ -8443,6 +8445,7 @@ func_arg:
84438445
n->argType = $2;
84448446
n->mode = FUNC_PARAM_DEFAULT;
84458447
n->defexpr = NULL;
8448+
n->location = @1;
84468449
$$ = n;
84478450
}
84488451
| arg_class func_type
@@ -8453,6 +8456,7 @@ func_arg:
84538456
n->argType = $2;
84548457
n->mode = $1;
84558458
n->defexpr = NULL;
8459+
n->location = @1;
84568460
$$ = n;
84578461
}
84588462
| func_type
@@ -8463,6 +8467,7 @@ func_arg:
84638467
n->argType = $1;
84648468
n->mode = FUNC_PARAM_DEFAULT;
84658469
n->defexpr = NULL;
8470+
n->location = @1;
84668471
$$ = n;
84678472
}
84688473
;
@@ -8799,6 +8804,7 @@ table_func_column: param_name func_type
87998804
n->argType = $2;
88008805
n->mode = FUNC_PARAM_TABLE;
88018806
n->defexpr = NULL;
8807+
n->location = @1;
88028808
$$ = n;
88038809
}
88048810
;
@@ -18908,7 +18914,7 @@ makeOrderedSetArgs(List *directargs, List *orderedargs,
1890818914
ereport(ERROR,
1890918915
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1891018916
errmsg("an ordered-set aggregate with a VARIADIC direct argument must have one VARIADIC aggregated argument of the same data type"),
18911-
parser_errposition(exprLocation((Node *) firsto))));
18917+
parser_errposition(firsto->location)));
1891218918

1891318919
/* OK, drop the duplicate VARIADIC argument from the internal form */
1891418920
orderedargs = NIL;
@@ -19183,7 +19189,7 @@ makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args,
1918319189
* Merge the input and output parameters of a table function.
1918419190
*/
1918519191
static List *
19186-
mergeTableFuncParameters(List *func_args, List *columns)
19192+
mergeTableFuncParameters(List *func_args, List *columns, core_yyscan_t yyscanner)
1918719193
{
1918819194
ListCell *lc;
1918919195

@@ -19197,7 +19203,8 @@ mergeTableFuncParameters(List *func_args, List *columns)
1919719203
p->mode != FUNC_PARAM_VARIADIC)
1919819204
ereport(ERROR,
1919919205
(errcode(ERRCODE_SYNTAX_ERROR),
19200-
errmsg("OUT and INOUT arguments aren't allowed in TABLE functions")));
19206+
errmsg("OUT and INOUT arguments aren't allowed in TABLE functions"),
19207+
parser_errposition(p->location)));
1920119208
}
1920219209

1920319210
return list_concat(func_args, columns);

src/include/catalog/catversion.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,6 @@
5757
*/
5858

5959
/* yyyymmddN */
60-
#define CATALOG_VERSION_NO 202410242
60+
#define CATALOG_VERSION_NO 202410311
6161

6262
#endif

src/include/nodes/parsenodes.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3482,6 +3482,7 @@ typedef struct FunctionParameter
34823482
TypeName *argType; /* TypeName for parameter type */
34833483
FunctionParameterMode mode; /* IN/OUT/etc */
34843484
Node *defexpr; /* raw default expr, or NULL if not given */
3485+
ParseLoc location; /* token location, or -1 if unknown */
34853486
} FunctionParameter;
34863487

34873488
typedef struct AlterFunctionStmt

src/test/modules/test_ddl_deparse/expected/create_type.out

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ CREATE FUNCTION text_w_default_out(text_w_default)
1313
AS 'textout'
1414
LANGUAGE internal STABLE STRICT ;
1515
NOTICE: argument type text_w_default is only a shell
16+
LINE 1: CREATE FUNCTION text_w_default_out(text_w_default)
17+
^
1618
NOTICE: DDL test: type simple, tag CREATE FUNCTION
1719
CREATE TYPE employee_type AS (name TEXT, salary NUMERIC);
1820
NOTICE: DDL test: type simple, tag CREATE TYPE

src/test/modules/test_ddl_deparse/expected/opfamily.out

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ NOTICE: DDL test: type simple, tag CREATE FUNCTION
88
create function int8alias1out(int8alias1) returns cstring
99
strict immutable language internal as 'int8out';
1010
NOTICE: argument type int8alias1 is only a shell
11+
LINE 1: create function int8alias1out(int8alias1) returns cstring
12+
^
1113
NOTICE: DDL test: type simple, tag CREATE FUNCTION
1214
create type int8alias1 (
1315
input = int8alias1in,
@@ -24,6 +26,8 @@ NOTICE: DDL test: type simple, tag CREATE FUNCTION
2426
create function int8alias2out(int8alias2) returns cstring
2527
strict immutable language internal as 'int8out';
2628
NOTICE: argument type int8alias2 is only a shell
29+
LINE 1: create function int8alias2out(int8alias2) returns cstring
30+
^
2731
NOTICE: DDL test: type simple, tag CREATE FUNCTION
2832
create type int8alias2 (
2933
input = int8alias2in,

src/test/modules/test_pg_dump/expected/test_pg_dump.out

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ CREATE FUNCTION casttesttype_out(casttesttype)
3636
AS 'textout'
3737
LANGUAGE internal STRICT IMMUTABLE;
3838
NOTICE: argument type casttesttype is only a shell
39+
LINE 1: CREATE FUNCTION casttesttype_out(casttesttype)
40+
^
3941
CREATE TYPE casttesttype (
4042
internallength = variable,
4143
input = casttesttype_in,

src/test/regress/expected/create_cast.out

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ CREATE FUNCTION casttesttype_out(casttesttype)
1313
AS 'textout'
1414
LANGUAGE internal STRICT IMMUTABLE;
1515
NOTICE: argument type casttesttype is only a shell
16+
LINE 1: CREATE FUNCTION casttesttype_out(casttesttype)
17+
^
1618
CREATE TYPE casttesttype (
1719
internallength = variable,
1820
input = casttesttype_in,

src/test/regress/expected/create_procedure.out

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,9 +401,13 @@ LINE 1: CREATE PROCEDURE ptestx() LANGUAGE SQL STRICT AS $$ INSERT I...
401401
CREATE PROCEDURE ptestx(a VARIADIC int[], b OUT int) LANGUAGE SQL
402402
AS $$ SELECT a[1] $$;
403403
ERROR: VARIADIC parameter must be the last parameter
404+
LINE 1: CREATE PROCEDURE ptestx(a VARIADIC int[], b OUT int) LANGUAG...
405+
^
404406
CREATE PROCEDURE ptestx(a int DEFAULT 42, b OUT int) LANGUAGE SQL
405407
AS $$ SELECT a $$;
406408
ERROR: procedure OUT parameters cannot appear after one with a default value
409+
LINE 1: CREATE PROCEDURE ptestx(a int DEFAULT 42, b OUT int) LANGUAG...
410+
^
407411
ALTER PROCEDURE ptest1(text) STRICT;
408412
ERROR: invalid attribute in procedure definition
409413
LINE 1: ALTER PROCEDURE ptest1(text) STRICT;

src/test/regress/expected/create_type.out

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ CREATE FUNCTION widget_out(widget)
2020
AS :'regresslib'
2121
LANGUAGE C STRICT IMMUTABLE;
2222
NOTICE: argument type widget is only a shell
23+
LINE 1: CREATE FUNCTION widget_out(widget)
24+
^
2325
CREATE FUNCTION int44in(cstring)
2426
RETURNS city_budget
2527
AS :'regresslib'
@@ -31,6 +33,8 @@ CREATE FUNCTION int44out(city_budget)
3133
AS :'regresslib'
3234
LANGUAGE C STRICT IMMUTABLE;
3335
NOTICE: argument type city_budget is only a shell
36+
LINE 1: CREATE FUNCTION int44out(city_budget)
37+
^
3438
CREATE TYPE widget (
3539
internallength = 24,
3640
input = widget_in,
@@ -75,6 +79,8 @@ CREATE FUNCTION int42_out(int42)
7579
AS 'int4out'
7680
LANGUAGE internal STRICT IMMUTABLE;
7781
NOTICE: argument type int42 is only a shell
82+
LINE 1: CREATE FUNCTION int42_out(int42)
83+
^
7884
CREATE FUNCTION text_w_default_in(cstring)
7985
RETURNS text_w_default
8086
AS 'textin'
@@ -85,6 +91,8 @@ CREATE FUNCTION text_w_default_out(text_w_default)
8591
AS 'textout'
8692
LANGUAGE internal STRICT IMMUTABLE;
8793
NOTICE: argument type text_w_default is only a shell
94+
LINE 1: CREATE FUNCTION text_w_default_out(text_w_default)
95+
^
8896
CREATE TYPE int42 (
8997
internallength = 4,
9098
input = int42_in,
@@ -186,6 +194,8 @@ NOTICE: return type base_type is only a shell
186194
CREATE FUNCTION base_fn_out(base_type) RETURNS cstring AS 'boolout'
187195
LANGUAGE internal IMMUTABLE STRICT;
188196
NOTICE: argument type base_type is only a shell
197+
LINE 1: CREATE FUNCTION base_fn_out(base_type) RETURNS cstring AS 'b...
198+
^
189199
CREATE TYPE base_type(INPUT = base_fn_in, OUTPUT = base_fn_out);
190200
DROP FUNCTION base_fn_in(cstring); -- error
191201
ERROR: cannot drop function base_fn_in(cstring) because other objects depend on it
@@ -320,9 +330,13 @@ NOTICE: return type myvarchar is only a shell
320330
CREATE FUNCTION myvarcharout(myvarchar) RETURNS cstring
321331
LANGUAGE internal IMMUTABLE PARALLEL SAFE STRICT AS 'varcharout';
322332
NOTICE: argument type myvarchar is only a shell
333+
LINE 1: CREATE FUNCTION myvarcharout(myvarchar) RETURNS cstring
334+
^
323335
CREATE FUNCTION myvarcharsend(myvarchar) RETURNS bytea
324336
LANGUAGE internal STABLE PARALLEL SAFE STRICT AS 'varcharsend';
325337
NOTICE: argument type myvarchar is only a shell
338+
LINE 1: CREATE FUNCTION myvarcharsend(myvarchar) RETURNS bytea
339+
^
326340
CREATE FUNCTION myvarcharrecv(internal, oid, integer) RETURNS myvarchar
327341
LANGUAGE internal STABLE PARALLEL SAFE STRICT AS 'varcharrecv';
328342
NOTICE: return type myvarchar is only a shell

0 commit comments

Comments
 (0)