Hi
st 12. 12. 2018 v 9:03 odesílatel Pavel Stehule <[email protected]>
napsal:
>
>
> čt 6. 12. 2018 v 18:27 odesílatel Pavel Stehule <[email protected]>
> napsal:
>
>>
>>
>> čt 6. 12. 2018 v 18:17 odesílatel Robert Haas <[email protected]>
>> napsal:
>>
>>> On Thu, Dec 6, 2018 at 12:13 PM Pavel Stehule <[email protected]>
>>> 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> => </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;
+$$;