čt 7. 3. 2019 v 18:45 odesílatel Andrew Dunstan < andrew.duns...@2ndquadrant.com> napsal:
> > On 3/7/19 12:41 PM, Pavel Stehule wrote: > > > > > > čt 7. 3. 2019 v 18:35 odesílatel Andrew Dunstan > > <andrew.duns...@2ndquadrant.com > > <mailto:andrew.duns...@2ndquadrant.com>> napsal: > > > > > > > > > > The other thing that bugs me a bit about the patch is that the only > > testing it does it to make sure that pragmas are ignored by the core > > plpgsql processor. Maybe that's enough, but mostly we tend to like to > > have one actual use of a feature. > > > > > > Unfortunately plpgsql_check is not part of upstream > > > > https://github.com/okbob/plpgsql_check > > > > I can to write some simple extension - some print tracing, that can > > use this feature? > > > > > > Works for me. Another idea I had was some sort of crypto signature pragma. > Here is pragma patch with demo Regards Pavel > > > I still think making it block level only is unwarranted, though. > > > cheers > > > andrew > > > -- > Andrew Dunstan https://www.2ndQuadrant.com > PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services > >
diff --git a/contrib/Makefile b/contrib/Makefile index 92184ed487..a5edd12e35 100644 --- a/contrib/Makefile +++ b/contrib/Makefile @@ -39,6 +39,7 @@ SUBDIRS = \ pgrowlocks \ pgstattuple \ pg_visibility \ + plpgsql_tracer \ postgres_fdw \ seg \ spi \ diff --git a/contrib/plpgsql_tracer/.gitignore b/contrib/plpgsql_tracer/.gitignore new file mode 100644 index 0000000000..5dcb3ff972 --- /dev/null +++ b/contrib/plpgsql_tracer/.gitignore @@ -0,0 +1,4 @@ +# Generated subdirectories +/log/ +/results/ +/tmp_check/ diff --git a/contrib/plpgsql_tracer/Makefile b/contrib/plpgsql_tracer/Makefile new file mode 100644 index 0000000000..7900471ed6 --- /dev/null +++ b/contrib/plpgsql_tracer/Makefile @@ -0,0 +1,25 @@ +# contrib/plpgsql_tracer/Makefile + +MODULES = plpgsql_tracer + +EXTENSION = plpgsql_tracer +PGFILEDESC = "plpgsql tracer - example of PRAGMA based extension" + +REGRESS = plpgsql_tracer + +ifdef USE_PGXS +PG_CONFIG = pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) +else +subdir = contrib/citext +top_builddir = ../.. +include $(top_builddir)/src/Makefile.global +include $(top_srcdir)/contrib/contrib-global.mk +endif + +ifeq ($(PORTNAME), darwin) +override CFLAGS += -undefined dynamic_lookup +endif + +override CFLAGS += -I$(top_builddir)/src/pl/plpgsql/src -Wall diff --git a/contrib/plpgsql_tracer/expected/plpgsql_tracer.out b/contrib/plpgsql_tracer/expected/plpgsql_tracer.out new file mode 100644 index 0000000000..d183fcebef --- /dev/null +++ b/contrib/plpgsql_tracer/expected/plpgsql_tracer.out @@ -0,0 +1,38 @@ +-- +-- Test plpgsql_tracer extension +-- +load 'plpgsql'; +load 'plpgsql_tracer'; +create or replace function fx() +returns integer +language plpgsql +AS $function$ +declare + x int = 0; + pragma plpgsql_tracer(on); + pragma ignore_me; +begin + x := x + 1; + x := x + 1; + declare + pragma plpgsql_tracer(off); + begin + -- hidden statemt + declare pragma plpgsql_tracer; -- ignored, just warning + begin + x := x + 1; + end; + end; + return x; +end; +$function$; +select fx(); +WARNING: missing argument of PRAGMA plpgsql_tracer +NOTICE: plpgsql_tracer: 7: assignment +NOTICE: plpgsql_tracer: 8: assignment +NOTICE: plpgsql_tracer: 18: RETURN + fx +---- + 3 +(1 row) + diff --git a/contrib/plpgsql_tracer/plpgsql_tracer.c b/contrib/plpgsql_tracer/plpgsql_tracer.c new file mode 100644 index 0000000000..03d4ed8a62 --- /dev/null +++ b/contrib/plpgsql_tracer/plpgsql_tracer.c @@ -0,0 +1,248 @@ +/* + * contrib/plpgsql_tracer/plpgsql_tracer.c + */ +#include "postgres.h" +#include "plpgsql.h" +#include "fmgr.h" + +#include "nodes/bitmapset.h" + +PG_MODULE_MAGIC; + + +#define PLPGSQL_TRACER_MAGIC 73071522 + +typedef struct plpgsql_tracer_data +{ + int magic; + Bitmapset *stmtids; +} plpgsql_tracer_data; + + +static void collect_stmtid(PLpgSQL_stmt *stmt, plpgsql_tracer_data *data, bool trace); + +static void plpgsql_tracer_func_init(PLpgSQL_execstate *estate, PLpgSQL_function *func); +static void plpgsql_tracer_stmt_beg(PLpgSQL_execstate *estate, PLpgSQL_stmt *stmt); + +static PLpgSQL_plugin plugin_funcs = { plpgsql_tracer_func_init, + NULL, + NULL, + plpgsql_tracer_stmt_beg, + NULL, + NULL, + NULL}; + +/* + * Collect traced statement id from list of statements. + */ +static void +collect_stmtid_list(List *stmts, + plpgsql_tracer_data *data, + bool trace) +{ + ListCell *lc; + + foreach(lc, stmts) + { + collect_stmtid((PLpgSQL_stmt *) lfirst(lc), data, trace); + } +} + + +/* + * It is iterate over all plpgsql statements and collect stmtid of statements + * inside blocks marked by PRAGMA trace. + */ +static void +collect_stmtid(PLpgSQL_stmt *stmt, + plpgsql_tracer_data *data, + bool trace) +{ + switch (stmt->cmd_type) + { + case PLPGSQL_STMT_BLOCK: + { + PLpgSQL_stmt_block *stmt_block = (PLpgSQL_stmt_block *) stmt; + ListCell *lc; + + foreach (lc, stmt_block->pragmas) + { + PLpgSQL_pragma *pragma = (PLpgSQL_pragma *) lfirst(lc); + + if (strcmp(pragma->name, "plpgsql_tracer") == 0) + { + ListCell *larg; + char *value = NULL; + int count = 0; + + /* this pragma requires just on/off parameter */ + foreach(larg, pragma->args) + { + PLpgSQL_pragma_arg *arg = (PLpgSQL_pragma_arg *) lfirst(larg); + + if (count++ > 0) + { + elog(WARNING, "PRAGMA plpgsql_tracer has only one parameter"); + break; + } + + if (arg->argname) + { + elog(WARNING, "PRAGMA plpgsql_tracer hasn't named parameters"); + break; + } + + if (arg->type == PLPGSQL_PRAGMA_ARG_IDENT) + value = arg->val.ident; + else if (arg->type == PLPGSQL_PRAGMA_ARG_SCONST) + value = arg->val.str; + else + { + elog(WARNING, "unsupported type of PRAGMA plpgsql_tracer"); + break; + } + + if (value) + { + if ((strcmp(value, "on") == 0) || + (strcmp(value, "true") == 0) || + (strcmp(value, "t") == 0)) + trace = true; + else if ((strcmp(value, "off") == 0) || + (strcmp(value, "false") == 0) || + (strcmp(value, "f") == 0)) + trace = false; + else + { + elog(WARNING, "unknown value of PRAGMA plpgsql_tracer parameter"); + break; + } + } + } + + if (count < 1) + elog(WARNING, "missing argument of PRAGMA plpgsql_tracer"); + } + } + + collect_stmtid_list(stmt_block->body, data, trace); + } + break; + + case PLPGSQL_STMT_IF: + { + PLpgSQL_stmt_if *stmt_if = (PLpgSQL_stmt_if *) stmt; + ListCell *lc; + + collect_stmtid_list(stmt_if->then_body, data, trace); + foreach(lc, stmt_if->elsif_list) + { + collect_stmtid_list(((PLpgSQL_if_elsif *) lfirst(lc))->stmts, + data, + trace); + } + collect_stmtid_list(stmt_if->else_body, data, trace); + } + break; + + case PLPGSQL_STMT_CASE: + { + PLpgSQL_stmt_case *stmt_case = (PLpgSQL_stmt_case *) stmt; + ListCell *lc; + + foreach(lc, stmt_case->case_when_list) + { + collect_stmtid_list(((PLpgSQL_case_when *) lfirst(lc))->stmts, + data, + trace); + } + collect_stmtid_list(stmt_case->else_stmts, data, trace); + } + break; + + case PLPGSQL_STMT_LOOP: + collect_stmtid_list(((PLpgSQL_stmt_while *) stmt)->body, data, trace); + break; + + case PLPGSQL_STMT_FORI: + collect_stmtid_list(((PLpgSQL_stmt_fori *) stmt)->body, data, trace); + break; + + case PLPGSQL_STMT_FORS: + collect_stmtid_list(((PLpgSQL_stmt_fors *) stmt)->body, data, trace); + break; + + case PLPGSQL_STMT_FORC: + collect_stmtid_list(((PLpgSQL_stmt_forc *) stmt)->body, data, trace); + break; + + case PLPGSQL_STMT_DYNFORS: + collect_stmtid_list(((PLpgSQL_stmt_dynfors *) stmt)->body, data, trace); + break; + + case PLPGSQL_STMT_FOREACH_A: + collect_stmtid_list(((PLpgSQL_stmt_foreach_a *) stmt)->body, data, trace); + break; + + default: + /* do nothing */ + break; + } + + if (trace && stmt->cmd_type != PLPGSQL_STMT_BLOCK) + data->stmtids = bms_add_member(data->stmtids, stmt->stmtid); +} + +/* + * Define functions for PL debug API + */ +static void +plpgsql_tracer_func_init(PLpgSQL_execstate *estate, PLpgSQL_function *func) +{ + plpgsql_tracer_data *tdata; + + tdata = palloc0(sizeof(plpgsql_tracer_data)); + tdata->magic = PLPGSQL_TRACER_MAGIC; + + estate->plugin_info = tdata; + + collect_stmtid((PLpgSQL_stmt *) func->action, tdata, false); +} + +static void +plpgsql_tracer_stmt_beg(PLpgSQL_execstate *estate, PLpgSQL_stmt *stmt) +{ + plpgsql_tracer_data *tdata = (plpgsql_tracer_data *) estate->plugin_info; + + if (tdata->magic == PLPGSQL_TRACER_MAGIC) + { + /* now, its our data */ + if (bms_is_member(stmt->stmtid, tdata->stmtids)) + { + elog(NOTICE, + "plpgsql_tracer: %4d: %s", + stmt->lineno, + plpgsql_stmt_typename(stmt)); + } + } +} + +/* + * Module initialization + * + * setup PLpgSQL_plugin API + */ +void +_PG_init(void) +{ + PLpgSQL_plugin **var_ptr; + static bool inited = false; + + if (inited) + return; + + var_ptr = (PLpgSQL_plugin **) find_rendezvous_variable("PLpgSQL_plugin"); + *var_ptr = &plugin_funcs; + + inited = true; +} diff --git a/contrib/plpgsql_tracer/plpgsql_tracer.control b/contrib/plpgsql_tracer/plpgsql_tracer.control new file mode 100644 index 0000000000..9f0fe46596 --- /dev/null +++ b/contrib/plpgsql_tracer/plpgsql_tracer.control @@ -0,0 +1,5 @@ +# plpgsql_tracer extension +comment = 'tracing functionality for plpgsql' +default_version = '1.0' +module_pathname = '$libdir/plpgsql_tracer' +relocatable = true diff --git a/contrib/plpgsql_tracer/sql/plpgsql_tracer.sql b/contrib/plpgsql_tracer/sql/plpgsql_tracer.sql new file mode 100644 index 0000000000..70528e0583 --- /dev/null +++ b/contrib/plpgsql_tracer/sql/plpgsql_tracer.sql @@ -0,0 +1,32 @@ +-- +-- Test plpgsql_tracer extension +-- + +load 'plpgsql'; +load 'plpgsql_tracer'; + +create or replace function fx() +returns integer +language plpgsql +AS $function$ +declare + x int = 0; + pragma plpgsql_tracer(on); + pragma ignore_me; +begin + x := x + 1; + x := x + 1; + declare + pragma plpgsql_tracer(off); + begin + -- hidden statemt + declare pragma plpgsql_tracer; -- ignored, just warning + begin + x := x + 1; + end; + end; + return x; +end; +$function$; + +select fx(); 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; +$$;