čt 7. 3. 2019 v 18:45 odesílatel Andrew Dunstan <
[email protected]> napsal:
>
> On 3/7/19 12:41 PM, Pavel Stehule wrote:
> >
> >
> > čt 7. 3. 2019 v 18:35 odesílatel Andrew Dunstan
> > <[email protected]
> > <mailto:[email protected]>> napsal:
> >
> >
> >
> >
> > The other thing that bugs me a bit about the patch is that the only
> > testing it does it to make sure that pragmas are ignored by the core
> > plpgsql processor. Maybe that's enough, but mostly we tend to like to
> > have one actual use of a feature.
> >
> >
> > Unfortunately plpgsql_check is not part of upstream
> >
> > https://github.com/okbob/plpgsql_check
> >
> > I can to write some simple extension - some print tracing, that can
> > use this feature?
> >
> >
>
> Works for me. Another idea I had was some sort of crypto signature pragma.
>
Here is pragma patch with demo
Regards
Pavel
>
>
> I still think making it block level only is unwarranted, though.
>
>
> cheers
>
>
> andrew
>
>
> --
> Andrew Dunstan https://www.2ndQuadrant.com
> PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
>
>
diff --git a/contrib/Makefile b/contrib/Makefile
index 92184ed487..a5edd12e35 100644
--- a/contrib/Makefile
+++ b/contrib/Makefile
@@ -39,6 +39,7 @@ SUBDIRS = \
pgrowlocks \
pgstattuple \
pg_visibility \
+ plpgsql_tracer \
postgres_fdw \
seg \
spi \
diff --git a/contrib/plpgsql_tracer/.gitignore b/contrib/plpgsql_tracer/.gitignore
new file mode 100644
index 0000000000..5dcb3ff972
--- /dev/null
+++ b/contrib/plpgsql_tracer/.gitignore
@@ -0,0 +1,4 @@
+# Generated subdirectories
+/log/
+/results/
+/tmp_check/
diff --git a/contrib/plpgsql_tracer/Makefile b/contrib/plpgsql_tracer/Makefile
new file mode 100644
index 0000000000..7900471ed6
--- /dev/null
+++ b/contrib/plpgsql_tracer/Makefile
@@ -0,0 +1,25 @@
+# contrib/plpgsql_tracer/Makefile
+
+MODULES = plpgsql_tracer
+
+EXTENSION = plpgsql_tracer
+PGFILEDESC = "plpgsql tracer - example of PRAGMA based extension"
+
+REGRESS = plpgsql_tracer
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = contrib/citext
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
+
+ifeq ($(PORTNAME), darwin)
+override CFLAGS += -undefined dynamic_lookup
+endif
+
+override CFLAGS += -I$(top_builddir)/src/pl/plpgsql/src -Wall
diff --git a/contrib/plpgsql_tracer/expected/plpgsql_tracer.out b/contrib/plpgsql_tracer/expected/plpgsql_tracer.out
new file mode 100644
index 0000000000..d183fcebef
--- /dev/null
+++ b/contrib/plpgsql_tracer/expected/plpgsql_tracer.out
@@ -0,0 +1,38 @@
+--
+-- Test plpgsql_tracer extension
+--
+load 'plpgsql';
+load 'plpgsql_tracer';
+create or replace function fx()
+returns integer
+language plpgsql
+AS $function$
+declare
+ x int = 0;
+ pragma plpgsql_tracer(on);
+ pragma ignore_me;
+begin
+ x := x + 1;
+ x := x + 1;
+ declare
+ pragma plpgsql_tracer(off);
+ begin
+ -- hidden statemt
+ declare pragma plpgsql_tracer; -- ignored, just warning
+ begin
+ x := x + 1;
+ end;
+ end;
+ return x;
+end;
+$function$;
+select fx();
+WARNING: missing argument of PRAGMA plpgsql_tracer
+NOTICE: plpgsql_tracer: 7: assignment
+NOTICE: plpgsql_tracer: 8: assignment
+NOTICE: plpgsql_tracer: 18: RETURN
+ fx
+----
+ 3
+(1 row)
+
diff --git a/contrib/plpgsql_tracer/plpgsql_tracer.c b/contrib/plpgsql_tracer/plpgsql_tracer.c
new file mode 100644
index 0000000000..03d4ed8a62
--- /dev/null
+++ b/contrib/plpgsql_tracer/plpgsql_tracer.c
@@ -0,0 +1,248 @@
+/*
+ * contrib/plpgsql_tracer/plpgsql_tracer.c
+ */
+#include "postgres.h"
+#include "plpgsql.h"
+#include "fmgr.h"
+
+#include "nodes/bitmapset.h"
+
+PG_MODULE_MAGIC;
+
+
+#define PLPGSQL_TRACER_MAGIC 73071522
+
+typedef struct plpgsql_tracer_data
+{
+ int magic;
+ Bitmapset *stmtids;
+} plpgsql_tracer_data;
+
+
+static void collect_stmtid(PLpgSQL_stmt *stmt, plpgsql_tracer_data *data, bool trace);
+
+static void plpgsql_tracer_func_init(PLpgSQL_execstate *estate, PLpgSQL_function *func);
+static void plpgsql_tracer_stmt_beg(PLpgSQL_execstate *estate, PLpgSQL_stmt *stmt);
+
+static PLpgSQL_plugin plugin_funcs = { plpgsql_tracer_func_init,
+ NULL,
+ NULL,
+ plpgsql_tracer_stmt_beg,
+ NULL,
+ NULL,
+ NULL};
+
+/*
+ * Collect traced statement id from list of statements.
+ */
+static void
+collect_stmtid_list(List *stmts,
+ plpgsql_tracer_data *data,
+ bool trace)
+{
+ ListCell *lc;
+
+ foreach(lc, stmts)
+ {
+ collect_stmtid((PLpgSQL_stmt *) lfirst(lc), data, trace);
+ }
+}
+
+
+/*
+ * It is iterate over all plpgsql statements and collect stmtid of statements
+ * inside blocks marked by PRAGMA trace.
+ */
+static void
+collect_stmtid(PLpgSQL_stmt *stmt,
+ plpgsql_tracer_data *data,
+ bool trace)
+{
+ switch (stmt->cmd_type)
+ {
+ case PLPGSQL_STMT_BLOCK:
+ {
+ PLpgSQL_stmt_block *stmt_block = (PLpgSQL_stmt_block *) stmt;
+ ListCell *lc;
+
+ foreach (lc, stmt_block->pragmas)
+ {
+ PLpgSQL_pragma *pragma = (PLpgSQL_pragma *) lfirst(lc);
+
+ if (strcmp(pragma->name, "plpgsql_tracer") == 0)
+ {
+ ListCell *larg;
+ char *value = NULL;
+ int count = 0;
+
+ /* this pragma requires just on/off parameter */
+ foreach(larg, pragma->args)
+ {
+ PLpgSQL_pragma_arg *arg = (PLpgSQL_pragma_arg *) lfirst(larg);
+
+ if (count++ > 0)
+ {
+ elog(WARNING, "PRAGMA plpgsql_tracer has only one parameter");
+ break;
+ }
+
+ if (arg->argname)
+ {
+ elog(WARNING, "PRAGMA plpgsql_tracer hasn't named parameters");
+ break;
+ }
+
+ if (arg->type == PLPGSQL_PRAGMA_ARG_IDENT)
+ value = arg->val.ident;
+ else if (arg->type == PLPGSQL_PRAGMA_ARG_SCONST)
+ value = arg->val.str;
+ else
+ {
+ elog(WARNING, "unsupported type of PRAGMA plpgsql_tracer");
+ break;
+ }
+
+ if (value)
+ {
+ if ((strcmp(value, "on") == 0) ||
+ (strcmp(value, "true") == 0) ||
+ (strcmp(value, "t") == 0))
+ trace = true;
+ else if ((strcmp(value, "off") == 0) ||
+ (strcmp(value, "false") == 0) ||
+ (strcmp(value, "f") == 0))
+ trace = false;
+ else
+ {
+ elog(WARNING, "unknown value of PRAGMA plpgsql_tracer parameter");
+ break;
+ }
+ }
+ }
+
+ if (count < 1)
+ elog(WARNING, "missing argument of PRAGMA plpgsql_tracer");
+ }
+ }
+
+ collect_stmtid_list(stmt_block->body, data, trace);
+ }
+ break;
+
+ case PLPGSQL_STMT_IF:
+ {
+ PLpgSQL_stmt_if *stmt_if = (PLpgSQL_stmt_if *) stmt;
+ ListCell *lc;
+
+ collect_stmtid_list(stmt_if->then_body, data, trace);
+ foreach(lc, stmt_if->elsif_list)
+ {
+ collect_stmtid_list(((PLpgSQL_if_elsif *) lfirst(lc))->stmts,
+ data,
+ trace);
+ }
+ collect_stmtid_list(stmt_if->else_body, data, trace);
+ }
+ break;
+
+ case PLPGSQL_STMT_CASE:
+ {
+ PLpgSQL_stmt_case *stmt_case = (PLpgSQL_stmt_case *) stmt;
+ ListCell *lc;
+
+ foreach(lc, stmt_case->case_when_list)
+ {
+ collect_stmtid_list(((PLpgSQL_case_when *) lfirst(lc))->stmts,
+ data,
+ trace);
+ }
+ collect_stmtid_list(stmt_case->else_stmts, data, trace);
+ }
+ break;
+
+ case PLPGSQL_STMT_LOOP:
+ collect_stmtid_list(((PLpgSQL_stmt_while *) stmt)->body, data, trace);
+ break;
+
+ case PLPGSQL_STMT_FORI:
+ collect_stmtid_list(((PLpgSQL_stmt_fori *) stmt)->body, data, trace);
+ break;
+
+ case PLPGSQL_STMT_FORS:
+ collect_stmtid_list(((PLpgSQL_stmt_fors *) stmt)->body, data, trace);
+ break;
+
+ case PLPGSQL_STMT_FORC:
+ collect_stmtid_list(((PLpgSQL_stmt_forc *) stmt)->body, data, trace);
+ break;
+
+ case PLPGSQL_STMT_DYNFORS:
+ collect_stmtid_list(((PLpgSQL_stmt_dynfors *) stmt)->body, data, trace);
+ break;
+
+ case PLPGSQL_STMT_FOREACH_A:
+ collect_stmtid_list(((PLpgSQL_stmt_foreach_a *) stmt)->body, data, trace);
+ break;
+
+ default:
+ /* do nothing */
+ break;
+ }
+
+ if (trace && stmt->cmd_type != PLPGSQL_STMT_BLOCK)
+ data->stmtids = bms_add_member(data->stmtids, stmt->stmtid);
+}
+
+/*
+ * Define functions for PL debug API
+ */
+static void
+plpgsql_tracer_func_init(PLpgSQL_execstate *estate, PLpgSQL_function *func)
+{
+ plpgsql_tracer_data *tdata;
+
+ tdata = palloc0(sizeof(plpgsql_tracer_data));
+ tdata->magic = PLPGSQL_TRACER_MAGIC;
+
+ estate->plugin_info = tdata;
+
+ collect_stmtid((PLpgSQL_stmt *) func->action, tdata, false);
+}
+
+static void
+plpgsql_tracer_stmt_beg(PLpgSQL_execstate *estate, PLpgSQL_stmt *stmt)
+{
+ plpgsql_tracer_data *tdata = (plpgsql_tracer_data *) estate->plugin_info;
+
+ if (tdata->magic == PLPGSQL_TRACER_MAGIC)
+ {
+ /* now, its our data */
+ if (bms_is_member(stmt->stmtid, tdata->stmtids))
+ {
+ elog(NOTICE,
+ "plpgsql_tracer: %4d: %s",
+ stmt->lineno,
+ plpgsql_stmt_typename(stmt));
+ }
+ }
+}
+
+/*
+ * Module initialization
+ *
+ * setup PLpgSQL_plugin API
+ */
+void
+_PG_init(void)
+{
+ PLpgSQL_plugin **var_ptr;
+ static bool inited = false;
+
+ if (inited)
+ return;
+
+ var_ptr = (PLpgSQL_plugin **) find_rendezvous_variable("PLpgSQL_plugin");
+ *var_ptr = &plugin_funcs;
+
+ inited = true;
+}
diff --git a/contrib/plpgsql_tracer/plpgsql_tracer.control b/contrib/plpgsql_tracer/plpgsql_tracer.control
new file mode 100644
index 0000000000..9f0fe46596
--- /dev/null
+++ b/contrib/plpgsql_tracer/plpgsql_tracer.control
@@ -0,0 +1,5 @@
+# plpgsql_tracer extension
+comment = 'tracing functionality for plpgsql'
+default_version = '1.0'
+module_pathname = '$libdir/plpgsql_tracer'
+relocatable = true
diff --git a/contrib/plpgsql_tracer/sql/plpgsql_tracer.sql b/contrib/plpgsql_tracer/sql/plpgsql_tracer.sql
new file mode 100644
index 0000000000..70528e0583
--- /dev/null
+++ b/contrib/plpgsql_tracer/sql/plpgsql_tracer.sql
@@ -0,0 +1,32 @@
+--
+-- Test plpgsql_tracer extension
+--
+
+load 'plpgsql';
+load 'plpgsql_tracer';
+
+create or replace function fx()
+returns integer
+language plpgsql
+AS $function$
+declare
+ x int = 0;
+ pragma plpgsql_tracer(on);
+ pragma ignore_me;
+begin
+ x := x + 1;
+ x := x + 1;
+ declare
+ pragma plpgsql_tracer(off);
+ begin
+ -- hidden statemt
+ declare pragma plpgsql_tracer; -- ignored, just warning
+ begin
+ x := x + 1;
+ end;
+ end;
+ return x;
+end;
+$function$;
+
+select fx();
diff --git a/doc/src/sgml/plpgsql.sgml b/doc/src/sgml/plpgsql.sgml
index f8c6435c50..54bfb3f137 100644
--- a/doc/src/sgml/plpgsql.sgml
+++ b/doc/src/sgml/plpgsql.sgml
@@ -814,6 +814,49 @@ $$ LANGUAGE plpgsql;
happen in a plain SQL command.
</para>
</sect2>
+
+ <sect2 id="plpgsql-declaration-pragma">
+ <title>Block level PRAGMA</title>
+
+ <para>
+ A <application>PL/pgSQL</application> function supports pragma on block
+ level. Pragma is a compiler directive, that can be used by
+ <application>PL/pgSQL</application> compiler, or by any extensions that
+ can work with <application>PL/pgSQL</application> code.
+ </para>
+
+<synopsis>
+<literal>PRAGMA</literal> <replaceable>name</replaceable>;
+<literal>PRAGMA</literal> <replaceable>name</replaceable> ( <optional> <replaceable>argument_name</replaceable> => </optional> <replaceable>value</replaceable> );
+</synopsis>
+
+ <para>
+ The pragma can be used for <application>plpgsql_check</application>
+ enabling/disabling code checking or for storing additional information:
+
+<programlisting>
+DECLARE
+ PRAGMA plpgsql_check(off);
+BEGIN
+ -- code inside block will not be checked by plpgsql_check
+ ...
+
+
+DECLARE
+ -- force routine volatility detection
+ PRAGMA plpgsql_check(volatility => volatile);
+ PRAGMA plpgsql_check(temporary_table => 'tmp_tab', '(a int, b int, c int)');
+BEGIN
+ ...
+</programlisting>
+
+ More details are described in related extension's description.
+ </para>
+
+ <para>
+ Unknown pragma is ignored.
+ </para>
+ </sect2>
</sect1>
<sect1 id="plpgsql-expressions">
diff --git a/src/pl/plpgsql/src/Makefile b/src/pl/plpgsql/src/Makefile
index cc1c2613d3..2aded819f9 100644
--- a/src/pl/plpgsql/src/Makefile
+++ b/src/pl/plpgsql/src/Makefile
@@ -27,7 +27,8 @@ DATA = plpgsql.control plpgsql--1.0.sql plpgsql--unpackaged--1.0.sql
REGRESS_OPTS = --dbname=$(PL_TESTDB)
REGRESS = plpgsql_call plpgsql_control plpgsql_domain plpgsql_record \
- plpgsql_cache plpgsql_transaction plpgsql_trigger plpgsql_varprops
+ plpgsql_cache plpgsql_transaction plpgsql_trigger plpgsql_varprops \
+ plpgsql_pragma
# where to find gen_keywordlist.pl and subsidiary files
TOOLSDIR = $(top_srcdir)/src/tools
diff --git a/src/pl/plpgsql/src/expected/plpgsql_pragma.out b/src/pl/plpgsql/src/expected/plpgsql_pragma.out
new file mode 100644
index 0000000000..ffe5c7664a
--- /dev/null
+++ b/src/pl/plpgsql/src/expected/plpgsql_pragma.out
@@ -0,0 +1,11 @@
+do $$
+DECLARE
+ var int;
+ PRAGMA xxx;
+ PRAGMA do;
+ PRAGMA var; -- name can be any identifier
+ PRAGMA xxx(10, 10.1, 'aaaa', "aaaaa".aaaa, off, on); -- supported types
+ PRAGMA xxx(label => value);
+BEGIN
+END;
+$$;
diff --git a/src/pl/plpgsql/src/pl_gram.y b/src/pl/plpgsql/src/pl_gram.y
index 03f7cdce8c..33e6929af9 100644
--- a/src/pl/plpgsql/src/pl_gram.y
+++ b/src/pl/plpgsql/src/pl_gram.y
@@ -111,6 +111,11 @@ static PLpgSQL_expr *read_cursor_args(PLpgSQL_var *cursor,
static List *read_raise_options(void);
static void check_raise_parameters(PLpgSQL_stmt_raise *stmt);
+/*
+ * local variable for collection pragmas inside one declare block
+ */
+static List *pragmas;
+
%}
%expect 0
@@ -146,6 +151,7 @@ static void check_raise_parameters(PLpgSQL_stmt_raise *stmt);
char *label;
int n_initvars;
int *initvarnos;
+ List *pragmas;
} declhdr;
struct
{
@@ -166,6 +172,8 @@ static void check_raise_parameters(PLpgSQL_stmt_raise *stmt);
PLpgSQL_diag_item *diagitem;
PLpgSQL_stmt_fetch *fetch;
PLpgSQL_case_when *casewhen;
+ PLpgSQL_pragma *pragma;
+ PLpgSQL_pragma_arg *pragma_arg;
}
%type <declhdr> decl_sect
@@ -221,6 +229,9 @@ static void check_raise_parameters(PLpgSQL_stmt_raise *stmt);
%type <keyword> unreserved_keyword
+%type <list> pragma_args
+%type <pragma_arg> pragma_arg
+%type <pragma_arg> pragma_val
/*
* Basic non-keyword token types. These are hard-wired into the core lexer.
@@ -321,6 +332,7 @@ static void check_raise_parameters(PLpgSQL_stmt_raise *stmt);
%token <keyword> K_PG_EXCEPTION_CONTEXT
%token <keyword> K_PG_EXCEPTION_DETAIL
%token <keyword> K_PG_EXCEPTION_HINT
+%token <keyword> K_PRAGMA
%token <keyword> K_PRINT_STRICT_PARAMS
%token <keyword> K_PRIOR
%token <keyword> K_QUERY
@@ -418,6 +430,7 @@ pl_block : decl_sect K_BEGIN proc_sect exception_sect K_END opt_label
new->label = $1.label;
new->n_initvars = $1.n_initvars;
new->initvarnos = $1.initvarnos;
+ new->pragmas = $1.pragmas;
new->body = $3;
new->exceptions = $4;
@@ -436,6 +449,7 @@ decl_sect : opt_block_label
$$.label = $1;
$$.n_initvars = 0;
$$.initvarnos = NULL;
+ $$.pragmas = NIL;
}
| opt_block_label decl_start
{
@@ -443,6 +457,7 @@ decl_sect : opt_block_label
$$.label = $1;
$$.n_initvars = 0;
$$.initvarnos = NULL;
+ $$.pragmas = NIL;
}
| opt_block_label decl_start decl_stmts
{
@@ -450,6 +465,9 @@ decl_sect : opt_block_label
$$.label = $1;
/* Remember variables declared in decl_stmts */
$$.n_initvars = plpgsql_add_initdatums(&($$.initvarnos));
+
+ /* there are nothing special work, use local list only */
+ $$.pragmas = pragmas;
}
;
@@ -579,6 +597,112 @@ decl_statement : decl_varname decl_const decl_datatype decl_collate decl_notnull
new->cursor_explicit_argrow = $5->dno;
new->cursor_options = CURSOR_OPT_FAST_PLAN | $2;
}
+ | K_PRAGMA any_identifier ';'
+ {
+ PLpgSQL_pragma *new = palloc0(sizeof(PLpgSQL_pragma));
+
+ new->name = $2;
+ new->args = NIL;
+
+ pragmas = lappend(pragmas, new);
+ }
+ | K_PRAGMA any_identifier '(' pragma_args ')' ';'
+ {
+ PLpgSQL_pragma *new = palloc0(sizeof(PLpgSQL_pragma));
+
+ new->name = $2;
+ new->args = $4;
+
+ pragmas = lappend(pragmas, new);
+ }
+ ;
+
+pragma_args : pragma_args ',' pragma_arg
+ {
+ $$ = lappend($1, $3);
+ }
+ | pragma_arg
+ {
+ $$ = list_make1($1);
+ }
+ ;
+
+pragma_arg : pragma_val
+ {
+ $1->argname = NULL;
+ $$ = $1;
+ }
+ | any_identifier EQUALS_GREATER pragma_val
+ {
+ $3->argname = $1;
+ $$ = $3;
+ }
+ ;
+
+pragma_val : T_WORD
+ {
+ PLpgSQL_pragma_arg *new = palloc0(sizeof(PLpgSQL_pragma_arg));
+
+ new->type = PLPGSQL_PRAGMA_ARG_IDENT;
+ new->val.ident = $1.ident;
+ $$ = new;
+ }
+ | unreserved_keyword
+ {
+ PLpgSQL_pragma_arg *new = palloc0(sizeof(PLpgSQL_pragma_arg));
+
+ new->type = PLPGSQL_PRAGMA_ARG_IDENT;
+ new->val.ident = pstrdup($1);
+ $$ = new;
+ }
+ | T_DATUM
+ {
+ PLpgSQL_pragma_arg *new = palloc0(sizeof(PLpgSQL_pragma_arg));
+
+ if ($1.ident)
+ {
+ new->type = PLPGSQL_PRAGMA_ARG_QUAL_IDENT;
+ new->val.idents = $1.idents;
+ }
+ else
+ {
+ new->type = PLPGSQL_PRAGMA_ARG_IDENT;
+ new->val.ident = $1.ident;
+ }
+ $$ = new;
+ }
+ | T_CWORD
+ {
+ PLpgSQL_pragma_arg *new = palloc0(sizeof(PLpgSQL_pragma_arg));
+
+ new->type = PLPGSQL_PRAGMA_ARG_QUAL_IDENT;
+ new->val.idents = $1.idents;
+ $$ = new;
+ }
+ | SCONST
+ {
+ PLpgSQL_pragma_arg *new = palloc0(sizeof(PLpgSQL_pragma_arg));
+
+ new->type = PLPGSQL_PRAGMA_ARG_SCONST;
+ new->val.str = $1;
+ $$ = new;
+ }
+ | FCONST
+ {
+ PLpgSQL_pragma_arg *new = palloc0(sizeof(PLpgSQL_pragma_arg));
+
+ new->type = PLPGSQL_PRAGMA_ARG_FCONST;
+ new->val.fval = atof($1);
+ $$ = new;
+ }
+ | ICONST
+ {
+ PLpgSQL_pragma_arg *new = palloc0(sizeof(PLpgSQL_pragma_arg));
+
+ new->type = PLPGSQL_PRAGMA_ARG_ICONST;
+ new->val.ival = $1;
+ $$ = new;
+ }
;
opt_scrollable :
@@ -2422,11 +2546,13 @@ expr_until_loop :
opt_block_label :
{
plpgsql_ns_push(NULL, PLPGSQL_LABEL_BLOCK);
+ pragmas = NIL;
$$ = NULL;
}
| LESS_LESS any_identifier GREATER_GREATER
{
plpgsql_ns_push($2, PLPGSQL_LABEL_BLOCK);
+ pragmas = NIL;
$$ = $2;
}
;
diff --git a/src/pl/plpgsql/src/pl_reserved_kwlist.h b/src/pl/plpgsql/src/pl_reserved_kwlist.h
index 8425c3ca2e..00383f970b 100644
--- a/src/pl/plpgsql/src/pl_reserved_kwlist.h
+++ b/src/pl/plpgsql/src/pl_reserved_kwlist.h
@@ -44,6 +44,7 @@ PG_KEYWORD("loop", K_LOOP)
PG_KEYWORD("not", K_NOT)
PG_KEYWORD("null", K_NULL)
PG_KEYWORD("or", K_OR)
+PG_KEYWORD("pragma", K_PRAGMA)
PG_KEYWORD("strict", K_STRICT)
PG_KEYWORD("then", K_THEN)
PG_KEYWORD("to", K_TO)
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index 0a5fbfa9d6..9eee915cd4 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -499,6 +499,7 @@ typedef struct PLpgSQL_stmt_block
int n_initvars; /* Length of initvarnos[] */
int *initvarnos; /* dnos of variables declared in this block */
PLpgSQL_exception_block *exceptions;
+ List *pragmas; /* list of pragmas */
} PLpgSQL_stmt_block;
/*
@@ -1158,6 +1159,35 @@ typedef struct PLwdatum
List *idents; /* valid if composite name */
} PLwdatum;
+typedef enum PLpgSQL_pragma_arg_type
+{
+ PLPGSQL_PRAGMA_ARG_IDENT,
+ PLPGSQL_PRAGMA_ARG_QUAL_IDENT,
+ PLPGSQL_PRAGMA_ARG_SCONST,
+ PLPGSQL_PRAGMA_ARG_FCONST,
+ PLPGSQL_PRAGMA_ARG_ICONST,
+} PLpgSQL_pragma_arg_type;
+
+typedef struct PLpgSQL_pragma_arg
+{
+ char *argname;
+ PLpgSQL_pragma_arg_type type;
+ union
+ {
+ char *ident;
+ List *idents;
+ int ival;
+ double fval;
+ char *str;
+ } val;
+} PLpgSQL_pragma_arg;
+
+typedef struct PLpgSQL_pragma
+{
+ char *name; /* name of pragma */
+ List *args;
+} PLpgSQL_pragma;
+
/**********************************************************************
* Global variable declarations
**********************************************************************/
diff --git a/src/pl/plpgsql/src/sql/plpgsql_pragma.sql b/src/pl/plpgsql/src/sql/plpgsql_pragma.sql
new file mode 100644
index 0000000000..ffe5c7664a
--- /dev/null
+++ b/src/pl/plpgsql/src/sql/plpgsql_pragma.sql
@@ -0,0 +1,11 @@
+do $$
+DECLARE
+ var int;
+ PRAGMA xxx;
+ PRAGMA do;
+ PRAGMA var; -- name can be any identifier
+ PRAGMA xxx(10, 10.1, 'aaaa', "aaaaa".aaaa, off, on); -- supported types
+ PRAGMA xxx(label => value);
+BEGIN
+END;
+$$;