提交 f78c920c 编写于 作者: P Peter Eisentraut 提交者: Ekta Khanna

Add plan_cache_mode setting

This allows overriding the choice of custom or generic plan.

Author: Pavel Stehule <pavel.stehule@gmail.com>
Discussion: https://www.postgresql.org/message-id/flat/CAFj8pRAGLaiEm8ur5DWEBo7qHRWTk9HxkuUAz00CZZtJj-LkCA%40mail.gmail.com
(cherry picked from commit f7cb2842)
上级 4c8dace3
......@@ -108,6 +108,9 @@ static void PlanCacheRelCallback(Datum arg, Oid relid);
static void PlanCacheFuncCallback(Datum arg, int cacheid, uint32 hashvalue);
static void PlanCacheSysCallback(Datum arg, int cacheid, uint32 hashvalue);
/* GUC parameter */
int plan_cache_mode;
/*
* InitPlanCache: initialize module during InitPostgres.
*
......@@ -1089,6 +1092,12 @@ choose_custom_plan(CachedPlanSource *plansource, ParamListInfo boundParams, Into
if (IsTransactionStmtPlan(plansource))
return false;
/* Let settings force the decision */
if (plan_cache_mode == PLAN_CACHE_MODE_FORCE_GENERIC_PLAN)
return false;
if (plan_cache_mode == PLAN_CACHE_MODE_FORCE_CUSTOM_PLAN)
return true;
/* See if caller wants to force the decision */
if (plansource->cursor_options & CURSOR_OPT_GENERIC_PLAN)
return false;
......
......@@ -406,6 +406,13 @@ static const struct config_enum_entry force_parallel_mode_options[] = {
{NULL, 0, false}
};
static const struct config_enum_entry plan_cache_mode_options[] = {
{"auto", PLAN_CACHE_MODE_AUTO, false},
{"force_generic_plan", PLAN_CACHE_MODE_FORCE_GENERIC_PLAN, false},
{"force_custom_plan", PLAN_CACHE_MODE_FORCE_CUSTOM_PLAN, false},
{NULL, 0, false}
};
/*
* Options for enum values stored in other modules
*/
......@@ -3902,6 +3909,18 @@ static struct config_enum ConfigureNamesEnum[] =
NULL, NULL, NULL
},
{
{"plan_cache_mode", PGC_USERSET, QUERY_TUNING_OTHER,
gettext_noop("Controls the planner's selection of custom or generic plan."),
gettext_noop("Prepared statements can have custom and generic plans, and the planner "
"will attempt to choose which is better. This can be set to override "
"the default behavior.")
},
&plan_cache_mode,
PLAN_CACHE_MODE_AUTO, plan_cache_mode_options,
NULL, NULL, NULL
},
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, NULL, NULL, NULL, NULL
......
......@@ -329,6 +329,7 @@ max_prepared_transactions = 250 # can be 0 or more
#join_collapse_limit = 20 # 1 disables collapsing of explicit
# JOIN clauses
#force_parallel_mode = off
#plan_cache_mode = auto
#gp_segments_for_planner = 0 # if 0, actual number of segments is used
......
......@@ -181,4 +181,15 @@ extern CachedPlan *GetCachedPlan(CachedPlanSource *plansource,
IntoClause *intoClause);
extern void ReleaseCachedPlan(CachedPlan *plan, bool useResOwner);
#endif /* PLANCACHE_H */
/* possible values for plan_cache_mode */
typedef enum
{
PLAN_CACHE_MODE_AUTO,
PLAN_CACHE_MODE_FORCE_GENERIC_PLAN,
PLAN_CACHE_MODE_FORCE_CUSTOM_PLAN
} PlanCacheMode;
/* GUC parameter */
extern int plan_cache_mode;
#endif /* PLANCACHE_H */
......@@ -423,6 +423,7 @@
"optimizer_use_external_constant_expression_evaluation_for_ints",
"optimizer_use_gpdb_allocators",
"password_encryption",
"plan_cache_mode",
"pljava_classpath_insecure",
"pljava_debug",
"port",
......
......@@ -260,3 +260,91 @@ NOTICE: 3
(1 row)
-- Test plan_cache_mode
create table test_mode (a int);
-- GPDB:setting the number of rows slightly higher to get a plan with
-- Index Only Scan (similar to upstream)
insert into test_mode select 1 from generate_series(1,15000) union all select 2;
create index on test_mode (a);
analyze test_mode;
prepare test_mode_pp (int) as select count(*) from test_mode where a = $1;
-- up to 5 executions, custom plan is used
explain (costs off) execute test_mode_pp(2);
QUERY PLAN
----------------------------------------------------------
Aggregate
-> Gather Motion 1:1 (slice1; segments: 1)
-> Index Only Scan using test_mode_a_idx on test_mode
Index Cond: (a = 2)
Optimizer: Postgres query optimizer
(5 rows)
-- force generic plan
set plan_cache_mode to force_generic_plan;
explain (costs off) execute test_mode_pp(2);
QUERY PLAN
------------------------------------------------
Finalize Aggregate
-> Gather Motion 3:1 (slice1; segments: 3)
-> Partial Aggregate
-> Seq Scan on test_mode
Filter: (a = $1)
Optimizer: Postgres query optimizer
(6 rows)
-- get to generic plan by 5 executions
set plan_cache_mode to auto;
execute test_mode_pp(1); -- 1x
count
-------
15000
(1 row)
execute test_mode_pp(1); -- 2x
count
-------
15000
(1 row)
execute test_mode_pp(1); -- 3x
count
-------
15000
(1 row)
execute test_mode_pp(1); -- 4x
count
-------
15000
(1 row)
execute test_mode_pp(1); -- 5x
count
-------
15000
(1 row)
-- we should now get a really bad plan
explain (costs off) execute test_mode_pp(2);
QUERY PLAN
-----------------------------
Aggregate
-> Gather Motion 1:1 (slice1; segments: 1)
-> Index Only Scan using test_mode_a_idx on test_mode
Index Cond: (a = 2)
Optimizer: Postgres query optimizer
(5 rows)
-- but we can force a custom plan
set plan_cache_mode to force_custom_plan;
explain (costs off) execute test_mode_pp(2);
QUERY PLAN
----------------------------------------------------------
Aggregate
-> Gather Motion 1:1 (slice1; segments: 1)
-> Index Only Scan using test_mode_a_idx on test_mode
Index Cond: (a = 2)
Optimizer: Postgres query optimizer
(5 rows)
drop table test_mode;
--
-- Tests to exercise the plan caching/invalidation mechanism
--
CREATE TEMP TABLE pcachetest AS SELECT * FROM int8_tbl;
-- create and use a cached plan
PREPARE prepstmt AS SELECT * FROM pcachetest;
EXECUTE prepstmt;
q1 | q2
------------------+-------------------
123 | 456
123 | 4567890123456789
4567890123456789 | 123
4567890123456789 | 4567890123456789
4567890123456789 | -4567890123456789
(5 rows)
-- and one with parameters
PREPARE prepstmt2(bigint) AS SELECT * FROM pcachetest WHERE q1 = $1;
EXECUTE prepstmt2(123);
q1 | q2
-----+------------------
123 | 456
123 | 4567890123456789
(2 rows)
-- invalidate the plans and see what happens
DROP TABLE pcachetest;
EXECUTE prepstmt;
ERROR: relation "pcachetest" does not exist
EXECUTE prepstmt2(123);
ERROR: relation "pcachetest" does not exist
-- recreate the temp table (this demonstrates that the raw plan is
-- purely textual and doesn't depend on OIDs, for instance)
CREATE TEMP TABLE pcachetest AS SELECT * FROM int8_tbl;
EXECUTE prepstmt;
q1 | q2
------------------+-------------------
4567890123456789 | -4567890123456789
4567890123456789 | 123
123 | 456
123 | 4567890123456789
4567890123456789 | 4567890123456789
(5 rows)
EXECUTE prepstmt2(123);
q1 | q2
-----+------------------
123 | 456
123 | 4567890123456789
(2 rows)
-- prepared statements should prevent change in output tupdesc,
-- since clients probably aren't expecting that to change on the fly
ALTER TABLE pcachetest ADD COLUMN q3 bigint;
EXECUTE prepstmt;
ERROR: cached plan must not change result type
DETAIL: resultDesc is not NULL. plansource->resulDesc is not NULL.
EXECUTE prepstmt2(123);
ERROR: cached plan must not change result type
DETAIL: resultDesc is not NULL. plansource->resulDesc is not NULL.
-- but we're nice guys and will let you undo your mistake
ALTER TABLE pcachetest DROP COLUMN q3;
EXECUTE prepstmt;
q1 | q2
------------------+-------------------
4567890123456789 | -4567890123456789
4567890123456789 | 123
123 | 456
123 | 4567890123456789
4567890123456789 | 4567890123456789
(5 rows)
EXECUTE prepstmt2(123);
q1 | q2
-----+------------------
123 | 456
123 | 4567890123456789
(2 rows)
-- Try it with a view, which isn't directly used in the resulting plan
-- but should trigger invalidation anyway
CREATE TEMP VIEW pcacheview AS
SELECT * FROM pcachetest;
PREPARE vprep AS SELECT * FROM pcacheview;
EXECUTE vprep;
q1 | q2
------------------+-------------------
4567890123456789 | -4567890123456789
4567890123456789 | 123
123 | 456
123 | 4567890123456789
4567890123456789 | 4567890123456789
(5 rows)
CREATE OR REPLACE TEMP VIEW pcacheview AS
SELECT q1, q2/2 AS q2 FROM pcachetest;
EXECUTE vprep;
q1 | q2
------------------+-------------------
4567890123456789 | -2283945061728394
4567890123456789 | 61
123 | 228
123 | 2283945061728394
4567890123456789 | 2283945061728394
(5 rows)
-- Check basic SPI plan invalidation
create function cache_test(int) returns int as $$
declare total int;
begin
create temp table t1(f1 int) distributed by (f1);
insert into t1 values($1);
insert into t1 values(11);
insert into t1 values(12);
insert into t1 values(13);
select sum(f1) into total from t1;
drop table t1;
return total;
end
$$ language plpgsql;
select cache_test(1);
cache_test
------------
37
(1 row)
select cache_test(2);
cache_test
------------
38
(1 row)
select cache_test(3);
cache_test
------------
39
(1 row)
-- Check invalidation of plpgsql "simple expression"
create temp view v1 as
select 2+2 as f1;
create function cache_test_2() returns int as $$
begin
return f1 from v1;
end$$ language plpgsql;
select cache_test_2();
cache_test_2
--------------
4
(1 row)
create or replace temp view v1 as
select 2+2+4 as f1;
select cache_test_2();
cache_test_2
--------------
8
(1 row)
create or replace temp view v1 as
select 2+2+4+(select max(unique1) from tenk1) as f1;
select cache_test_2();
cache_test_2
--------------
10007
(1 row)
--- Check that change of search_path is honored when re-using cached plan
create schema s1
create table abc (f1 int);
create schema s2
create table abc (f1 int);
insert into s1.abc values(123);
insert into s2.abc values(456);
set search_path = s1;
prepare p1 as select f1 from abc;
execute p1;
f1
-----
123
(1 row)
set search_path = s2;
select f1 from abc;
f1
-----
456
(1 row)
execute p1;
f1
-----
456
(1 row)
alter table s1.abc add column f2 float8; -- force replan
execute p1;
f1
-----
456
(1 row)
drop schema s1 cascade;
NOTICE: drop cascades to table s1.abc
drop schema s2 cascade;
NOTICE: drop cascades to table abc
reset search_path;
-- Check that invalidation deals with regclass constants
create temp sequence seq;
prepare p2 as select nextval('seq');
execute p2;
nextval
---------
1
(1 row)
drop sequence seq;
create temp sequence seq;
execute p2;
nextval
---------
1
(1 row)
-- Check DDL via SPI, immediately followed by SPI plan re-use
-- (bug in original coding)
create function cachebug() returns void as $$
declare r int;
begin
drop table if exists temptable cascade;
-- Ignore NOTICE about missing DISTRIBUTED BY. It was annoying here, as
-- usually you would only see it on the first invocation, but sometimes
-- you'd also get it on the second invocation, if the plan cache
-- got invalidated in between the invocations.
set client_min_messages=warning;
create temp table temptable as select * from generate_series(1,3) as f1;
reset client_min_messages;
create temp view vv as select * from temptable;
for r in select * from vv order by f1 loop
raise notice '%', r;
end loop;
end$$ language plpgsql;
select cachebug();
NOTICE: table "temptable" does not exist, skipping
NOTICE: 1
NOTICE: 2
NOTICE: 3
cachebug
----------
(1 row)
select cachebug();
NOTICE: drop cascades to view vv
NOTICE: 1
NOTICE: 2
NOTICE: 3
cachebug
----------
(1 row)
-- Test plan_cache_mode
create table test_mode (a int);
-- GPDB:setting the number of rows slightly higher to get a plan with
-- Index Only Scan (similar to upstream)
insert into test_mode select 1 from generate_series(1,15000) union all select 2;
create index on test_mode (a);
analyze test_mode;
prepare test_mode_pp (int) as select count(*) from test_mode where a = $1;
-- up to 5 executions, custom plan is used
explain (costs off) execute test_mode_pp(2);
QUERY PLAN
----------------------------------------------------------
Aggregate
-> Gather Motion 3:1 (slice1; segments: 3)
-> Index Scan using test_mode_a_idx on test_mode
Index Cond: (a = 2)
Optimizer: Pivotal Optimizer (GPORCA) version 3.93.0
(5 rows)
-- force generic plan
set plan_cache_mode to force_generic_plan;
explain (costs off) execute test_mode_pp(2);
QUERY PLAN
------------------------------------------------
Finalize Aggregate
-> Gather Motion 3:1 (slice1; segments: 3)
-> Partial Aggregate
-> Seq Scan on test_mode
Filter: (a = $1)
Optimizer: Postgres query optimizer
(6 rows)
-- get to generic plan by 5 executions
set plan_cache_mode to auto;
execute test_mode_pp(1); -- 1x
count
-------
15000
(1 row)
execute test_mode_pp(1); -- 2x
count
-------
15000
(1 row)
execute test_mode_pp(1); -- 3x
count
-------
15000
(1 row)
execute test_mode_pp(1); -- 4x
count
-------
15000
(1 row)
execute test_mode_pp(1); -- 5x
count
-------
15000
(1 row)
-- we should now get a really bad plan
explain (costs off) execute test_mode_pp(2);
QUERY PLAN
-----------------------------
Aggregate
-> Gather Motion 3:1 (slice1; segments: 3)
-> Index Scan using test_mode_a_idx on test_mode
Index Cond: (a = 2)
Optimizer: Pivotal Optimizer (GPORCA) version 3.93.0
(5 rows)
-- but we can force a custom plan
set plan_cache_mode to force_custom_plan;
explain (costs off) execute test_mode_pp(2);
QUERY PLAN
----------------------------------------------------------
Aggregate
-> Gather Motion 3:1 (slice1; segments: 3)
-> Index Scan using test_mode_a_idx on test_mode
Index Cond: (a = 2)
Optimizer: Pivotal Optimizer (GPORCA) version 3.93.0
(5 rows)
drop table test_mode;
......@@ -162,3 +162,39 @@ end$$ language plpgsql;
select cachebug();
select cachebug();
-- Test plan_cache_mode
create table test_mode (a int);
-- GPDB:setting the number of rows slightly higher to get a plan with
-- Index Only Scan (similar to upstream)
insert into test_mode select 1 from generate_series(1,15000) union all select 2;
create index on test_mode (a);
analyze test_mode;
prepare test_mode_pp (int) as select count(*) from test_mode where a = $1;
-- up to 5 executions, custom plan is used
explain (costs off) execute test_mode_pp(2);
-- force generic plan
set plan_cache_mode to force_generic_plan;
explain (costs off) execute test_mode_pp(2);
-- get to generic plan by 5 executions
set plan_cache_mode to auto;
execute test_mode_pp(1); -- 1x
execute test_mode_pp(1); -- 2x
execute test_mode_pp(1); -- 3x
execute test_mode_pp(1); -- 4x
execute test_mode_pp(1); -- 5x
-- we should now get a really bad plan
explain (costs off) execute test_mode_pp(2);
-- but we can force a custom plan
set plan_cache_mode to force_custom_plan;
explain (costs off) execute test_mode_pp(2);
drop table test_mode;
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册