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> => </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; +$$;