aboutsummaryrefslogtreecommitdiff
path: root/src/pl
diff options
context:
space:
mode:
Diffstat (limited to 'src/pl')
-rw-r--r--src/pl/plperl/meson.build2
-rw-r--r--src/pl/plpgsql/src/expected/plpgsql_misc.out36
-rw-r--r--src/pl/plpgsql/src/pl_comp.c47
-rw-r--r--src/pl/plpgsql/src/pl_exec.c15
-rw-r--r--src/pl/plpgsql/src/pl_gram.y21
-rw-r--r--src/pl/plpgsql/src/pl_reserved_kwlist.h2
-rw-r--r--src/pl/plpgsql/src/pl_scanner.c2
-rw-r--r--src/pl/plpgsql/src/pl_unreserved_kwlist.h2
-rw-r--r--src/pl/plpgsql/src/sql/plpgsql_misc.sql29
-rw-r--r--src/pl/plpython/Makefile2
-rw-r--r--src/pl/plpython/plpy_cursorobject.c6
-rw-r--r--src/pl/plpython/plpy_planobject.c6
-rw-r--r--src/pl/plpython/plpy_resultobject.c6
-rw-r--r--src/pl/plpython/plpy_subxactobject.c6
-rw-r--r--src/pl/tcl/Makefile2
15 files changed, 147 insertions, 37 deletions
diff --git a/src/pl/plperl/meson.build b/src/pl/plperl/meson.build
index b463d4d56c5..7c4081c3460 100644
--- a/src/pl/plperl/meson.build
+++ b/src/pl/plperl/meson.build
@@ -96,7 +96,7 @@ tests += {
'plperl_transaction',
'plperl_env',
],
- 'regress_args': ['--dlpath', meson.build_root() / 'src/test/regress'],
+ 'regress_args': ['--dlpath', meson.project_build_root() / 'src/test/regress'],
},
}
diff --git a/src/pl/plpgsql/src/expected/plpgsql_misc.out b/src/pl/plpgsql/src/expected/plpgsql_misc.out
index a6511df08ec..ffb377f5f54 100644
--- a/src/pl/plpgsql/src/expected/plpgsql_misc.out
+++ b/src/pl/plpgsql/src/expected/plpgsql_misc.out
@@ -65,3 +65,39 @@ do $$ declare x public.foo%rowtype; begin end $$;
ERROR: relation "public.foo" does not exist
CONTEXT: compilation of PL/pgSQL function "inline_code_block" near line 1
do $$ declare x public.misc_table%rowtype; begin end $$;
+-- Test handling of an unreserved keyword as a variable name
+-- and record field name.
+do $$
+declare
+ execute int;
+ r record;
+begin
+ execute := 10;
+ raise notice 'execute = %', execute;
+ select 1 as strict into r;
+ raise notice 'r.strict = %', r.strict;
+end $$;
+NOTICE: execute = 10
+NOTICE: r.strict = 1
+-- Test handling of a reserved keyword as a record field name.
+do $$ declare r record;
+begin
+ select 1 as x, 2 as foreach into r;
+ raise notice 'r.x = %', r.x;
+ raise notice 'r.foreach = %', r.foreach; -- fails
+end $$;
+NOTICE: r.x = 1
+ERROR: field name "foreach" is a reserved key word
+LINE 1: r.foreach
+ ^
+HINT: Use double quotes to quote it.
+QUERY: r.foreach
+CONTEXT: PL/pgSQL function inline_code_block line 5 at RAISE
+do $$ declare r record;
+begin
+ select 1 as x, 2 as foreach into r;
+ raise notice 'r.x = %', r.x;
+ raise notice 'r."foreach" = %', r."foreach"; -- ok
+end $$;
+NOTICE: r.x = 1
+NOTICE: r."foreach" = 2
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index b80c59447fb..f6976689a69 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -177,6 +177,7 @@ plpgsql_compile_callback(FunctionCallInfo fcinfo,
yyscan_t scanner;
Datum prosrcdatum;
char *proc_source;
+ char *proc_signature;
HeapTuple typeTup;
Form_pg_type typeStruct;
PLpgSQL_variable *var;
@@ -223,6 +224,9 @@ plpgsql_compile_callback(FunctionCallInfo fcinfo,
plpgsql_check_syntax = forValidator;
plpgsql_curr_compile = function;
+ /* format_procedure leaks memory, so run it in temp context */
+ proc_signature = format_procedure(fcinfo->flinfo->fn_oid);
+
/*
* All the permanent output of compilation (e.g. parse tree) is kept in a
* per-function memory context, so it can be reclaimed easily.
@@ -237,7 +241,7 @@ plpgsql_compile_callback(FunctionCallInfo fcinfo,
ALLOCSET_DEFAULT_SIZES);
plpgsql_compile_tmp_cxt = MemoryContextSwitchTo(func_cxt);
- function->fn_signature = format_procedure(fcinfo->flinfo->fn_oid);
+ function->fn_signature = pstrdup(proc_signature);
MemoryContextSetIdentifier(func_cxt, function->fn_signature);
function->fn_oid = fcinfo->flinfo->fn_oid;
function->fn_input_collation = fcinfo->fncollation;
@@ -1211,17 +1215,22 @@ resolve_column_ref(ParseState *pstate, PLpgSQL_expr *expr,
}
/*
- * We should not get here, because a RECFIELD datum should
- * have been built at parse time for every possible qualified
- * reference to fields of this record. But if we do, handle
- * it like field-not-found: throw error or return NULL.
+ * Ideally we'd never get here, because a RECFIELD datum
+ * should have been built at parse time for every qualified
+ * reference to a field of this record that appears in the
+ * source text. However, plpgsql_yylex will not build such a
+ * datum unless the field name lexes as token type IDENT.
+ * Hence, if the would-be field name is a PL/pgSQL reserved
+ * word, we lose. Assume that that's what happened and tell
+ * the user to quote it, unless the caller prefers we just
+ * return NULL.
*/
if (error_if_no_field)
ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_COLUMN),
- errmsg("record \"%s\" has no field \"%s\"",
- (nnames_field == 1) ? name1 : name2,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("field name \"%s\" is a reserved key word",
colname),
+ errhint("Use double quotes to quote it."),
parser_errposition(pstate, cref->location)));
}
break;
@@ -1668,6 +1677,11 @@ plpgsql_parse_wordrowtype(char *ident)
{
Oid classOid;
Oid typOid;
+ TypeName *typName;
+ MemoryContext oldCxt;
+
+ /* Avoid memory leaks in long-term function context */
+ oldCxt = MemoryContextSwitchTo(plpgsql_compile_tmp_cxt);
/*
* Look up the relation. Note that because relation rowtypes have the
@@ -1690,9 +1704,12 @@ plpgsql_parse_wordrowtype(char *ident)
errmsg("relation \"%s\" does not have a composite type",
ident)));
+ typName = makeTypeName(ident);
+
+ MemoryContextSwitchTo(oldCxt);
+
/* Build and return the row type struct */
- return plpgsql_build_datatype(typOid, -1, InvalidOid,
- makeTypeName(ident));
+ return plpgsql_build_datatype(typOid, -1, InvalidOid, typName);
}
/* ----------
@@ -1706,6 +1723,7 @@ plpgsql_parse_cwordrowtype(List *idents)
Oid classOid;
Oid typOid;
RangeVar *relvar;
+ TypeName *typName;
MemoryContext oldCxt;
/*
@@ -1728,11 +1746,12 @@ plpgsql_parse_cwordrowtype(List *idents)
errmsg("relation \"%s\" does not have a composite type",
relvar->relname)));
+ typName = makeTypeNameFromNameList(idents);
+
MemoryContextSwitchTo(oldCxt);
/* Build and return the row type struct */
- return plpgsql_build_datatype(typOid, -1, InvalidOid,
- makeTypeNameFromNameList(idents));
+ return plpgsql_build_datatype(typOid, -1, InvalidOid, typName);
}
/*
@@ -1947,6 +1966,8 @@ plpgsql_build_recfield(PLpgSQL_rec *rec, const char *fldname)
* origtypname is the parsed form of what the user wrote as the type name.
* It can be NULL if the type could not be a composite type, or if it was
* identified by OID to begin with (e.g., it's a function argument type).
+ * origtypname is in short-lived storage and must be copied if we choose
+ * to incorporate it into the function's parse tree.
*/
PLpgSQL_type *
plpgsql_build_datatype(Oid typeOid, int32 typmod,
@@ -2065,7 +2086,7 @@ build_datatype(HeapTuple typeTup, int32 typmod,
errmsg("type %s is not composite",
format_type_be(typ->typoid))));
- typ->origtypname = origtypname;
+ typ->origtypname = copyObject(origtypname);
typ->tcache = typentry;
typ->tupdesc_id = typentry->tupDesc_identifier;
}
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index bb99781c56e..d19425b7a71 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -5703,7 +5703,7 @@ exec_eval_expr(PLpgSQL_execstate *estate,
/*
* Else do it the hard way via exec_run_select
*/
- rc = exec_run_select(estate, expr, 2, NULL);
+ rc = exec_run_select(estate, expr, 0, NULL);
if (rc != SPI_OK_SELECT)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
@@ -5757,6 +5757,10 @@ exec_eval_expr(PLpgSQL_execstate *estate,
/* ----------
* exec_run_select Execute a select query
+ *
+ * Note: passing maxtuples different from 0 ("return all tuples") is
+ * deprecated because it will prevent parallel execution of the query.
+ * However, we retain the parameter in case we need it someday.
* ----------
*/
static int
@@ -8606,6 +8610,15 @@ exec_set_found(PLpgSQL_execstate *estate, bool state)
PLpgSQL_var *var;
var = (PLpgSQL_var *) (estate->datums[estate->found_varno]);
+
+ /*
+ * Use pg_assume() to avoid a spurious warning with some compilers, by
+ * telling the compiler that the VARATT_IS_EXTERNAL_NON_EXPANDED() branch
+ * in assign_simple_var() will never be reached when called from here, due
+ * to "found" being a boolean (i.e. a byvalue type), not a varlena.
+ */
+ pg_assume(var->datatype->typlen != -1);
+
assign_simple_var(estate, var, BoolGetDatum(state), false, false);
}
diff --git a/src/pl/plpgsql/src/pl_gram.y b/src/pl/plpgsql/src/pl_gram.y
index 5612e66d023..17568d82554 100644
--- a/src/pl/plpgsql/src/pl_gram.y
+++ b/src/pl/plpgsql/src/pl_gram.y
@@ -1368,7 +1368,8 @@ for_control : for_variable K_IN
int tok = yylex(&yylval, &yylloc, yyscanner);
int tokloc = yylloc;
- if (tok == K_EXECUTE)
+ if (tok_is_keyword(tok, &yylval,
+ K_EXECUTE, "execute"))
{
/* EXECUTE means it's a dynamic FOR loop */
PLpgSQL_stmt_dynfors *new;
@@ -2135,7 +2136,8 @@ stmt_open : K_OPEN cursor_variable
yyerror(&yylloc, NULL, yyscanner, "syntax error, expected \"FOR\"");
tok = yylex(&yylval, &yylloc, yyscanner);
- if (tok == K_EXECUTE)
+ if (tok_is_keyword(tok, &yylval,
+ K_EXECUTE, "execute"))
{
int endtoken;
@@ -2536,6 +2538,7 @@ unreserved_keyword :
| K_ERRCODE
| K_ERROR
| K_EXCEPTION
+ | K_EXECUTE
| K_EXIT
| K_FETCH
| K_FIRST
@@ -2581,6 +2584,7 @@ unreserved_keyword :
| K_SLICE
| K_SQLSTATE
| K_STACKED
+ | K_STRICT
| K_TABLE
| K_TABLE_NAME
| K_TYPE
@@ -3514,7 +3518,8 @@ make_return_query_stmt(int location, YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_
new->stmtid = ++plpgsql_curr_compile->nstatements;
/* check for RETURN QUERY EXECUTE */
- if ((tok = yylex(yylvalp, yyllocp, yyscanner)) != K_EXECUTE)
+ tok = yylex(yylvalp, yyllocp, yyscanner);
+ if (!tok_is_keyword(tok, yylvalp, K_EXECUTE, "execute"))
{
/* ordinary static query */
plpgsql_push_back_token(tok, yylvalp, yyllocp, yyscanner);
@@ -3597,7 +3602,7 @@ read_into_target(PLpgSQL_variable **target, bool *strict, YYSTYPE *yylvalp, YYLT
*strict = false;
tok = yylex(yylvalp, yyllocp, yyscanner);
- if (strict && tok == K_STRICT)
+ if (strict && tok_is_keyword(tok, yylvalp, K_STRICT, "strict"))
{
*strict = true;
tok = yylex(yylvalp, yyllocp, yyscanner);
@@ -3848,6 +3853,7 @@ parse_datatype(const char *string, int location, yyscan_t yyscanner)
int32 typmod;
sql_error_callback_arg cbarg;
ErrorContextCallback syntax_errcontext;
+ MemoryContext oldCxt;
cbarg.location = location;
cbarg.yyscanner = yyscanner;
@@ -3857,9 +3863,14 @@ parse_datatype(const char *string, int location, yyscan_t yyscanner)
syntax_errcontext.previous = error_context_stack;
error_context_stack = &syntax_errcontext;
- /* Let the main parser try to parse it under standard SQL rules */
+ /*
+ * Let the main parser try to parse it under standard SQL rules. The
+ * parser leaks memory, so run it in temp context.
+ */
+ oldCxt = MemoryContextSwitchTo(plpgsql_compile_tmp_cxt);
typeName = typeStringToTypeName(string, NULL);
typenameTypeIdAndMod(NULL, typeName, &type_id, &typmod);
+ MemoryContextSwitchTo(oldCxt);
/* Restore former ereport callback */
error_context_stack = syntax_errcontext.previous;
diff --git a/src/pl/plpgsql/src/pl_reserved_kwlist.h b/src/pl/plpgsql/src/pl_reserved_kwlist.h
index ce7b0c9d331..f3ef2cbd8d7 100644
--- a/src/pl/plpgsql/src/pl_reserved_kwlist.h
+++ b/src/pl/plpgsql/src/pl_reserved_kwlist.h
@@ -33,7 +33,6 @@ PG_KEYWORD("case", K_CASE)
PG_KEYWORD("declare", K_DECLARE)
PG_KEYWORD("else", K_ELSE)
PG_KEYWORD("end", K_END)
-PG_KEYWORD("execute", K_EXECUTE)
PG_KEYWORD("for", K_FOR)
PG_KEYWORD("foreach", K_FOREACH)
PG_KEYWORD("from", K_FROM)
@@ -44,7 +43,6 @@ PG_KEYWORD("loop", K_LOOP)
PG_KEYWORD("not", K_NOT)
PG_KEYWORD("null", K_NULL)
PG_KEYWORD("or", K_OR)
-PG_KEYWORD("strict", K_STRICT)
PG_KEYWORD("then", K_THEN)
PG_KEYWORD("to", K_TO)
PG_KEYWORD("using", K_USING)
diff --git a/src/pl/plpgsql/src/pl_scanner.c b/src/pl/plpgsql/src/pl_scanner.c
index d08187dafcb..19825e5c718 100644
--- a/src/pl/plpgsql/src/pl_scanner.c
+++ b/src/pl/plpgsql/src/pl_scanner.c
@@ -53,7 +53,7 @@ IdentifierLookup plpgsql_IdentifierLookup = IDENTIFIER_LOOKUP_NORMAL;
* We try to avoid reserving more keywords than we have to; but there's
* little point in not reserving a word if it's reserved in the core grammar.
* Currently, the following words are reserved here but not in the core:
- * BEGIN BY DECLARE EXECUTE FOREACH IF LOOP STRICT WHILE
+ * BEGIN BY DECLARE FOREACH IF LOOP WHILE
*/
/* ScanKeywordList lookup data for PL/pgSQL keywords */
diff --git a/src/pl/plpgsql/src/pl_unreserved_kwlist.h b/src/pl/plpgsql/src/pl_unreserved_kwlist.h
index 98f99ec470c..b48c5a645ff 100644
--- a/src/pl/plpgsql/src/pl_unreserved_kwlist.h
+++ b/src/pl/plpgsql/src/pl_unreserved_kwlist.h
@@ -58,6 +58,7 @@ PG_KEYWORD("elsif", K_ELSIF)
PG_KEYWORD("errcode", K_ERRCODE)
PG_KEYWORD("error", K_ERROR)
PG_KEYWORD("exception", K_EXCEPTION)
+PG_KEYWORD("execute", K_EXECUTE)
PG_KEYWORD("exit", K_EXIT)
PG_KEYWORD("fetch", K_FETCH)
PG_KEYWORD("first", K_FIRST)
@@ -103,6 +104,7 @@ PG_KEYWORD("scroll", K_SCROLL)
PG_KEYWORD("slice", K_SLICE)
PG_KEYWORD("sqlstate", K_SQLSTATE)
PG_KEYWORD("stacked", K_STACKED)
+PG_KEYWORD("strict", K_STRICT)
PG_KEYWORD("table", K_TABLE)
PG_KEYWORD("table_name", K_TABLE_NAME)
PG_KEYWORD("type", K_TYPE)
diff --git a/src/pl/plpgsql/src/sql/plpgsql_misc.sql b/src/pl/plpgsql/src/sql/plpgsql_misc.sql
index d3a7f703a75..0bc39fcf325 100644
--- a/src/pl/plpgsql/src/sql/plpgsql_misc.sql
+++ b/src/pl/plpgsql/src/sql/plpgsql_misc.sql
@@ -37,3 +37,32 @@ do $$ declare x foo.bar%rowtype; begin end $$;
do $$ declare x foo.bar.baz%rowtype; begin end $$;
do $$ declare x public.foo%rowtype; begin end $$;
do $$ declare x public.misc_table%rowtype; begin end $$;
+
+-- Test handling of an unreserved keyword as a variable name
+-- and record field name.
+do $$
+declare
+ execute int;
+ r record;
+begin
+ execute := 10;
+ raise notice 'execute = %', execute;
+ select 1 as strict into r;
+ raise notice 'r.strict = %', r.strict;
+end $$;
+
+-- Test handling of a reserved keyword as a record field name.
+
+do $$ declare r record;
+begin
+ select 1 as x, 2 as foreach into r;
+ raise notice 'r.x = %', r.x;
+ raise notice 'r.foreach = %', r.foreach; -- fails
+end $$;
+
+do $$ declare r record;
+begin
+ select 1 as x, 2 as foreach into r;
+ raise notice 'r.x = %', r.x;
+ raise notice 'r."foreach" = %', r."foreach"; -- ok
+end $$;
diff --git a/src/pl/plpython/Makefile b/src/pl/plpython/Makefile
index f959083a0bd..25f295c3709 100644
--- a/src/pl/plpython/Makefile
+++ b/src/pl/plpython/Makefile
@@ -11,7 +11,7 @@ ifeq ($(PORTNAME), win32)
override python_libspec =
endif
-override CPPFLAGS := -I. -I$(srcdir) $(python_includespec) $(CPPFLAGS)
+override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS) $(python_includespec)
rpathdir = $(python_libdir)
diff --git a/src/pl/plpython/plpy_cursorobject.c b/src/pl/plpython/plpy_cursorobject.c
index 37d7efca77c..cc74c4df6ba 100644
--- a/src/pl/plpython/plpy_cursorobject.c
+++ b/src/pl/plpython/plpy_cursorobject.c
@@ -58,9 +58,9 @@ static PyType_Slot PLyCursor_slots[] =
static PyType_Spec PLyCursor_spec =
{
.name = "PLyCursor",
- .basicsize = sizeof(PLyCursorObject),
- .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
- .slots = PLyCursor_slots,
+ .basicsize = sizeof(PLyCursorObject),
+ .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .slots = PLyCursor_slots,
};
static PyTypeObject *PLy_CursorType;
diff --git a/src/pl/plpython/plpy_planobject.c b/src/pl/plpython/plpy_planobject.c
index 6044893afdd..edfb76c8770 100644
--- a/src/pl/plpython/plpy_planobject.c
+++ b/src/pl/plpython/plpy_planobject.c
@@ -45,9 +45,9 @@ static PyType_Slot PLyPlan_slots[] =
static PyType_Spec PLyPlan_spec =
{
.name = "PLyPlan",
- .basicsize = sizeof(PLyPlanObject),
- .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
- .slots = PLyPlan_slots,
+ .basicsize = sizeof(PLyPlanObject),
+ .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .slots = PLyPlan_slots,
};
static PyTypeObject *PLy_PlanType;
diff --git a/src/pl/plpython/plpy_resultobject.c b/src/pl/plpython/plpy_resultobject.c
index 0d9997cbaa3..d433929b360 100644
--- a/src/pl/plpython/plpy_resultobject.c
+++ b/src/pl/plpython/plpy_resultobject.c
@@ -70,9 +70,9 @@ static PyType_Slot PLyResult_slots[] =
static PyType_Spec PLyResult_spec =
{
.name = "PLyResult",
- .basicsize = sizeof(PLyResultObject),
- .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
- .slots = PLyResult_slots,
+ .basicsize = sizeof(PLyResultObject),
+ .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .slots = PLyResult_slots,
};
static PyTypeObject *PLy_ResultType;
diff --git a/src/pl/plpython/plpy_subxactobject.c b/src/pl/plpython/plpy_subxactobject.c
index c2484a99b4a..c225b652ab4 100644
--- a/src/pl/plpython/plpy_subxactobject.c
+++ b/src/pl/plpython/plpy_subxactobject.c
@@ -46,9 +46,9 @@ static PyType_Slot PLySubtransaction_slots[] =
static PyType_Spec PLySubtransaction_spec =
{
.name = "PLySubtransaction",
- .basicsize = sizeof(PLySubtransactionObject),
- .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
- .slots = PLySubtransaction_slots,
+ .basicsize = sizeof(PLySubtransactionObject),
+ .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .slots = PLySubtransaction_slots,
};
static PyTypeObject *PLy_SubtransactionType;
diff --git a/src/pl/tcl/Makefile b/src/pl/tcl/Makefile
index ea52a2efc22..dd57f7d694c 100644
--- a/src/pl/tcl/Makefile
+++ b/src/pl/tcl/Makefile
@@ -11,7 +11,7 @@ top_builddir = ../../..
include $(top_builddir)/src/Makefile.global
-override CPPFLAGS := -I. -I$(srcdir) $(TCL_INCLUDE_SPEC) $(CPPFLAGS)
+override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS) $(TCL_INCLUDE_SPEC)
# On Windows, we don't link directly with the Tcl library; see below
ifneq ($(PORTNAME), win32)