so 9. 3. 2019 v 22:17 odesílatel Pavel Stehule <pavel.steh...@gmail.com>
napsal:

>
>
> čt 7. 3. 2019 v 18:45 odesílatel Andrew Dunstan <
> andrew.duns...@2ndquadrant.com> napsal:
>
>>
>> On 3/7/19 12:41 PM, Pavel Stehule wrote:
>> >
>> >
>> > čt 7. 3. 2019 v 18:35 odesílatel Andrew Dunstan
>> > <andrew.duns...@2ndquadrant.com
>> > <mailto:andrew.duns...@2ndquadrant.com>> 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
>

fixed check-world

Regards

Pavel


> 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..aedbd4f20f
--- /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/plpgsql_tracer
+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> =&gt; </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;
+$$;

Reply via email to