Hi

st 12. 12. 2018 v 9:03 odesílatel Pavel Stehule <pavel.steh...@gmail.com>
napsal:

>
>
> čt 6. 12. 2018 v 18:27 odesílatel Pavel Stehule <pavel.steh...@gmail.com>
> napsal:
>
>>
>>
>> čt 6. 12. 2018 v 18:17 odesílatel Robert Haas <robertmh...@gmail.com>
>> napsal:
>>
>>> On Thu, Dec 6, 2018 at 12:13 PM Pavel Stehule <pavel.steh...@gmail.com>
>>> wrote:
>>> > My idea about plpgsql PRAGMA is very close to PL/SQL or Ada PRAGMA.
>>> This is not runtime statement - the information from this command will be
>>> assigned to related object - function, block, command at parser time.
>>>
>>> That's sensible, but the syntax you were proposing didn't look like it
>>> was related to a specific statement.  I was objecting to the idea that
>>> PRAGMA whatever; should be construed as an annotation of,
>>> specifically, the following statement.
>>>
>>
>> please, can you propose, some what you like?
>>
>> For my purpose I can imagine PRAGMA on function level with same syntax
>> like PL/SQL - I need to push somewhere some information that I can use for
>> plpgsql_check to protect users against false alarms. The locality in this
>> moment is not too important for me. But I prefer solution that doesn't
>> looks too strange, and is possible just with change plpgsql parser.
>>
>
> I looked to some documentation - and for example - the PL/SQL PRAGMA
> inline looks very similar to what I propose.
>
> For me good enough is block level pragma used in DECLARE section
>
> DECLARE
>   pragma plpgsql_check(OFF);
> BEGIN
>   .. this part will not be checked ..
> END;
>
> The syntax will be prepared for future PL/SQL pragmas like
> AUTONOMOUS_TRANSACTION, ..
>

here is block only level PRAGMA - available only from DECLARE part.

The informations are attached to PLpgSQL_stmt_block as List's field pragmas;

Note, if somebody will write support for autonomous transactions, then then
the PL/SQL syntax will be prepared. But my motivation is primary for some
possibility to push some parameters to plpgsql extensions with user
friendly persistent natural readable way.

Regards

Pavel


>
> Regards
>
> Pavel
>
>>
>> Regards
>>
>> Pavel
>>
>>
>>> --
>>> Robert Haas
>>> EnterpriseDB: http://www.enterprisedb.com
>>> The Enterprise PostgreSQL Company
>>>
>>
diff --git a/doc/src/sgml/plpgsql.sgml b/doc/src/sgml/plpgsql.sgml
index 1f2abbb5d1..fc95d3e950 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 25a5a9d448..0c97ddbb12 100644
--- a/src/pl/plpgsql/src/Makefile
+++ b/src/pl/plpgsql/src/Makefile
@@ -27,7 +27,7 @@ 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_varprops
+	plpgsql_cache plpgsql_transaction plpgsql_varprops plpgsql_pragma
 
 all: all-lib
 
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 a979a5109d..53e707bdd5 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
@@ -417,6 +429,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;
 
@@ -435,6 +448,7 @@ decl_sect		: opt_block_label
 						$$.label	  = $1;
 						$$.n_initvars = 0;
 						$$.initvarnos = NULL;
+						$$.pragmas	  = NIL;
 					}
 				| opt_block_label decl_start
 					{
@@ -442,6 +456,7 @@ decl_sect		: opt_block_label
 						$$.label	  = $1;
 						$$.n_initvars = 0;
 						$$.initvarnos = NULL;
+						$$.pragmas	  = NIL;
 					}
 				| opt_block_label decl_start decl_stmts
 					{
@@ -449,6 +464,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;
 					}
 				;
 
@@ -578,6 +596,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 :
@@ -2395,11 +2519,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_scanner.c b/src/pl/plpgsql/src/pl_scanner.c
index ab18946847..e4bad0db7a 100644
--- a/src/pl/plpgsql/src/pl_scanner.c
+++ b/src/pl/plpgsql/src/pl_scanner.c
@@ -86,6 +86,7 @@ static const ScanKeyword reserved_keywords[] = {
 	PG_KEYWORD("not", K_NOT, RESERVED_KEYWORD)
 	PG_KEYWORD("null", K_NULL, RESERVED_KEYWORD)
 	PG_KEYWORD("or", K_OR, RESERVED_KEYWORD)
+	PG_KEYWORD("pragma", K_PRAGMA, RESERVED_KEYWORD)
 	PG_KEYWORD("strict", K_STRICT, RESERVED_KEYWORD)
 	PG_KEYWORD("then", K_THEN, RESERVED_KEYWORD)
 	PG_KEYWORD("to", K_TO, RESERVED_KEYWORD)
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index 42177ccaa6..be9b1503ff 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -491,6 +491,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;
 
 /*
@@ -1119,6 +1120,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