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. + + + Block level PRAGMA + + + A PL/pgSQL function supports pragma on block + level. Pragma is a compiler directive, that can be used by + PL/pgSQL compiler, or by any extensions that + can work with PL/pgSQL code. + + + +PRAGMA name; +PRAGMA name ( argument_name => value ); + + + + The pragma can be used for plpgsql_check + enabling/disabling code checking or for storing additional information: + + +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 + ... + + + More details are described in related extension's description. + + + + Unknown pragma is ignored. + + 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 decl_sect @@ -221,6 +229,9 @@ static void check_raise_parameters(PLpgSQL_stmt_raise *stmt); %type unreserved_keyword +%type pragma_args +%type pragma_arg +%type 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 K_PG_EXCEPTION_CONTEXT %token K_PG_EXCEPTION_DETAIL %token K_PG_EXCEPTION_HINT +%token K_PRAGMA %token K_PRINT_STRICT_PARAMS %token K_PRIOR %token 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; +$$;