Hi
>
> >
> > some examples based on Ada doc
> >
> > FUNCTION xxx RETURN int AS
> > PRAGMA yyy -- pragma has function scope
> > BEGIN
> >
> > FUNCTION xxx RETURN int AS
> > BEGIN
> > DECLARE
> > PRAGMA yyy -- pragma has block scope
>
> ok, sub-block makes sense over statement level IMO.
>
I am sending proof concept (parser only implementation) - it allows to
control query plan usage on function and on block level
Examples
CREATE OR REPLACE FUNCTION fx()
RETURNS int AS $$
PRAGMA use_query_plan_cache(off); -- disable query plan cache on function
level
DECLARE r record;
BEGIN
FOR r IN SELECT ... -- some complex query, where we prefer on one shot
plan
LOOP
DECLARE
PRAGMA use_query_plan_cache(on); -- enable query plan cache for block
BEGIN
... statements inside cycle reuses query plan
END;
END LOOP;
END;
$$ LANGUAGE plpgsql;
or
BEGIN
...
DECLARE
PRAGMA use_query_plan_cache(off);
BEGIN
-- these queries has fresh plan only
SELECT ...
SELECT ...
END; -- end of PRAGMA scope
...
-- usual behave
END;
The behave is static - controlled on compile time only - the controlled
feature can be enabled/disabled. The impact on runtime is zero
* the syntax is verbose - readable - I prefer strong clean signal for
readers so something internals is different
* consistent with Ada, PL/SQL
* remove one reason for dynamic SQL
* allows to mix queries with without query plan cache - interesting for
patter FOR IN slow query LOOP fast query; END LOOP;
* there is small risk of compatibility break - if somebody use variables
named PRAGMA, because new reserved keyword is necessary - fails on syntax
error - so it is easy identified.
* this syntax can be reused - autonomous_transaction like PL/SQL. I read a
manual of Gnu Ada - and this is used often for implementation legacy
(obsolete) behave, functionality.
Notes, comments?
Regards
Pavel
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 3c52d71..a5fd040 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -288,6 +288,7 @@ do_compile(FunctionCallInfo fcinfo,
int *in_arg_varnos = NULL;
PLpgSQL_variable **out_arg_variables;
MemoryContext func_cxt;
+ PLpgSQL_settings settings;
/*
* Setup the scanner input and error info. We assume that this function
@@ -373,6 +374,11 @@ do_compile(FunctionCallInfo fcinfo,
plpgsql_DumpExecTree = false;
plpgsql_start_datums();
+ /* Prepare default for PRAGMA directives */
+ settings.prev = NULL;
+ settings.use_query_plan_cache = true;
+ plpgsql_settings_init(&settings);
+
switch (function->fn_is_trigger)
{
case PLPGSQL_NOT_TRIGGER:
@@ -796,6 +802,7 @@ plpgsql_compile_inline(char *proc_source)
PLpgSQL_variable *var;
int parse_rc;
MemoryContext func_cxt;
+ PLpgSQL_settings settings;
/*
* Setup the scanner input and error info. We assume that this function
@@ -851,6 +858,11 @@ plpgsql_compile_inline(char *proc_source)
plpgsql_DumpExecTree = false;
plpgsql_start_datums();
+ /* Prepare default for PRAGMA directives */
+ settings.prev = NULL;
+ settings.use_query_plan_cache = true;
+ plpgsql_settings_init(&settings);
+
/* Set up as though in a function returning VOID */
function->fn_rettype = VOIDOID;
function->fn_retset = false;
diff --git a/src/pl/plpgsql/src/pl_funcs.c b/src/pl/plpgsql/src/pl_funcs.c
index 906fe01..c7ee968 100644
--- a/src/pl/plpgsql/src/pl_funcs.c
+++ b/src/pl/plpgsql/src/pl_funcs.c
@@ -33,6 +33,7 @@
* ----------
*/
static PLpgSQL_nsitem *ns_top = NULL;
+static PLpgSQL_settings *settings_top = NULL;
/* ----------
@@ -226,6 +227,66 @@ plpgsql_ns_find_nearest_loop(PLpgSQL_nsitem *ns_cur)
/*
+ * Compilator settings routines
+ */
+
+void
+plpgsql_settings_init(PLpgSQL_settings *defval)
+{
+ settings_top = defval;
+}
+
+/*
+ * Creates new settings based on previous settings
+ */
+void
+plpgsql_settings_clone(void)
+{
+ PLpgSQL_settings *new = palloc(sizeof(PLpgSQL_settings));
+
+ Assert(settings_top != NULL);
+
+ memcpy(new, settings_top, sizeof(PLpgSQL_settings));
+ new->prev = settings_top;
+ settings_top = new;
+}
+
+/*
+ * apply a pragma to current settings
+ */
+void
+plpgsql_settings_pragma(PLpgSQL_pragma_type typ, bool value)
+{
+ Assert(settings_top != NULL);
+
+ switch (typ)
+ {
+ case PLPGSQL_PRAGMA_QUERY_PLAN_CACHE:
+ settings_top->use_query_plan_cache = value;
+ }
+}
+
+/*
+ * restore previous compiler settings
+ */
+void
+plpgsql_settings_pop(void)
+{
+ PLpgSQL_settings *prev;
+
+ Assert(settings_top != NULL);
+ prev = settings_top->prev;
+ pfree(settings_top);
+ settings_top = prev;
+}
+
+PLpgSQL_settings *
+plpgsql_settings_top(void)
+{
+ return settings_top;
+}
+
+/*
* Statement type as a string, for use in error messages etc.
*/
const char *
@@ -1534,7 +1595,7 @@ dump_getdiag(PLpgSQL_stmt_getdiag *stmt)
static void
dump_expr(PLpgSQL_expr *expr)
{
- printf("'%s'", expr->query);
+ printf("%s'%s'", expr->use_query_plan_cache ? "*" : "", expr->query);
}
void
diff --git a/src/pl/plpgsql/src/pl_gram.y b/src/pl/plpgsql/src/pl_gram.y
index 4a4cd6a..2e8287b 100644
--- a/src/pl/plpgsql/src/pl_gram.y
+++ b/src/pl/plpgsql/src/pl_gram.y
@@ -216,6 +216,8 @@ static void check_raise_parameters(PLpgSQL_stmt_raise *stmt);
%type <ival> opt_scrollable
%type <fetch> opt_fetch_direction
+%type <boolean> onoff
+
%type <keyword> unreserved_keyword
@@ -306,6 +308,8 @@ static void check_raise_parameters(PLpgSQL_stmt_raise *stmt);
%token <keyword> K_NOT
%token <keyword> K_NOTICE
%token <keyword> K_NULL
+%token <keyword> K_OFF
+%token <keyword> K_ON
%token <keyword> K_OPEN
%token <keyword> K_OPTION
%token <keyword> K_OR
@@ -315,9 +319,11 @@ 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
+%token <keyword> K_QUERY_PLAN_CACHE
%token <keyword> K_RAISE
%token <keyword> K_RELATIVE
%token <keyword> K_RESULT_OID
@@ -348,9 +354,9 @@ static void check_raise_parameters(PLpgSQL_stmt_raise *stmt);
%%
-pl_function : comp_options pl_block opt_semi
+pl_function : comp_options pragmas_opt pl_block opt_semi
{
- plpgsql_parse_result = (PLpgSQL_stmt_block *) $2;
+ plpgsql_parse_result = (PLpgSQL_stmt_block *) $3;
}
;
@@ -398,6 +404,30 @@ opt_semi :
| ';'
;
+onoff : K_ON
+ {
+ $$ = true;
+ }
+ | K_OFF
+ {
+ $$ = false;
+ }
+ ;
+
+pragma : K_PRAGMA K_QUERY_PLAN_CACHE '(' onoff ')' ';'
+ {
+ plpgsql_settings_pragma(PLPGSQL_PRAGMA_QUERY_PLAN_CACHE, $4);
+ }
+ ;
+
+pragmas : pragmas pragma
+ | pragma
+ ;
+
+pragmas_opt :
+ | pragmas
+ ;
+
pl_block : decl_sect K_BEGIN proc_sect exception_sect K_END opt_label
{
PLpgSQL_stmt_block *new;
@@ -414,6 +444,7 @@ pl_block : decl_sect K_BEGIN proc_sect exception_sect K_END opt_label
check_labels($1.label, $6, @6);
plpgsql_ns_pop();
+ plpgsql_settings_pop();
$$ = (PLpgSQL_stmt *)new;
}
@@ -448,6 +479,10 @@ decl_start : K_DECLARE
{
/* Forget any variables created before block */
plpgsql_add_initdatums(NULL);
+
+ /* clone compiler settings */
+ plpgsql_settings_clone();
+
/*
* Disable scanner lookup of identifiers while
* we process the decl_stmts
@@ -476,6 +511,7 @@ decl_stmt : decl_statement
errmsg("block label must be placed before DECLARE, not after"),
parser_errposition(@1)));
}
+ | pragma
;
decl_statement : decl_varname decl_const decl_datatype decl_collate decl_notnull decl_defval
@@ -579,6 +615,7 @@ decl_statement : decl_varname decl_const decl_datatype decl_collate decl_notnull
}
strcpy(cp2, "'::pg_catalog.refcursor");
curname_def->query = pstrdup(buf);
+ curname_def->use_query_plan_cache = plpgsql_settings_top()->use_query_plan_cache;
new->default_val = curname_def;
new->cursor_explicit_expr = $7;
@@ -2442,6 +2479,8 @@ unreserved_keyword :
| K_NEXT
| K_NO
| K_NOTICE
+ | K_OFF
+ | K_ON
| K_OPEN
| K_OPTION
| K_PERFORM
@@ -2453,6 +2492,7 @@ unreserved_keyword :
| K_PRINT_STRICT_PARAMS
| K_PRIOR
| K_QUERY
+ | K_QUERY_PLAN_CACHE
| K_RAISE
| K_RELATIVE
| K_RESULT_OID
@@ -2689,6 +2729,7 @@ read_sql_construct(int until,
expr->paramnos = NULL;
expr->rwparam = -1;
expr->ns = plpgsql_ns_top();
+ expr->use_query_plan_cache = plpgsql_settings_top()->use_query_plan_cache;
pfree(ds.data);
if (valid_sql)
@@ -2937,6 +2978,7 @@ make_execsql_stmt(int firsttoken, int location)
expr->paramnos = NULL;
expr->rwparam = -1;
expr->ns = plpgsql_ns_top();
+ expr->use_query_plan_cache = plpgsql_settings_top()->use_query_plan_cache;
pfree(ds.data);
check_sql_expr(expr->query, location, 0);
@@ -3821,6 +3863,7 @@ read_cursor_args(PLpgSQL_var *cursor, int until, const char *expected)
expr->paramnos = NULL;
expr->rwparam = -1;
expr->ns = plpgsql_ns_top();
+ expr->use_query_plan_cache = plpgsql_settings_top()->use_query_plan_cache;
pfree(ds.data);
/* Next we'd better find the until token */
diff --git a/src/pl/plpgsql/src/pl_scanner.c b/src/pl/plpgsql/src/pl_scanner.c
index c401213..cf5bd39 100644
--- a/src/pl/plpgsql/src/pl_scanner.c
+++ b/src/pl/plpgsql/src/pl_scanner.c
@@ -84,6 +84,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)
@@ -139,6 +140,8 @@ static const ScanKeyword unreserved_keywords[] = {
PG_KEYWORD("next", K_NEXT, UNRESERVED_KEYWORD)
PG_KEYWORD("no", K_NO, UNRESERVED_KEYWORD)
PG_KEYWORD("notice", K_NOTICE, UNRESERVED_KEYWORD)
+ PG_KEYWORD("off", K_OFF, UNRESERVED_KEYWORD)
+ PG_KEYWORD("on", K_ON, UNRESERVED_KEYWORD)
PG_KEYWORD("open", K_OPEN, UNRESERVED_KEYWORD)
PG_KEYWORD("option", K_OPTION, UNRESERVED_KEYWORD)
PG_KEYWORD("perform", K_PERFORM, UNRESERVED_KEYWORD)
@@ -150,6 +153,7 @@ static const ScanKeyword unreserved_keywords[] = {
PG_KEYWORD("print_strict_params", K_PRINT_STRICT_PARAMS, UNRESERVED_KEYWORD)
PG_KEYWORD("prior", K_PRIOR, UNRESERVED_KEYWORD)
PG_KEYWORD("query", K_QUERY, UNRESERVED_KEYWORD)
+ PG_KEYWORD("query_plan_cache", K_QUERY_PLAN_CACHE, UNRESERVED_KEYWORD)
PG_KEYWORD("raise", K_RAISE, UNRESERVED_KEYWORD)
PG_KEYWORD("relative", K_RELATIVE, UNRESERVED_KEYWORD)
PG_KEYWORD("result_oid", K_RESULT_OID, UNRESERVED_KEYWORD)
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index 3421eed..e3c4435 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -35,6 +35,14 @@
#define _(x) dgettext(TEXTDOMAIN, x)
/*
+ * Compiler directives
+ */
+typedef enum PLpgSQL_pragma_type
+{
+ PLPGSQL_PRAGMA_QUERY_PLAN_CACHE
+} PLpgSQL_pragma_type;
+
+/*
* Compiler's namespace item types
*/
typedef enum PLpgSQL_nsitem_type
@@ -224,6 +232,7 @@ typedef struct PLpgSQL_expr
int dno;
char *query;
SPIPlanPtr plan;
+ bool use_query_plan_cache;
Bitmapset *paramnos; /* all dnos referenced by this query */
int rwparam; /* dno of read/write param, or -1 if none */
@@ -1004,6 +1013,15 @@ typedef struct PLwdatum
List *idents; /* valid if composite name */
} PLwdatum;
+/*
+ * Compiler directives
+ */
+typedef struct PLpgSQL_settings
+{
+ struct PLpgSQL_settings *prev;
+ bool use_query_plan_cache;
+} PLpgSQL_settings;
+
/**********************************************************************
* Global variable declarations
**********************************************************************/
@@ -1123,6 +1141,15 @@ extern PLpgSQL_nsitem *plpgsql_ns_lookup_label(PLpgSQL_nsitem *ns_cur,
extern PLpgSQL_nsitem *plpgsql_ns_find_nearest_loop(PLpgSQL_nsitem *ns_cur);
/*
+ * Function for compiler directives processing in pl_func.c
+ */
+extern void plpgsql_settings_init(PLpgSQL_settings *defval);
+extern void plpgsql_settings_clone(void);
+extern void plpgsql_settings_pragma(PLpgSQL_pragma_type typ, bool value);
+extern PLpgSQL_settings *plpgsql_settings_top(void);
+extern void plpgsql_settings_pop(void);
+
+/*
* Other functions in pl_funcs.c
*/
extern const char *plpgsql_stmt_typename(PLpgSQL_stmt *stmt);
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers