Hi, po 4. 2. 2019 v 6:10 odesÃlatel Michael Paquier <mich...@paquier.xyz> napsal:
> On Fri, Jan 04, 2019 at 02:17:49PM +0100, Pavel Stehule wrote: > > It means to write own lexer and preparse source code before I start > > checking. > > > > I think so block level PRAGMA is significantly better solution > > Please note that the latest patch is failing to apply, so I have moved > the patch to next CF, waiting on author. > attached rebased patch thank you for checking. Regards Pavel -- > Michael >
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; +$$;