diff --git a/build/script/loongarch64_opengauss_list b/build/script/loongarch64_opengauss_list index f271fa14f1087c19da5a43f08a2249b0bd6cc548..7c5f382b285cdc07a92fd031b6c8afbfffc74c50 100644 --- a/build/script/loongarch64_opengauss_list +++ b/build/script/loongarch64_opengauss_list @@ -103,6 +103,8 @@ ./share/postgresql/extension/dblink--1.0.sql ./share/postgresql/extension/dblink--unpackaged--1.0.sql ./share/postgresql/extension/dblink.control +./share/postgresql/extension/gms_stats--1.0.sql +./share/postgresql/extension/gms_stats--1.0.control ./share/postgresql/timezone/GB-Eire ./share/postgresql/timezone/Turkey ./share/postgresql/timezone/Kwajalein @@ -782,6 +784,7 @@ ./lib/postgresql/dblink.so ./lib/postgresql/pgoutput.so ./lib/postgresql/assessment.so +./lib/postgresql/gms_stats.so ./lib/libpljava.so ./lib/libpq.a ./lib/libpq.so diff --git a/contrib/gms_stats/expected/gms_stats.out b/contrib/gms_stats/expected/gms_stats.out index 0453fdeb38290a57d95ee9b5c68aeccf44dedd0f..6f81e0101c481210c7501423b714d9b64f06fcfb 100644 --- a/contrib/gms_stats/expected/gms_stats.out +++ b/contrib/gms_stats/expected/gms_stats.out @@ -15,9 +15,6 @@ begin gms_stats.gather_schema_stats('gms_stats_test'); end; / -NOTICE: PL/SQL procedure successfully completed. -CONTEXT: SQL statement "CALL gms_stats.gather_schema_stats('gms_stats_test')" -PL/pgSQL function inline_code_block line 2 at PERFORM select schemaname, tablename, attname, avg_width, most_common_vals, most_common_freqs from pg_stats where schemaname='gms_stats_test' order by tablename, attname; schemaname | tablename | attname | avg_width | most_common_vals | most_common_freqs ----------------+-----------------+---------+-----------+------------------+------------------- @@ -31,7 +28,6 @@ select schemaname, tablename, attname, avg_width, most_common_vals, most_common_ create table normal_table2(a int, b char(10)); insert into normal_table2 select generate_series(1,700), 'abc'; call gms_stats.gather_schema_stats('gms_stats_test'); -NOTICE: PL/SQL procedure successfully completed. gather_schema_stats --------------------- @@ -55,3 +51,707 @@ DETAIL: drop cascades to table normal_table drop cascades to table partition_table drop cascades to materialized view mv_tb drop cascades to table normal_table2 +create schema sc_stats; +set current_schema = sc_stats; +create table t_stats (id int primary key, c2 text); +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t_stats_pkey" for table "t_stats" +insert into t_stats values (generate_series(1, 100), 'aabbcc'); +insert into t_stats values (generate_series(101, 200), '123dfg'); +insert into t_stats values (generate_series(201, 300), '人面桃花相映红'); +insert into t_stats values (generate_series(301, 400), 'fortunate'); +insert into t_stats values (generate_series(401, 500), 'open@gauss'); +insert into t_stats values (generate_series(501, 600), '127.0.0.1'); +insert into t_stats values (generate_series(601, 700), '!@#$!%#!'); +insert into t_stats values (generate_series(701, 800), '[1,2,3,4]'); +insert into t_stats values (generate_series(801, 900), '{"name":"张三","age":18}'); +insert into t_stats values (generate_series(901, 1000), ''); +create table t_stats2 (id int primary key, c2 text); +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t_stats2_pkey" for table "t_stats2" +-- 创建用户表 +call gms_stats.create_stat_table('sc_stats', 't_temp_stats'); + create_stat_table +------------------- + +(1 row) + +\d sc_stats.t_temp_stats + Table "sc_stats.t_temp_stats" + Column | Type | Modifiers +---------------+------------------+----------- + namespaceid | oid | + starelid | oid | + partid | oid | + statype | "char" | + starelkind | "char" | + staattnum | smallint | + stainherit | boolean | + stanullfrac | real | + stawidth | integer | + stadistinct | real | + reltuples | double precision | + relpages | double precision | + stakind1 | smallint | + stakind2 | smallint | + stakind3 | smallint | + stakind4 | smallint | + stakind5 | smallint | + staop1 | oid | + staop2 | oid | + staop3 | oid | + staop4 | oid | + staop5 | oid | + stanumbers1 | real[] | + stanumbers2 | real[] | + stanumbers3 | real[] | + stanumbers4 | real[] | + stanumbers5 | real[] | + stavalues1 | anyarray | + stavalues2 | anyarray | + stavalues3 | anyarray | + stavalues4 | anyarray | + stavalues5 | anyarray | + stadndistinct | real | + staextinfo | text | +Indexes: + "t_temp_stats_rel_part_type_idx" btree (starelid, partid, statype) TABLESPACE pg_default + "t_temp_stats_rel_type_idx" btree (starelid, statype) TABLESPACE pg_default + "t_temp_stats_rel_type_kind_attnum_idx" btree (starelid, statype, starelkind, staattnum) TABLESPACE pg_default + +-- 收集统计信息 +call gms_stats.gather_database_stats(); + gather_database_stats +----------------------- + +(1 row) + +call gms_stats.lock_table_stats('sc_stats', 't_stats'); + lock_table_stats +------------------ + +(1 row) + +select namespace, t2.relname as tablename, partname, stalocktype +from pg_statistic_lock t1 +left join pg_class t2 on t1.relid = t2.oid +where namespace = 'sc_stats' and tablename = 't_stats' +order by tablename, stalocktype; + namespace | tablename | partname | stalocktype +-----------+-----------+----------+------------- + sc_stats | t_stats | | t +(1 row) + +call gms_stats.gather_table_stats('sc_stats', 't_stats'); +ERROR: Relation "sc_stats"."t_stats" has been locked +CONTEXT: referenced column: gs_gather_table_stats +SQL statement "SELECT gms_stats.gs_gather_table_stats(ownname, tabname, partname, stattab, statid, statown, force)" +PL/pgSQL function gms_stats.gather_table_stats(character varying,character varying,character varying,numeric,boolean,character varying,numeric,character varying,boolean,character varying,character varying,character varying,boolean,character varying,boolean,text,character varying) line 3 at PERFORM +call gms_stats.gather_table_stats('sc_stats', 't_stats', force:=true); + gather_table_stats +-------------------- + +(1 row) + +select schemaname, tablename, attname, avg_width, most_common_vals, most_common_freqs from pg_stats where schemaname='sc_stats' and tablename = 't_stats' order by tablename, attname; + schemaname | tablename | attname | avg_width | most_common_vals | most_common_freqs +------------+-----------+---------+-----------+---------------------------------------------------------------------------------------------------------------------+------------------------------ + sc_stats | t_stats | c2 | 12 | {!@#$!%#!,"[1,2,3,4]",123dfg,127.0.0.1,aabbcc,fortunate,"{\"name\":\"张三\",\"age\":18}",open@gauss,人面桃花相映红} | {.1,.1,.1,.1,.1,.1,.1,.1,.1} + sc_stats | t_stats | id | 4 | | +(2 rows) + +select t2.nspname as schemaname, t3.relname as tablename, partid, statype, starelkind, staattnum, stalocktype +from pg_statistic_history t1 +left join pg_namespace t2 on t1.namespaceid = t2.oid +left join pg_class t3 on t1.starelid = t3.oid +where schemaname = 'sc_stats' and tablename = 't_stats' +order by tablename, staattnum; + schemaname | tablename | partid | statype | starelkind | staattnum | stalocktype +------------+-----------+--------+---------+------------+-----------+------------- + sc_stats | t_stats | 0 | c | c | 1 | + sc_stats | t_stats | 0 | c | c | 2 | +(2 rows) + +call gms_stats.unlock_table_stats('sc_stats', 't_stats'); + unlock_table_stats +-------------------- + +(1 row) + +select namespace, t2.relname as tablename, partname, stalocktype +from pg_statistic_lock t1 +left join pg_class t2 on t1.relid = t2.oid +order by tablename, stalocktype; + namespace | tablename | partname | stalocktype +-----------+-----------+----------+------------- +(0 rows) + +call gms_stats.gather_table_stats('sc_stats', 't_stats'); + gather_table_stats +-------------------- + +(1 row) + +call gms_stats.gather_index_stats('sc_stats', 't_stats_pkey'); + gather_index_stats +-------------------- + +(1 row) + +call gms_stats.set_table_stats('sc_stats', 't_stats', numrows:=1010); + set_table_stats +----------------- + +(1 row) + +select reltuples, relpages +from pg_class t1 +left join pg_namespace t2 on t1.relnamespace = t2.oid +where t2.nspname = 'sc_stats' and relname = 't_stats'; + reltuples | relpages +-----------+---------- + 1010 | 6 +(1 row) + +call gms_stats.set_index_stats('sc_stats', 't_stats_pkey', numdist:=1010); + set_index_stats +----------------- + +(1 row) + +call gms_stats.set_column_stats('sc_stats', 't_stats', 'c2', distcnt:=1010); + set_column_stats +------------------ + +(1 row) + +call gms_stats.gather_table_stats('sc_stats', 't_stats'); + gather_table_stats +-------------------- + +(1 row) + +select schemaname, tablename, attname, avg_width, most_common_vals, most_common_freqs from pg_stats where schemaname='sc_stats' and tablename = 't_stats' order by tablename, attname; + schemaname | tablename | attname | avg_width | most_common_vals | most_common_freqs +------------+-----------+---------+-----------+---------------------------------------------------------------------------------------------------------------------+------------------------------ + sc_stats | t_stats | c2 | 12 | {!@#$!%#!,"[1,2,3,4]",123dfg,127.0.0.1,aabbcc,fortunate,"{\"name\":\"张三\",\"age\":18}",open@gauss,人面桃花相映红} | {.1,.1,.1,.1,.1,.1,.1,.1,.1} + sc_stats | t_stats | id | 4 | | +(2 rows) + +select t2.nspname as schemaname, t3.relname as tablename, partid, statype, starelkind, staattnum, stalocktype, t1.reltuples as reltuples, t1.relpages as relpages +from pg_statistic_history t1 +left join pg_namespace t2 on t1.namespaceid = t2.oid +left join pg_class t3 on t1.starelid = t3.oid +where schemaname = 'sc_stats' and tablename = 't_stats' +order by tablename, staattnum; + schemaname | tablename | partid | statype | starelkind | staattnum | stalocktype | reltuples | relpages +------------+-----------+--------+---------+------------+-----------+-------------+-----------+---------- + sc_stats | t_stats | 0 | c | c | 1 | | 0 | 0 + sc_stats | t_stats | 0 | c | c | 2 | | 0 | 0 +(2 rows) + +call gms_stats.gather_table_stats('sc_stats', 't_stats2', stattab:='t_temp_stats'); +NOTICE: Data collection succeeded. +CONTEXT: referenced column: gs_gather_table_stats +SQL statement "SELECT gms_stats.gs_gather_table_stats(ownname, tabname, partname, stattab, statid, statown, force)" +PL/pgSQL function gms_stats.gather_table_stats(character varying,character varying,character varying,numeric,boolean,character varying,numeric,character varying,boolean,character varying,character varying,character varying,boolean,character varying,boolean,text,character varying) line 3 at PERFORM + gather_table_stats +-------------------- + +(1 row) + +select t2.nspname as schemaname, t3.relname as tablename, statype, starelkind, staattnum, stainherit, stanullfrac, stawidth, stadistinct, t1.reltuples as reltuples, t1.relpages as relpages +from sc_stats.t_temp_stats t1 +left join pg_namespace t2 on t1.namespaceid = t2.oid +left join pg_class t3 on t1.starelid = t3.oid +where schemaname = 'sc_stats' and tablename = 't_stats2' +order by tablename, staattnum; + schemaname | tablename | statype | starelkind | staattnum | stainherit | stanullfrac | stawidth | stadistinct | reltuples | relpages +------------+-----------+---------+------------+-----------+------------+-------------+----------+-------------+-----------+---------- + sc_stats | t_stats2 | t | | | | | | | 0 | 0 +(1 row) + +-- 导出数据 +call gms_stats.lock_schema_stats('sc_stats'); + lock_schema_stats +------------------- + +(1 row) + +select * from pg_statistic_lock where namespace = 'sc_stats' and stalocktype = 's'; + relid | stalocktype | namespace | partname +-------+-------------+-----------+---------- + 0 | s | sc_stats | +(1 row) + +call gms_stats.export_column_stats('sc_stats', 't_stats2', 'c2', stattab:='t_temp_stats'); + export_column_stats +--------------------- + +(1 row) + +select t2.nspname as schemaname, t3.relname as tablename, statype, starelkind, staattnum, stainherit, stanullfrac, stawidth, stadistinct, t1.reltuples as reltuples, t1.relpages as relpages +from sc_stats.t_temp_stats t1 +left join pg_namespace t2 on t1.namespaceid = t2.oid +left join pg_class t3 on t1.starelid = t3.oid +where schemaname = 'sc_stats' and tablename = 't_stats2' +order by tablename, staattnum; + schemaname | tablename | statype | starelkind | staattnum | stainherit | stanullfrac | stawidth | stadistinct | reltuples | relpages +------------+-----------+---------+------------+-----------+------------+-------------+----------+-------------+-----------+---------- + sc_stats | t_stats2 | t | | | | | | | 0 | 0 +(1 row) + +call gms_stats.export_index_stats('sc_stats', 't_stats_pkey', stattab:='t_temp_stats'); + export_index_stats +-------------------- + +(1 row) + +select t2.nspname as schemaname, t3.relname as tablename, statype, starelkind, staattnum, stainherit, stanullfrac, stawidth, stadistinct, t1.reltuples as reltuples, t1.relpages as relpages +from sc_stats.t_temp_stats t1 +left join pg_namespace t2 on t1.namespaceid = t2.oid +left join pg_class t3 on t1.starelid = t3.oid +where schemaname = 'sc_stats' and tablename = 't_stats' +order by tablename, staattnum; + schemaname | tablename | statype | starelkind | staattnum | stainherit | stanullfrac | stawidth | stadistinct | reltuples | relpages +------------+-----------+---------+------------+-----------+------------+-------------+----------+-------------+-----------+---------- +(0 rows) + +call gms_stats.export_table_stats('sc_stats', 't_stats', stattab:='t_temp_stats'); + export_table_stats +-------------------- + +(1 row) + +select t2.nspname as schemaname, t3.relname as tablename, statype, starelkind, staattnum, stainherit, stanullfrac, stawidth, stadistinct, t1.reltuples as reltuples, t1.relpages as relpages +from sc_stats.t_temp_stats t1 +left join pg_namespace t2 on t1.namespaceid = t2.oid +left join pg_class t3 on t1.starelid = t3.oid +where schemaname = 'sc_stats' and tablename = 't_stats' +order by tablename, staattnum; + schemaname | tablename | statype | starelkind | staattnum | stainherit | stanullfrac | stawidth | stadistinct | reltuples | relpages +------------+-----------+---------+------------+-----------+------------+-------------+----------+-------------+-----------+---------- + sc_stats | t_stats | c | c | 1 | f | 0 | 4 | -1 | | + sc_stats | t_stats | c | c | 2 | f | .1 | 12 | 9 | | + sc_stats | t_stats | t | | | | | | | 1000 | 6 +(3 rows) + +call gms_stats.unlock_schema_stats('sc_stats'); + unlock_schema_stats +--------------------- + +(1 row) + +select * from pg_statistic_lock where namespace = 'sc_stats' and stalocktype = 's'; + relid | stalocktype | namespace | partname +-------+-------------+-----------+---------- +(0 rows) + +call gms_stats.export_column_stats('sc_stats', 't_stats2', 'c2', stattab:='t_temp_stats'); + export_column_stats +--------------------- + +(1 row) + +select t2.nspname as schemaname, t3.relname as tablename, statype, starelkind, staattnum, stainherit, stanullfrac, stawidth, stadistinct, t1.reltuples as reltuples, t1.relpages as relpages +from sc_stats.t_temp_stats t1 +left join pg_namespace t2 on t1.namespaceid = t2.oid +left join pg_class t3 on t1.starelid = t3.oid +where schemaname = 'sc_stats' and tablename = 't_stats2' +order by tablename, staattnum; + schemaname | tablename | statype | starelkind | staattnum | stainherit | stanullfrac | stawidth | stadistinct | reltuples | relpages +------------+-----------+---------+------------+-----------+------------+-------------+----------+-------------+-----------+---------- + sc_stats | t_stats2 | t | | | | | | | 0 | 0 +(1 row) + +call gms_stats.export_index_stats('sc_stats', 't_stats_pkey', stattab:='t_temp_stats'); + export_index_stats +-------------------- + +(1 row) + +select t2.nspname as schemaname, t3.relname as tablename, statype, starelkind, staattnum, stainherit, stanullfrac, stawidth, stadistinct, t1.reltuples as reltuples, t1.relpages as relpages +from sc_stats.t_temp_stats t1 +left join pg_namespace t2 on t1.namespaceid = t2.oid +left join pg_class t3 on t1.starelid = t3.oid +where schemaname = 'sc_stats' and tablename = 't_stats' +order by tablename, staattnum; + schemaname | tablename | statype | starelkind | staattnum | stainherit | stanullfrac | stawidth | stadistinct | reltuples | relpages +------------+-----------+---------+------------+-----------+------------+-------------+----------+-------------+-----------+---------- + sc_stats | t_stats | c | c | 1 | f | 0 | 4 | -1 | | + sc_stats | t_stats | c | c | 2 | f | .1 | 12 | 9 | | + sc_stats | t_stats | t | | | | | | | 1000 | 6 +(3 rows) + +call gms_stats.export_table_stats('sc_stats', 't_stats', stattab:='t_temp_stats'); + export_table_stats +-------------------- + +(1 row) + +select t2.nspname as schemaname, t3.relname as tablename, statype, starelkind, staattnum, stainherit, stanullfrac, stawidth, stadistinct, t1.reltuples as reltuples, t1.relpages as relpages +from sc_stats.t_temp_stats t1 +left join pg_namespace t2 on t1.namespaceid = t2.oid +left join pg_class t3 on t1.starelid = t3.oid +where schemaname = 'sc_stats' and tablename = 't_stats' +order by tablename, staattnum; + schemaname | tablename | statype | starelkind | staattnum | stainherit | stanullfrac | stawidth | stadistinct | reltuples | relpages +------------+-----------+---------+------------+-----------+------------+-------------+----------+-------------+-----------+---------- + sc_stats | t_stats | c | c | 1 | f | 0 | 4 | -1 | | + sc_stats | t_stats | c | c | 2 | f | .1 | 12 | 9 | | + sc_stats | t_stats | t | | | | | | | 1000 | 6 +(3 rows) + +call gms_stats.export_schema_stats('sc_stats', stattab:='t_temp_stats'); + export_schema_stats +--------------------- + +(1 row) + +select t2.nspname as schemaname, t3.relname as tablename, statype, starelkind, staattnum, stainherit, stanullfrac, stawidth, stadistinct, t1.reltuples as reltuples, t1.relpages as relpages +from sc_stats.t_temp_stats t1 +left join pg_namespace t2 on t1.namespaceid = t2.oid +left join pg_class t3 on t1.starelid = t3.oid +where schemaname = 'sc_stats' +order by tablename, staattnum; + schemaname | tablename | statype | starelkind | staattnum | stainherit | stanullfrac | stawidth | stadistinct | reltuples | relpages +------------+--------------+---------+------------+-----------+------------+-------------+----------+-------------+-----------+---------- + sc_stats | t_stats | c | c | 1 | f | 0 | 4 | -1 | | + sc_stats | t_stats | c | c | 2 | f | .1 | 12 | 9 | | + sc_stats | t_stats | t | | | | | | | 1000 | 6 + sc_stats | t_stats2 | t | | | | | | | 0 | 0 + sc_stats | t_temp_stats | t | | | | | | | 0 | 0 +(5 rows) + +call gms_stats.gather_table_stats('sc_stats', 't_stats', stattab:='t_temp_stats'); +NOTICE: Data collection succeeded. +CONTEXT: referenced column: gs_gather_table_stats +SQL statement "SELECT gms_stats.gs_gather_table_stats(ownname, tabname, partname, stattab, statid, statown, force)" +PL/pgSQL function gms_stats.gather_table_stats(character varying,character varying,character varying,numeric,boolean,character varying,numeric,character varying,boolean,character varying,character varying,character varying,boolean,character varying,boolean,text,character varying) line 3 at PERFORM + gather_table_stats +-------------------- + +(1 row) + +call gms_stats.gather_table_stats('sc_stats', 't_stats2', stattab:='t_temp_stats'); +NOTICE: Data collection succeeded. +CONTEXT: referenced column: gs_gather_table_stats +SQL statement "SELECT gms_stats.gs_gather_table_stats(ownname, tabname, partname, stattab, statid, statown, force)" +PL/pgSQL function gms_stats.gather_table_stats(character varying,character varying,character varying,numeric,boolean,character varying,numeric,character varying,boolean,character varying,character varying,character varying,boolean,character varying,boolean,text,character varying) line 3 at PERFORM + gather_table_stats +-------------------- + +(1 row) + +call gms_stats.import_column_stats('sc_stats', 't_stats2', 'c2', stattab:='t_temp_stats'); + import_column_stats +--------------------- + +(1 row) + +call gms_stats.import_index_stats('sc_stats', 't_stats_pkey', stattab:='t_temp_stats'); + import_index_stats +-------------------- + +(1 row) + +call gms_stats.import_table_stats('sc_stats', 't_stats', stattab:='t_temp_stats'); + import_table_stats +-------------------- + +(1 row) + +call gms_stats.import_schema_stats('sc_stats', stattab:='t_temp_stats'); + import_schema_stats +--------------------- + +(1 row) + +select gms_stats.get_stats_history_retention; + get_stats_history_retention +----------------------------- + 31 +(1 row) + +-- 清除恢复 +call gms_stats.purge_stats(to_timestamp('2024-08-26 13:26:15', 'yyyy-MM-dd hh24:mi:ss')); + purge_stats +------------- + +(1 row) + +call gms_stats.restore_table_stats('sc_stats', 't_stats', to_timestamp('2024-08-22 13:26:15', 'yyyy-MM-dd hh24:mi:ss')); + restore_table_stats +--------------------- + +(1 row) + +call gms_stats.restore_schema_stats('sc_stats', to_timestamp('2024-08-22 13:26:15', 'yyyy-MM-dd hh24:mi:ss')); + restore_schema_stats +---------------------- + +(1 row) + +-- 删除统计信息 +call gms_stats.delete_column_stats('sc_stats', 't_stats', 'c2'); + delete_column_stats +--------------------- + +(1 row) + +call gms_stats.delete_index_stats('sc_stats', 't_stats_pkey'); + delete_index_stats +-------------------- + +(1 row) + +call gms_stats.delete_table_stats('sc_stats', 't_stats'); + delete_table_stats +-------------------- + +(1 row) + +call gms_stats.delete_schema_stats('sc_stats'); + delete_schema_stats +--------------------- + +(1 row) + +BEGIN +gms_stats.create_stat_table('sc_stats', 'STAT_TAB', 'pg_default'); +exception +when others then +NULL; +end; +/ +BEGIN +gms_stats.drop_stat_table('sc_stats', 'STAT_TAB'); +exception +when others then +NULL; +end; +/ +create table t1(c1 int, c2 date); +BEGIN +gms_stats.delete_column_stats('sc_stats', 't1', 'c1'); +exception +when others then +NULL; +end; +/ +DECLARE +ownname VARCHAR2(200); +indname VARCHAR2(200); +BEGIN +gms_stats.delete_index_stats( +ownname, +indname); +exception +when others then +NULL; +end; +/ +BEGIN +gms_stats.DELETE_SCHEMA_STATS('sc_stats'); +END; +/ +BEGIN +gms_stats.delete_table_stats('sc_stats', 't1'); +exception +when others then +NULL; +end; +/ +BEGIN +gms_stats.drop_stat_table('sc_stats', 't1'); +exception +when others then +NULL; +end; +/ +CREATE TABLE example_data ( +id NUMBER, +value NUMBER +); +BEGIN +gms_stats.CREATE_STAT_TABLE('sc_stats','stat_t'); +gms_stats.EXPORT_COLUMN_STATS('sc_stats','example_data','id', NULL,'stat_t'); +END; +/ +DROP TABLE stat_t; +DROP TABLE example_data; +CREATE TABLE example_data ( +id NUMBER, +value NUMBER +); +CREATE index idx on example_data(id); +BEGIN +gms_stats.CREATE_STAT_TABLE('sc_stats','stat_t'); +gms_stats.EXPORT_INDEX_STATS('sc_stats', 'idx', NULL, 'stat_t'); +END; +/ +DROP INDEX idx; +DROP TABLE stat_t; +DROP TABLE example_data; +BEGIN +gms_stats.CREATE_STAT_TABLE('sc_stats','stat_t'); +gms_stats.EXPORT_SCHEMA_STATS(stattab => 'stat_t', ownname => 'sc_stats'); +END; +/ +DROP TABLE stat_t; +BEGIN +gms_stats.export_table_stats('sc_stats', 't_stats', NULL, 'STAT_TAB', NULL, TRUE); +exception +when others then +NULL; +end; +/ +SELECT gms_stats.GET_STATS_HISTORY_RETENTION; + get_stats_history_retention +----------------------------- + 31 +(1 row) + +CREATE TABLE example_data ( +id NUMBER, +value NUMBER +); +BEGIN +gms_stats.CREATE_STAT_TABLE('sc_stats','stat_t'); +gms_stats.IMPORT_COLUMN_STATS(ownname => 'sc_stats', stattab => 'stat_t', tabname => 'example_data', colname => 'id'); +END; +/ +DROP TABLE stat_t; +DROP TABLE example_data; +CREATE TABLE example_data ( +id NUMBER, +value NUMBER +); +CREATE index idx on example_data(id); +BEGIN +gms_stats.CREATE_STAT_TABLE('sc_stats','stat_t'); +gms_stats.EXPORT_INDEX_STATS('sc_stats', 'idx', NULL, 'stat_t'); +gms_stats.IMPORT_INDEX_STATS('sc_stats', 'idx', NULL, 'stat_t'); +gms_stats.DROP_STAT_TABLE('sc_stats', 'stat_t'); +END; +/ +DROP INDEX idx; +DROP TABLE example_data; +BEGIN +gms_stats.CREATE_STAT_TABLE('sc_stats','stat_t'); +gms_stats.EXPORT_SCHEMA_STATS(stattab => 'stat_t', ownname => 'sc_stats'); +gms_stats.IMPORT_SCHEMA_STATS(stattab => 'stat_t', ownname => 'sc_stats'); +gms_stats.DROP_STAT_TABLE('sc_stats', 'stat_t'); +END; +/ +BEGIN +gms_stats.import_table_stats('public', 'servers', stattab => 'STAT_TAB'); +exception +when others then +NULL; +end; +/ +CREATE TABLE sales ( +sales_id NUMBER, +sales_date DATE, +amount NUMBER +) +PARTITION BY RANGE (sales_date) ( +PARTITION sales_q1 VALUES LESS THAN (TO_DATE('2022-04-01', 'YYYY-MM-DD')), +PARTITION sales_q2 VALUES LESS THAN (TO_DATE('2022-07-01', 'YYYY-MM-DD')), +PARTITION sales_q3 VALUES LESS THAN (TO_DATE('2022-10-01', 'YYYY-MM-DD')), +PARTITION sales_q4 VALUES LESS THAN (MAXVALUE) +); +BEGIN +gms_stats.LOCK_PARTITION_STATS('sc_stats','sales','sales_q1'); +gms_stats.UNLOCK_PARTITION_STATS('sc_stats','sales','sales_q1'); +END; +/ +DROP TABLE sales; +BEGIN +gms_stats.LOCK_SCHEMA_STATS('sc_stats'); +gms_stats.UNLOCK_SCHEMA_STATS('sc_stats'); +END; +/ +CREATE TABLE sales ( +sales_id NUMBER, +sales_date DATE, +amount NUMBER +) +PARTITION BY RANGE (sales_date) ( +PARTITION sales_q1 VALUES LESS THAN (TO_DATE('2022-04-01', 'YYYY-MM-DD')), +PARTITION sales_q2 VALUES LESS THAN (TO_DATE('2022-07-01', 'YYYY-MM-DD')), +PARTITION sales_q3 VALUES LESS THAN (TO_DATE('2022-10-01', 'YYYY-MM-DD')), +PARTITION sales_q4 VALUES LESS THAN (MAXVALUE) +); +BEGIN +gms_stats.LOCK_TABLE_STATS('sc_stats','sales'); +gms_stats.UNLOCK_TABLE_STATS('sc_stats','sales'); +END; +/ +DROP TABLE sales; +BEGIN +gms_stats.PURGE_STATS (null); +END; +/ +CREATE TABLE example_data ( +id NUMBER, +value NUMBER +); +CREATE index idx on example_data(id); +BEGIN +gms_stats.SET_INDEX_STATS('sc_stats', 'idx'); +END; +/ +DROP INDEX IDX; +DROP TABLE example_data; +BEGIN +gms_stats.set_table_stats('sc_stats', 'EMPLOYEES', numrows=> 1000, numblks=>100); +exception +when others then +NULL; +end; +/ +CREATE TABLE sales ( +sales_id NUMBER, +sales_date DATE, +amount NUMBER +) +PARTITION BY RANGE (sales_date) ( +PARTITION sales_q1 VALUES LESS THAN (TO_DATE('2022-04-01', 'YYYY-MM-DD')), +PARTITION sales_q2 VALUES LESS THAN (TO_DATE('2022-07-01', 'YYYY-MM-DD')), +PARTITION sales_q3 VALUES LESS THAN (TO_DATE('2022-10-01', 'YYYY-MM-DD')), +PARTITION sales_q4 VALUES LESS THAN (MAXVALUE) +); +BEGIN +gms_stats.LOCK_PARTITION_STATS('sc_stats','sales','sales_q1'); +gms_stats.UNLOCK_PARTITION_STATS('sc_stats','sales','sales_q1'); +END; +/ +DROP TABLE sales; +BEGIN +gms_stats.LOCK_SCHEMA_STATS('sc_stats'); +gms_stats.UNLOCK_SCHEMA_STATS('sc_stats'); +END; +/ +CREATE TABLE sales ( +sales_id NUMBER, +sales_date DATE, +amount NUMBER +) +PARTITION BY RANGE (sales_date) ( +PARTITION sales_q1 VALUES LESS THAN (TO_DATE('2022-04-01', 'YYYY-MM-DD')), +PARTITION sales_q2 VALUES LESS THAN (TO_DATE('2022-07-01', 'YYYY-MM-DD')), +PARTITION sales_q3 VALUES LESS THAN (TO_DATE('2022-10-01', 'YYYY-MM-DD')), +PARTITION sales_q4 VALUES LESS THAN (MAXVALUE) +); +BEGIN +gms_stats.LOCK_TABLE_STATS('sc_stats','sales'); +gms_stats.UNLOCK_TABLE_STATS('sc_stats','sales'); +END; +/ +DROP TABLE sales; +reset current_schema; +drop schema sc_stats cascade; +NOTICE: drop cascades to 3 other objects +DETAIL: drop cascades to table sc_stats.t_stats +drop cascades to table sc_stats.t_stats2 +drop cascades to table sc_stats.t_temp_stats diff --git a/contrib/gms_stats/gms_stats--1.0.sql b/contrib/gms_stats/gms_stats--1.0.sql index ad9e008b78333a2ca3a50295c1d9e301ce3477b7..2c2f64456aac661f228cf5cc0d93caac072a2406 100644 --- a/contrib/gms_stats/gms_stats--1.0.sql +++ b/contrib/gms_stats/gms_stats--1.0.sql @@ -36,7 +36,569 @@ CREATE OR REPLACE PROCEDURE gms_stats.gather_schema_stats( obj_filter_list objecttab default null) IS BEGIN perform gms_stats.gs_analyze_schema_tables(ownname); - raise notice 'PL/SQL procedure successfully completed.'; END; --- gms_stats package end +CREATE FUNCTION gms_stats.gs_create_stat_table(ownname varchar2, stattab varchar2, tblspace varchar2, global_temporary boolean) + RETURNS void +AS 'MODULE_PATHNAME','gs_create_stat_table' +LANGUAGE C VOLATILE NOT FENCED; + +CREATE OR REPLACE PROCEDURE gms_stats.create_stat_table( + ownname varchar2, + stattab varchar2, + tblspace varchar2 default null, + global_temporary boolean default false) IS +BEGIN + perform gms_stats.gs_create_stat_table(ownname, stattab, tblspace, global_temporary); +END; + +CREATE FUNCTION gms_stats.gs_drop_stat_table(ownname varchar2, stattab varchar2) + RETURNS void +AS 'MODULE_PATHNAME','gs_drop_stat_table' +LANGUAGE C VOLATILE NOT FENCED; + +CREATE OR REPLACE PROCEDURE gms_stats.drop_stat_table( + ownname varchar2, + stattab varchar2) IS +BEGIN + perform gms_stats.gs_drop_stat_table(ownname, stattab); +END; + +CREATE FUNCTION gms_stats.gs_lock_schema_stats(ownname varchar2) + RETURNS void +AS 'MODULE_PATHNAME','gs_lock_schema_stats' +LANGUAGE C VOLATILE NOT FENCED; + +CREATE OR REPLACE PROCEDURE gms_stats.lock_schema_stats( + ownname varchar2) IS +BEGIN + perform gms_stats.gs_lock_schema_stats(ownname); +END; + +CREATE FUNCTION gms_stats.gs_lock_table_stats(ownname varchar2, tabname varchar2) + RETURNS void +AS 'MODULE_PATHNAME','gs_lock_table_stats' +LANGUAGE C VOLATILE NOT FENCED; + +CREATE OR REPLACE PROCEDURE gms_stats.lock_table_stats( + ownname varchar2, + tabname varchar2) IS +BEGIN + perform gms_stats.gs_lock_table_stats(ownname, tabname); +END; + +CREATE FUNCTION gms_stats.gs_lock_partition_stats(ownname varchar2, tabname varchar2, partname varchar2) + RETURNS void +AS 'MODULE_PATHNAME','gs_lock_partition_stats' +LANGUAGE C VOLATILE NOT FENCED; + +CREATE OR REPLACE PROCEDURE gms_stats.lock_partition_stats( + ownname varchar2, + tabname varchar2, + partname varchar2) IS +BEGIN + perform gms_stats.gs_lock_partition_stats(ownname, tabname, partname); +END; + +CREATE FUNCTION gms_stats.gs_unlock_schema_stats(ownname varchar2) + RETURNS void +AS 'MODULE_PATHNAME','gs_unlock_schema_stats' +LANGUAGE C VOLATILE NOT FENCED; + +CREATE OR REPLACE PROCEDURE gms_stats.unlock_schema_stats( + ownname varchar2) IS +BEGIN + perform gms_stats.gs_unlock_schema_stats(ownname); +END; + +CREATE FUNCTION gms_stats.gs_unlock_table_stats(ownname varchar2, tabname varchar2) + RETURNS void +AS 'MODULE_PATHNAME','gs_unlock_table_stats' +LANGUAGE C VOLATILE NOT FENCED; + +CREATE OR REPLACE PROCEDURE gms_stats.unlock_table_stats( + ownname varchar2, + tabname varchar2) IS +BEGIN + perform gms_stats.gs_unlock_table_stats(ownname, tabname); +END; + +CREATE FUNCTION gms_stats.gs_unlock_partition_stats(ownname varchar2, tabname varchar2, partname varchar2) + RETURNS void +AS 'MODULE_PATHNAME','gs_unlock_partition_stats' +LANGUAGE C VOLATILE NOT FENCED; + +CREATE OR REPLACE PROCEDURE gms_stats.unlock_partition_stats( + ownname varchar2, + tabname varchar2, + partname varchar2) IS +BEGIN + perform gms_stats.gs_unlock_partition_stats(ownname, tabname, partname); +END; + +CREATE FUNCTION gms_stats.gs_export_column_stats(ownname varchar2, tabname varchar2, colname varchar2, stattab varchar2, statown varchar2) + RETURNS void +AS 'MODULE_PATHNAME','gs_export_column_stats' +LANGUAGE C VOLATILE NOT FENCED; + +CREATE OR REPLACE PROCEDURE gms_stats.export_column_stats( + ownname varchar2, + tabname varchar2, + colname varchar2, + partname varchar2 default null, + stattab varchar2, + statid varchar2 default null, + statown varchar2 default null) IS +BEGIN + perform gms_stats.gs_export_column_stats(ownname, tabname, colname, stattab, statown); +END; + +CREATE FUNCTION gms_stats.gs_export_index_stats(ownname varchar2, indname varchar2, stattab varchar2, statown varchar2) + RETURNS void +AS 'MODULE_PATHNAME','gs_export_index_stats' +LANGUAGE C VOLATILE NOT FENCED; + +CREATE OR REPLACE PROCEDURE gms_stats.export_index_stats( + ownname varchar2, + indname varchar2, + partname varchar2 default null, + stattab varchar2, + statid varchar2 default null, + statown varchar2 default null) IS +BEGIN + perform gms_stats.gs_export_index_stats(ownname, indname, stattab, statown); +END; + +CREATE FUNCTION gms_stats.gs_export_table_stats(ownname varchar2, tabname varchar2, partname varchar2, stattab varchar2, statown varchar2, cascade boolean) + RETURNS void +AS 'MODULE_PATHNAME','gs_export_table_stats' +LANGUAGE C VOLATILE NOT FENCED; + +CREATE OR REPLACE PROCEDURE gms_stats.export_table_stats( + ownname varchar2, + tabname varchar2, + partname varchar2 default null, + stattab varchar2, + statid varchar2 default null, + cascade boolean default true, + statown varchar2 default null, + stat_category varchar2 default null) IS +BEGIN + perform gms_stats.gs_export_table_stats(ownname, tabname, partname, stattab, statown, cascade); +END; + +CREATE FUNCTION gms_stats.gs_export_schema_stats(ownname varchar2, stattab varchar2, statown varchar2) + RETURNS void +AS 'MODULE_PATHNAME','gs_export_schema_stats' +LANGUAGE C VOLATILE NOT FENCED; + +CREATE OR REPLACE PROCEDURE gms_stats.export_schema_stats( + ownname varchar2, + stattab varchar2, + statid varchar2 default null, + statown varchar2 default null, + stat_category varchar2 default null) IS +BEGIN + perform gms_stats.gs_export_schema_stats(ownname, stattab, statown); +END; + +CREATE FUNCTION gms_stats.gs_import_column_stats(ownname varchar2, tabname varchar2, colname varchar2, stattab varchar2, statown varchar2, force boolean) + RETURNS void +AS 'MODULE_PATHNAME','gs_import_column_stats' +LANGUAGE C VOLATILE NOT FENCED; + +CREATE OR REPLACE PROCEDURE gms_stats.import_column_stats( + ownname varchar2, + tabname varchar2, + colname varchar2, + partname varchar2 default null, + stattab varchar2, + statid varchar2 default null, + statown varchar2 default null, + no_invalidate boolean default null, + force boolean default false) IS +BEGIN + perform gms_stats.gs_import_column_stats(ownname, tabname, colname, stattab, statown, force); +END; + +CREATE FUNCTION gms_stats.gs_import_index_stats(ownname varchar2, indname varchar2, stattab varchar2, statown varchar2, force boolean) + RETURNS void +AS 'MODULE_PATHNAME','gs_import_index_stats' +LANGUAGE C VOLATILE NOT FENCED; + +CREATE OR REPLACE PROCEDURE gms_stats.import_index_stats( + ownname varchar2, + indname varchar2, + partname varchar2 default null, + stattab varchar2, + statid varchar2 default null, + statown varchar2 default null, + no_invalidate boolean default null, + force boolean default false) IS +BEGIN + perform gms_stats.gs_import_index_stats(ownname, indname, stattab, statown, force); +END; + +CREATE FUNCTION gms_stats.gs_import_table_stats(ownname varchar2, tabname varchar2, partname varchar2, stattab varchar2, cascade boolean, statown varchar2, force boolean) + RETURNS void +AS 'MODULE_PATHNAME','gs_import_table_stats' +LANGUAGE C VOLATILE NOT FENCED; + +CREATE OR REPLACE PROCEDURE gms_stats.import_table_stats( + ownname varchar2, + tabname varchar2, + partname varchar2 default null, + stattab varchar2, + statid varchar2 default null, + cascade boolean default true, + statown varchar2 default null, + no_invalidate boolean default false, + force boolean default false, + stat_category varchar2 default null) IS +BEGIN + perform gms_stats.gs_import_table_stats(ownname, tabname, partname, stattab, cascade, statown, force); +END; + +CREATE FUNCTION gms_stats.gs_import_schema_stats(ownname varchar2, stattab varchar2, statown varchar2, force boolean) + RETURNS void +AS 'MODULE_PATHNAME','gs_import_schema_stats' +LANGUAGE C VOLATILE NOT FENCED; + +CREATE OR REPLACE PROCEDURE gms_stats.import_schema_stats( + ownname varchar2, + stattab varchar2, + statid varchar2 default null, + statown varchar2 default null, + no_invalidate boolean default null, + force boolean default false, + stat_category varchar2 default null) IS +BEGIN + perform gms_stats.gs_import_schema_stats(ownname, stattab, statown, force); +END; + +CREATE FUNCTION gms_stats.gs_delete_column_stats(ownname varchar2, tabname varchar2, colname varchar2, stattab varchar2, statid varchar2, statown varchar2, force boolean) + RETURNS void +AS 'MODULE_PATHNAME','gs_delete_column_stats' +LANGUAGE C VOLATILE NOT FENCED; + +CREATE OR REPLACE PROCEDURE gms_stats.delete_column_stats( + ownname varchar2, + tabname varchar2, + colname varchar2, + partname varchar2 default null, + stattab varchar2 default null, + statid varchar2 default null, + cascade_parts boolean default true, + statown varchar2 default null, + no_invalidate boolean default null, + force boolean default false, + col_stat_type varchar2 default 'ALL') IS +BEGIN + perform gms_stats.gs_delete_column_stats(ownname, tabname, colname, stattab, statid, statown, force); +END; + +CREATE FUNCTION gms_stats.gs_delete_index_stats(ownname varchar2, indname varchar2, stattab varchar2, statid varchar2, statown varchar2, force boolean) + RETURNS void +AS 'MODULE_PATHNAME','gs_delete_index_stats' +LANGUAGE C VOLATILE NOT FENCED; + +CREATE OR REPLACE PROCEDURE gms_stats.delete_index_stats( + ownname varchar2, + indname varchar2, + partname varchar2 default null, + stattab varchar2 default null, + statid varchar2 default null, + cascade_parts boolean default true, + statown varchar2 default null, + no_invalidate boolean default null, + stattype varchar2 default 'ALL', + force boolean default false, + stat_category varchar2 default null) IS +BEGIN + perform gms_stats.gs_delete_index_stats(ownname, indname, stattab, statid, statown, force); +END; + +CREATE FUNCTION gms_stats.gs_delete_table_stats( + ownname varchar2, + tabname varchar2, + partname varchar2, + stattab varchar2, + statid varchar2, + cascade_parts boolean, + cascade_columns boolean, + cascade_indexes boolean, + statown varchar2, + force boolean) + RETURNS void +AS 'MODULE_PATHNAME','gs_delete_table_stats' +LANGUAGE C VOLATILE NOT FENCED; + +CREATE OR REPLACE PROCEDURE gms_stats.delete_table_stats( + ownname varchar2, + tabname varchar2, + partname varchar2 default null, + stattab varchar2 default null, + statid varchar2 default null, + cascade_parts boolean default true, + cascade_columns boolean default true, + cascade_indexes boolean default true, + statown varchar2 default null, + no_invalidate boolean default null, + force boolean default false, + stat_category varchar2 default null) IS +BEGIN + perform gms_stats.gs_delete_table_stats(ownname, tabname, partname, stattab, statid, cascade_parts, cascade_columns, cascade_indexes, statown, force); +END; + +CREATE FUNCTION gms_stats.gs_delete_schema_stats(ownname varchar2, stattab varchar2, statid varchar2, statown varchar2, force boolean) + RETURNS void +AS 'MODULE_PATHNAME','gs_delete_schema_stats' +LANGUAGE C VOLATILE NOT FENCED; + +CREATE OR REPLACE PROCEDURE gms_stats.delete_schema_stats( + ownname varchar2, + stattab varchar2 default null, + statid varchar2 default null, + statown varchar2 default null, + no_invalidate boolean default null, + force boolean default false, + stat_category varchar2 default null) IS +BEGIN + perform gms_stats.gs_delete_schema_stats(ownname, stattab, statid, statown, force); +END; + +CREATE OR REPLACE PROCEDURE gms_stats.delete_schema_stats( + ownname name, + stattab varchar2 default null, + statid varchar2 default null, + statown varchar2 default null, + no_invalidate boolean default null, + force boolean default false, + stat_category varchar2 default null) IS +BEGIN + perform gms_stats.gs_delete_schema_stats(ownname::varchar2, stattab, statid, statown, force); +END; + +CREATE FUNCTION gms_stats.gs_set_column_stats(ownname varchar2, tabname varchar2, colname varchar2, stattab varchar2, statid varchar2, distcnt number, nullcnt number, statown varchar2, force boolean) + RETURNS void +AS 'MODULE_PATHNAME','gs_set_column_stats' +LANGUAGE C VOLATILE NOT FENCED; + +CREATE OR REPLACE PROCEDURE gms_stats.set_column_stats( + ownname varchar2, + tabname varchar2, + colname varchar2, + partname varchar2 default null, + stattab varchar2 default null, + statid varchar2 default null, + distcnt number default null, + density number default null, + nullcnt number default null, + srec text default null, + avgclen number default null, + flags number default null, + statown varchar2 default null, + no_invalidate boolean default null, + force boolean default false) IS +BEGIN + perform gms_stats.gs_set_column_stats(ownname, tabname, colname, stattab, statid, distcnt, nullcnt, statown, force); +END; + +CREATE OR REPLACE PROCEDURE gms_stats.set_column_stats( + ownname name, + tabname varchar2, + colname varchar2, + partname varchar2 default null, + stattab varchar2 default null, + statid varchar2 default null, + distcnt number default null, + density number default null, + nullcnt number default null, + srec text default null, + avgclen number default null, + flags number default null, + statown varchar2 default null, + no_invalidate boolean default null, + force boolean default false) IS +BEGIN + perform gms_stats.gs_set_column_stats(ownname::varchar2, tabname, colname, stattab, statid, distcnt, nullcnt, statown, force); +END; + +CREATE FUNCTION gms_stats.gs_set_index_stats(ownname varchar2, indname varchar2, stattab varchar2, statid varchar2, numdist number, statown varchar2, force boolean) + RETURNS void +AS 'MODULE_PATHNAME','gs_set_index_stats' +LANGUAGE C VOLATILE NOT FENCED; + +CREATE OR REPLACE PROCEDURE gms_stats.set_index_stats( + ownname varchar2, + indname varchar2, + partname varchar2 default null, + stattab varchar2 default null, + statid varchar2 default null, + numrows number default null, + numblks number default null, + numdist number default null, + avglblk number default null, + avgdblk number default null, + clstfct number default null, + indlevel number default null, + flags number default null, + statown varchar2 default null, + no_invalidate boolean default null, + guessq number default null, + cachedblk number default null, + cachehit number default null, + force boolean default false) IS +BEGIN + perform gms_stats.gs_set_index_stats(ownname, indname, stattab, statid, numdist, statown, force); +END; + +CREATE FUNCTION gms_stats.gs_set_table_stats(ownname varchar2, tabname varchar2, partname varchar2, stattab varchar2, statid varchar2, numrows number, numblks number, statown varchar2, force boolean) + RETURNS void +AS 'MODULE_PATHNAME','gs_set_table_stats' +LANGUAGE C VOLATILE NOT FENCED; + +CREATE OR REPLACE PROCEDURE gms_stats.set_table_stats( + ownname varchar2, + tabname varchar2, + partname varchar2 default null, + stattab varchar2 default null, + statid varchar2 default null, + numrows number default null, + numblks number default null, + avgrlen number default null, + flags number default null, + statown varchar2 default null, + no_invalidate boolean default null, + cachedblk number default null, + cachehit number default null, + force boolean default false, + im_imcu_count number default null, + im_block_count number default null, + scanrate number default null) IS +BEGIN + perform gms_stats.gs_set_table_stats(ownname, tabname, partname, stattab, statid, numrows, numblks, statown, force); +END; + +CREATE FUNCTION gms_stats.gs_restore_table_stats(ownname varchar2, tabname varchar2, as_of_timestamp timestamp with time zone, force boolean) + RETURNS void +AS 'MODULE_PATHNAME','gs_restore_table_stats' +LANGUAGE C VOLATILE NOT FENCED; + +CREATE OR REPLACE PROCEDURE gms_stats.restore_table_stats( + ownname varchar2, + tabname varchar2, + as_of_timestamp timestamp with time zone, + restore_cluster_index boolean default false, + force boolean default false, + no_invalidate boolean default null) IS +BEGIN + perform gms_stats.gs_restore_table_stats(ownname, tabname, as_of_timestamp, force); +END; + +CREATE FUNCTION gms_stats.gs_restore_schema_stats(ownname varchar2, as_of_timestamp timestamp with time zone, force boolean) + RETURNS void +AS 'MODULE_PATHNAME','gs_restore_schema_stats' +LANGUAGE C VOLATILE NOT FENCED; + +CREATE OR REPLACE PROCEDURE gms_stats.restore_schema_stats( + ownname varchar2, + as_of_timestamp timestamp with time zone, + force boolean default false, + no_invalidate boolean default null) IS +BEGIN + perform gms_stats.gs_restore_schema_stats(ownname, as_of_timestamp, force); +END; + +CREATE FUNCTION gms_stats.gs_gather_table_stats(ownname varchar2, tabname varchar2, partname varchar2, stattab varchar2, statid varchar2, statown varchar2, force boolean) + RETURNS void +AS 'MODULE_PATHNAME','gs_gather_table_stats' +LANGUAGE C VOLATILE NOT FENCED; + +CREATE OR REPLACE PROCEDURE gms_stats.gather_table_stats( + ownname varchar2, + tabname varchar2, + partname varchar2 default null, + estimate_percent number default null, + block_sample boolean default false, + method_opt varchar2 default null, + degree number default null, + granularity varchar2 default null, + cascade boolean default true, + stattab varchar2 default null, + statid varchar2 default null, + statown varchar2 default null, + no_invalidate boolean default false, + stattype varchar2 default null, + force boolean default false, + context text default null, + options varchar2 default null) IS +BEGIN + + perform gms_stats.gs_gather_table_stats(ownname, tabname, partname, stattab, statid, statown, force); +END; + +CREATE FUNCTION gms_stats.gs_gather_index_stats(ownname varchar2, indname varchar2, partname varchar2, stattab varchar2, statid varchar2, statown varchar2, force boolean) + RETURNS void +AS 'MODULE_PATHNAME','gs_gather_index_stats' +LANGUAGE C VOLATILE NOT FENCED; + +CREATE OR REPLACE PROCEDURE gms_stats.gather_index_stats( + ownname varchar2, + indname varchar2, + partname varchar2 default null, + estimate_percent number default null, + stattab varchar2 default null, + statid varchar2 default null, + statown varchar2 default null, + degree number default null, + granularity varchar2 default null, + no_invalidate boolean default null, + force boolean default false) IS +BEGIN + perform gms_stats.gs_gather_index_stats(ownname, indname, partname, stattab, statid, statown, force); +END; + +CREATE FUNCTION gms_stats.gs_gather_database_stats(stattab varchar2, statid varchar2, statown varchar2, gather_sys boolean) + RETURNS void +AS 'MODULE_PATHNAME','gs_gather_database_stats' +LANGUAGE C VOLATILE NOT FENCED; + +CREATE OR REPLACE PROCEDURE gms_stats.gather_database_stats( + estimate_percent number default null, + block_sample boolean default false, + method_opt varchar2 default null, + degree number default null, + granularity varchar2 default 'GLOBAL', + cascade boolean default true, + stattab varchar2 default null, + statid varchar2 default null, + options varchar2 default 'GATHER', + objlist objecttab default null, + statown varchar2 default null, + gather_sys boolean default true, + no_invalidate boolean default false, + obj_filter_list objecttab default null) IS +BEGIN + perform gms_stats.gs_gather_database_stats(stattab, statid, statown, gather_sys); +END; + +CREATE FUNCTION gms_stats.get_stats_history_availability() + RETURNS timestamp with time zone +AS 'MODULE_PATHNAME','get_stats_history_availability' +LANGUAGE C VOLATILE NOT FENCED; + +CREATE FUNCTION gms_stats.get_stats_history_retention_int() + RETURNS int +AS 'MODULE_PATHNAME','get_stats_history_retention' +LANGUAGE C VOLATILE NOT FENCED; + +CREATE FUNCTION gms_stats.get_stats_history_retention() + RETURNS number +AS 'SELECT gms_stats.get_stats_history_retention_int()::number;' +LANGUAGE SQL VOLATILE NOT FENCED; + +CREATE FUNCTION gms_stats.purge_stats(before_timestamp timestamp with time zone) + RETURNS void +AS 'MODULE_PATHNAME','purge_stats' +LANGUAGE C VOLATILE NOT FENCED; \ No newline at end of file diff --git a/contrib/gms_stats/gms_stats.cpp b/contrib/gms_stats/gms_stats.cpp index ba589aff8537ee2b275734e3f1b9bf01fc8d6a95..030d83a28ea72cc676cc855a55da5a349d31b6f0 100644 --- a/contrib/gms_stats/gms_stats.cpp +++ b/contrib/gms_stats/gms_stats.cpp @@ -25,22 +25,98 @@ #include "postgres.h" #include "funcapi.h" #include "fmgr.h" +#include "miscadmin.h" #include "access/skey.h" #include "access/heapam.h" +#include "access/sysattr.h" #include "catalog/indexing.h" +#include "catalog/pg_partition.h" +#include "catalog/pg_partition_fn.h" +#include "catalog/pg_statistic.h" +#include "catalog/pg_statistic_history.h" +#include "catalog/pg_statistic_lock.h" #include "commands/sqladvisor.h" #include "commands/vacuum.h" #include "executor/spi.h" #include "lib/stringinfo.h" +#include "tcop/utility.h" #include "utils/builtins.h" #include "utils/fmgroids.h" #include "utils/lsyscache.h" +#include "utils/numeric.h" #include "gms_stats.h" PG_MODULE_MAGIC; PG_FUNCTION_INFO_V1(gs_analyze_schema_tables); +PG_FUNCTION_INFO_V1(gs_create_stat_table); +PG_FUNCTION_INFO_V1(gs_drop_stat_table); +PG_FUNCTION_INFO_V1(gs_lock_schema_stats); +PG_FUNCTION_INFO_V1(gs_lock_table_stats); +PG_FUNCTION_INFO_V1(gs_lock_partition_stats); +PG_FUNCTION_INFO_V1(gs_unlock_schema_stats); +PG_FUNCTION_INFO_V1(gs_unlock_table_stats); +PG_FUNCTION_INFO_V1(gs_unlock_partition_stats); +PG_FUNCTION_INFO_V1(gs_export_column_stats); +PG_FUNCTION_INFO_V1(gs_export_index_stats); +PG_FUNCTION_INFO_V1(gs_export_table_stats); +PG_FUNCTION_INFO_V1(gs_export_schema_stats); +PG_FUNCTION_INFO_V1(gs_import_column_stats); +PG_FUNCTION_INFO_V1(gs_import_index_stats); +PG_FUNCTION_INFO_V1(gs_import_table_stats); +PG_FUNCTION_INFO_V1(gs_import_schema_stats); +PG_FUNCTION_INFO_V1(gs_delete_column_stats); +PG_FUNCTION_INFO_V1(gs_delete_index_stats); +PG_FUNCTION_INFO_V1(gs_delete_table_stats); +PG_FUNCTION_INFO_V1(gs_delete_schema_stats); +PG_FUNCTION_INFO_V1(gs_set_column_stats); +PG_FUNCTION_INFO_V1(gs_set_index_stats); +PG_FUNCTION_INFO_V1(gs_set_table_stats); +PG_FUNCTION_INFO_V1(gs_restore_table_stats); +PG_FUNCTION_INFO_V1(gs_restore_schema_stats); +PG_FUNCTION_INFO_V1(gs_gather_table_stats); +PG_FUNCTION_INFO_V1(gs_gather_index_stats); +PG_FUNCTION_INFO_V1(gs_gather_database_stats); +PG_FUNCTION_INFO_V1(get_stats_history_availability); +PG_FUNCTION_INFO_V1(get_stats_history_retention); +PG_FUNCTION_INFO_V1(purge_stats); + +#define Natts_user_table 34 +#define Anum_user_table_namespaceid 1 +#define Anum_user_table_starelid 2 +#define Anum_user_table_partid 3 +#define Anum_user_table_statype 4 +#define Anum_user_table_starelkind 5 +#define Anum_user_table_staattnum 6 +#define Anum_user_table_stainherit 7 +#define Anum_user_table_stanullfrac 8 +#define Anum_user_table_stawidth 9 +#define Anum_user_table_stadistinct 10 +#define Anum_user_table_reltuples 11 +#define Anum_user_table_relpages 12 +#define Anum_user_table_stakind1 13 +#define Anum_user_table_stakind2 14 +#define Anum_user_table_stakind3 15 +#define Anum_user_table_stakind4 16 +#define Anum_user_table_stakind5 17 +#define Anum_user_table_staop1 18 +#define Anum_user_table_staop2 19 +#define Anum_user_table_staop3 20 +#define Anum_user_table_staop4 21 +#define Anum_user_table_staop5 22 +#define Anum_user_table_stanumbers1 23 +#define Anum_user_table_stanumbers2 24 +#define Anum_user_table_stanumbers3 25 +#define Anum_user_table_stanumbers4 26 +#define Anum_user_table_stanumbers5 27 +#define Anum_user_table_stavalues1 28 +#define Anum_user_table_stavalues2 29 +#define Anum_user_table_stavalues3 30 +#define Anum_user_table_stavalues4 31 +#define Anum_user_table_stavalues5 32 +#define Anum_user_table_stadndistinct 33 +#define Anum_user_table_staextinfo 34 static List* GetRelationsInSchema(char *namespc) { @@ -116,3 +192,3267 @@ gs_analyze_schema_tables(PG_FUNCTION_ARGS) PG_RETURN_VOID(); } +static void execute_sql_string(const char* sql) +{ + int ret; + if ((ret = SPI_connect()) < 0) { + ereport(ERROR, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("SPI_connect failed: %s", SPI_result_code_string(ret)))); + } + + ret = SPI_execute(sql, false, 0); + if (ret != SPI_OK_UTILITY) { + ereport(ERROR, + (errcode(ERRCODE_SPI_EXECUTE_FAILURE), + errmsg("SPI_execute execute job interval fail, job_id: %d.", ret))); + } + + SPI_finish(); +} + +Datum gs_create_stat_table(PG_FUNCTION_ARGS) +{ + char* ownname = text_to_cstring(PG_GETARG_TEXT_P(0)); + char* stattab = text_to_cstring(PG_GETARG_TEXT_P(1)); + bool global_temporary = PG_GETARG_BOOL(3); + char* tblspace = NULL; + + if (!PG_ARGISNULL(2)) { + tblspace = text_to_cstring(PG_GETARG_TEXT_P(2)); + } + + bool prev = g_instance.attr.attr_common.allowSystemTableMods; + g_instance.attr.attr_common.allowSystemTableMods = true; + + StringInfo execute_sql = makeStringInfo(); + appendStringInfo(execute_sql, "CREATE "); + + if (global_temporary) { + appendStringInfo(execute_sql, "GLOBAL TMEPORARY "); + } + + appendStringInfo(execute_sql, "TABLE %s.%s (namespaceid oid, starelid oid, partid oid, statype \"char\"," \ + "starelkind \"char\", staattnum smallint, stainherit boolean, stanullfrac real, stawidth integer," \ + "stadistinct real, reltuples double precision, relpages double precision, stakind1 smallint, stakind2 smallint,"\ + "stakind3 smallint, stakind4 smallint, stakind5 smallint, staop1 oid, staop2 oid, staop3 oid, staop4 oid," \ + "staop5 oid, stanumbers1 real[], stanumbers2 real[], stanumbers3 real[], stanumbers4 real[],stanumbers5 real[],"\ + "stavalues1 anyarray, stavalues2 anyarray, stavalues3 anyarray, stavalues4 anyarray, stavalues5 anyarray," \ + "stadndistinct real, staextinfo text)", quote_identifier(ownname), quote_identifier(stattab)); + + if (tblspace != NULL) { + appendStringInfo(execute_sql, " TABLESPACE %s", quote_identifier(tblspace)); + } + appendStringInfo(execute_sql, ";"); + + execute_sql_string(execute_sql->data); + resetStringInfo(execute_sql); + appendStringInfo(execute_sql, "CREATE INDEX %s_rel_type_idx ON %s.%s USING BTREE (starelid, statype);", + stattab, quote_identifier(ownname), quote_identifier(stattab)); + execute_sql_string(execute_sql->data); + resetStringInfo(execute_sql); + appendStringInfo(execute_sql, + "CREATE INDEX %s_rel_part_type_idx ON %s.%s USING BTREE (starelid, partid, statype);", + stattab, quote_identifier(ownname), quote_identifier(stattab)); + execute_sql_string(execute_sql->data); + resetStringInfo(execute_sql); + appendStringInfo(execute_sql, + "CREATE INDEX %s_rel_type_kind_attnum_idx ON %s.%s USING BTREE (starelid, statype, starelkind, staattnum);", + stattab, quote_identifier(ownname), quote_identifier(stattab)); + execute_sql_string(execute_sql->data); + resetStringInfo(execute_sql); + + g_instance.attr.attr_common.allowSystemTableMods = prev; + + pfree_ext(ownname); + pfree_ext(stattab); + pfree_ext(tblspace); + + PG_RETURN_VOID(); +} + +static void DeletePgStatisticLock(char* ownname, char* tabname, char* partname) +{ + Relation rel = NULL; + HeapTuple tup = NULL; + SysScanDesc sysscan; + + Oid namespaceid = LookupNamespaceNoError(ownname); + if (!OidIsValid(namespaceid)) { + ereport(ERROR, (errmsg("Schema \"%s\" is not exists", ownname))); + } + + rel = heap_open(StatisticLockRelationId, RowExclusiveLock); + if (tabname != NULL) { + Oid relid = get_relname_relid(tabname, namespaceid); + if (!OidIsValid(relid)) { + ereport(ERROR, (errmsg("Relation \"%s\".\"%s\" is not exists", ownname, tabname))); + } + + if (partname != NULL) { + ScanKeyData skey[3]; + ScanKeyInit(&skey[0], Anum_pg_statistic_lock_relid, BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(relid)); + ScanKeyInit(&skey[1], Anum_pg_statistic_lock_partname, BTEqualStrategyNumber, F_TEXTEQ, + CStringGetTextDatum(partname)); + ScanKeyInit(&skey[2], Anum_pg_statistic_lock_stalocktype, BTEqualStrategyNumber, F_CHAREQ, + CharGetDatum(STALOCKTYPE_PARTITION)); + sysscan = systable_beginscan(rel, StatisticLockRelPartTypeIndexId, true, SnapshotNow, 3, skey); + } else { + ScanKeyData skey[2]; + ScanKeyInit(&skey[0], Anum_pg_statistic_lock_relid, BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(relid)); + ScanKeyInit(&skey[1], Anum_pg_statistic_lock_stalocktype, BTEqualStrategyNumber, F_CHAREQ, + CharGetDatum(STALOCKTYPE_RELATION)); + sysscan = systable_beginscan(rel, StatisticLockRelTypeIndexId, true, SnapshotNow, 2, skey); + } + } else { + ScanKeyData skey[2]; + ScanKeyInit(&skey[0], Anum_pg_statistic_lock_namespace, BTEqualStrategyNumber, F_TEXTEQ, + CStringGetTextDatum(ownname)); + ScanKeyInit(&skey[1], Anum_pg_statistic_lock_stalocktype, BTEqualStrategyNumber, F_CHAREQ, + CharGetDatum(STALOCKTYPE_SCHEMA)); + sysscan = systable_beginscan(rel, StatisticLockNamespacTypeIndexId, true, SnapshotNow, 2, skey); + } + + while (HeapTupleIsValid(tup = systable_getnext(sysscan))) { + simple_heap_delete(rel, &tup->t_self); + } + + systable_endscan(sysscan); + heap_close(rel, RowExclusiveLock); +} + +static void DeletePgStatisticHistory(char* ownname, char* tabname) +{ + ScanKeyData skey[2]; + SysScanDesc sysscan; + HeapTuple tuple; + + Oid namespaceid = LookupNamespaceNoError(ownname); + if (!OidIsValid(namespaceid)) { + ereport(ERROR, (errmsg("Schema \"%s\" is not exists", ownname))); + } + + Oid relid = get_relname_relid(tabname, namespaceid); + if (!OidIsValid(relid)) { + ereport(ERROR, (errmsg("Relation \"%s\".\"%s\" is not exists", ownname, tabname))); + } + + ScanKeyInit(&skey[0], Anum_pg_statistic_history_namespaceid, BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(namespaceid)); + ScanKeyInit(&skey[1], Anum_pg_statistic_history_starelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relid)); + + Relation rel = heap_open(StatisticHistoryRelationId, RowExclusiveLock); + sysscan = systable_beginscan(rel, StatisticHistoryNamespacTabIndexId, true, SnapshotNow, 2, skey); + while (HeapTupleIsValid(tuple = systable_getnext(sysscan))) { + simple_heap_delete(rel, &tuple->t_self); + } + + systable_endscan(sysscan); + heap_close(rel, RowExclusiveLock); +} + +Datum gs_drop_stat_table(PG_FUNCTION_ARGS) +{ + char* ownname = text_to_cstring(PG_GETARG_TEXT_P(0)); + char* stattab = text_to_cstring(PG_GETARG_TEXT_P(1)); + + DeletePgStatisticLock(ownname, stattab, NULL); + DeletePgStatisticHistory(ownname, stattab); + + StringInfo execute_sql = makeStringInfo(); + appendStringInfo(execute_sql, "DROP TABLE %s.%s;", quote_identifier(ownname), quote_identifier(stattab)); + execute_sql_string(execute_sql->data); + resetStringInfo(execute_sql); + + pfree_ext(ownname); + pfree_ext(stattab); + + PG_RETURN_VOID(); +} + +static void InsertPgStatisticLock(char* ownname, char* tabname, char* partname, char stalocktype) +{ + Datum values[Natts_pg_statistic_lock]; + bool nulls[Natts_pg_statistic_lock]; + bool replaces[Natts_pg_statistic_lock]; + HeapTuple tup = NULL; + HeapTuple tuple = NULL; + Oid relid = InvalidOid; + errno_t rc = 0; + SysScanDesc sysscan; + bool exists = false; + + Relation rel = heap_open(StatisticLockRelationId, RowExclusiveLock); + Oid namespaceid = LookupNamespaceNoError(ownname); + if (!OidIsValid(namespaceid)) { + ereport(ERROR, (errmsg("Schema \"%s\" is not exists", ownname))); + } + + if (tabname != NULL) { + relid = get_relname_relid(tabname, namespaceid); + if (!OidIsValid(relid)) { + ereport(ERROR, (errmsg("Relation \"%s\".\"%s\" is not exists", ownname, tabname))); + } + } + + if (stalocktype == STALOCKTYPE_SCHEMA) { + ScanKeyData skey[2]; + ScanKeyInit(&skey[0], Anum_pg_statistic_lock_namespace, BTEqualStrategyNumber, F_TEXTEQ, + CStringGetDatum(ownname)); + ScanKeyInit(&skey[1], Anum_pg_statistic_lock_stalocktype, BTEqualStrategyNumber, F_CHAREQ, + CharGetDatum(STALOCKTYPE_RELATION)); + sysscan = systable_beginscan(rel, StatisticLockNamespacTypeIndexId, true, SnapshotNow, 2, skey); + } else if (stalocktype == STALOCKTYPE_RELATION) { + ScanKeyData skey[2]; + ScanKeyInit(&skey[0], Anum_pg_statistic_lock_relid, BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(relid)); + ScanKeyInit(&skey[1], Anum_pg_statistic_lock_stalocktype, BTEqualStrategyNumber, F_CHAREQ, + CharGetDatum(STALOCKTYPE_RELATION)); + sysscan = systable_beginscan(rel, StatisticLockRelTypeIndexId, true, SnapshotNow, 2, skey); + } else if (stalocktype == STALOCKTYPE_PARTITION) { + ScanKeyData skey[3]; + ScanKeyInit(&skey[0], Anum_pg_statistic_lock_relid, BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(relid)); + ScanKeyInit(&skey[1], Anum_pg_statistic_lock_partname, BTEqualStrategyNumber, F_TEXTEQ, + CStringGetTextDatum(partname)); + ScanKeyInit(&skey[2], Anum_pg_statistic_lock_stalocktype, BTEqualStrategyNumber, F_CHAREQ, + CharGetDatum(STALOCKTYPE_PARTITION)); + sysscan = systable_beginscan(rel, StatisticLockRelPartTypeIndexId, true, SnapshotNow, 3, skey); + } + + rc = memset_s(values, sizeof(values), 0, sizeof(values)); + securec_check(rc, "\0", "\0"); + rc = memset_s(nulls, sizeof(nulls), false, sizeof(nulls)); + securec_check(rc, "\0", "\0"); + rc = memset_s(replaces, sizeof(replaces), true, sizeof(replaces)); + securec_check(rc, "\0", "\0"); + + values[Anum_pg_statistic_lock_namespace - 1] = CStringGetTextDatum(ownname); + values[Anum_pg_statistic_lock_stalocktype - 1] = CharGetDatum(stalocktype); + values[Anum_pg_statistic_lock_relid - 1] = ObjectIdGetDatum(relid); + values[Anum_pg_statistic_lock_partname - 1] = partname ? CStringGetTextDatum(partname) : CStringGetTextDatum(""); + + while (HeapTupleIsValid(tuple = systable_getnext(sysscan))) { + exists = true; + tup = heap_modify_tuple(tuple, RelationGetDescr(rel), values, nulls, replaces); + simple_heap_update(rel, &tup->t_self, tup); + } + + if (!exists) { + tup = heap_form_tuple(RelationGetDescr(rel), values, nulls); + (void)simple_heap_insert(rel, tup); + } + + CatalogUpdateIndexes(rel, tup); + systable_endscan(sysscan); + heap_freetuple_ext(tup); + heap_close(rel, RowExclusiveLock); +} + +Datum gs_lock_schema_stats(PG_FUNCTION_ARGS) +{ + char* ownname = text_to_cstring(PG_GETARG_TEXT_P(0)); + + if (t_thrd.proc->workingVersionNum >= STATISTIC_HISTORY_VERSION_NUMBER) { + InsertPgStatisticLock(ownname, NULL, NULL, STALOCKTYPE_SCHEMA); + } else { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Working Version Num less than %u does not support read/write pg_statistic_lock systable.", + STATISTIC_HISTORY_VERSION_NUMBER))); + } + + pfree_ext(ownname); + + PG_RETURN_VOID(); +} + +Datum gs_lock_table_stats(PG_FUNCTION_ARGS) +{ + char* ownname = text_to_cstring(PG_GETARG_TEXT_P(0)); + char* tabname = text_to_cstring(PG_GETARG_TEXT_P(1)); + + if (t_thrd.proc->workingVersionNum >= STATISTIC_HISTORY_VERSION_NUMBER) { + InsertPgStatisticLock(ownname, tabname, NULL, STALOCKTYPE_RELATION); + } else { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Working Version Num less than %u does not support read/write pg_statistic_lock systable.", + STATISTIC_HISTORY_VERSION_NUMBER))); + } + + pfree_ext(ownname); + pfree_ext(tabname); + + PG_RETURN_VOID(); +} + +Datum gs_lock_partition_stats(PG_FUNCTION_ARGS) +{ + char* ownname = text_to_cstring(PG_GETARG_TEXT_P(0)); + char* tabname = text_to_cstring(PG_GETARG_TEXT_P(1)); + char* partname = text_to_cstring(PG_GETARG_TEXT_P(2)); + + if (t_thrd.proc->workingVersionNum >= STATISTIC_HISTORY_VERSION_NUMBER) { + InsertPgStatisticLock(ownname, tabname, partname, STALOCKTYPE_PARTITION); + } else { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Working Version Num less than %u does not support read/write pg_statistic_lock systable.", + STATISTIC_HISTORY_VERSION_NUMBER))); + } + + pfree_ext(ownname); + pfree_ext(tabname); + pfree_ext(partname); + + PG_RETURN_VOID(); +} + +Datum gs_unlock_schema_stats(PG_FUNCTION_ARGS) +{ + char* ownname = text_to_cstring(PG_GETARG_TEXT_P(0)); + + if (t_thrd.proc->workingVersionNum >= STATISTIC_HISTORY_VERSION_NUMBER) { + DeletePgStatisticLock(ownname, NULL, NULL); + } else { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Working Version Num less than %u does not support read/write pg_statistic_lock systable.", + STATISTIC_HISTORY_VERSION_NUMBER))); + } + + pfree_ext(ownname); + + PG_RETURN_VOID(); +} + +Datum gs_unlock_table_stats(PG_FUNCTION_ARGS) +{ + char* ownname = text_to_cstring(PG_GETARG_TEXT_P(0)); + char* tabname = text_to_cstring(PG_GETARG_TEXT_P(1)); + + if (t_thrd.proc->workingVersionNum >= STATISTIC_HISTORY_VERSION_NUMBER) { + DeletePgStatisticLock(ownname, tabname, NULL); + } else { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Working Version Num less than %u does not support read/write pg_statistic_lock systable.", + STATISTIC_HISTORY_VERSION_NUMBER))); + } + + pfree_ext(ownname); + pfree_ext(tabname); + + PG_RETURN_VOID(); +} + +Datum gs_unlock_partition_stats(PG_FUNCTION_ARGS) +{ + char* ownname = text_to_cstring(PG_GETARG_TEXT_P(0)); + char* tabname = text_to_cstring(PG_GETARG_TEXT_P(1)); + char* partname = text_to_cstring(PG_GETARG_TEXT_P(2)); + + if (t_thrd.proc->workingVersionNum >= STATISTIC_HISTORY_VERSION_NUMBER) { + DeletePgStatisticLock(ownname, tabname, partname); + } else { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Working Version Num less than %u does not support read/write pg_statistic_lock systable.", + STATISTIC_HISTORY_VERSION_NUMBER))); + } + + pfree_ext(ownname); + pfree_ext(tabname); + pfree_ext(partname); + + PG_RETURN_VOID(); +} + +static void export_stats(Oid namespaceid, Oid relid, Oid starelid, char relkind, char statype, int attnum, bool inh, + HeapTuple tup) +{ + Datum values[Natts_user_table]; + bool nulls[Natts_user_table]; + bool replaces[Natts_user_table]; + Relation starel = heap_open(starelid, RowExclusiveLock); + errno_t rc = 0; + HeapTuple newtup = NULL; + bool isNull; + + rc = memset_s(values, sizeof(values), 0, sizeof(values)); + securec_check(rc, "\0", "\0"); + rc = memset_s(nulls, sizeof(nulls), true, sizeof(nulls)); + securec_check(rc, "\0", "\0"); + rc = memset_s(replaces, sizeof(replaces), true, sizeof(replaces)); + securec_check(rc, "\0", "\0"); + + values[Anum_user_table_namespaceid - 1] = ObjectIdGetDatum(namespaceid); + nulls[Anum_user_table_namespaceid - 1] = false; + values[Anum_user_table_starelid - 1] = ObjectIdGetDatum(relid); + nulls[Anum_user_table_starelid - 1] = false; + values[Anum_user_table_statype - 1] = CharGetDatum(statype); + nulls[Anum_user_table_statype - 1] = false; + values[Anum_user_table_starelkind - 1] = CharGetDatum(relkind); + nulls[Anum_user_table_starelkind - 1] = false; + values[Anum_user_table_staattnum - 1] = Int16GetDatum(attnum); + nulls[Anum_user_table_staattnum - 1] = false; + values[Anum_user_table_stainherit - 1] = BoolGetDatum(inh); + nulls[Anum_user_table_stainherit - 1] = false; + values[Anum_user_table_stanullfrac - 1] = + SysCacheGetAttr(STATRELKINDATTINH, tup, Anum_pg_statistic_stanullfrac, &isNull); + nulls[Anum_user_table_stanullfrac - 1] = isNull; + values[Anum_user_table_stawidth - 1] = + SysCacheGetAttr(STATRELKINDATTINH, tup, Anum_pg_statistic_stawidth, &isNull); + nulls[Anum_user_table_stawidth - 1] = isNull; + values[Anum_user_table_stadistinct - 1] = + SysCacheGetAttr(STATRELKINDATTINH, tup, Anum_pg_statistic_stadistinct, &isNull); + nulls[Anum_user_table_stadistinct - 1] = isNull; + values[Anum_user_table_stakind1 - 1] = + SysCacheGetAttr(STATRELKINDATTINH, tup, Anum_pg_statistic_stakind1, &isNull); + nulls[Anum_user_table_stakind1 - 1] = isNull; + values[Anum_user_table_stakind2 - 1] = + SysCacheGetAttr(STATRELKINDATTINH, tup, Anum_pg_statistic_stakind2, &isNull); + nulls[Anum_user_table_stakind2 - 1] = isNull; + values[Anum_user_table_stakind3 - 1] = + SysCacheGetAttr(STATRELKINDATTINH, tup, Anum_pg_statistic_stakind3, &isNull); + nulls[Anum_user_table_stakind3 - 1] = isNull; + values[Anum_user_table_stakind4 - 1] = + SysCacheGetAttr(STATRELKINDATTINH, tup, Anum_pg_statistic_stakind4, &isNull); + nulls[Anum_user_table_stakind4 - 1] = isNull; + values[Anum_user_table_stakind5 - 1] = + SysCacheGetAttr(STATRELKINDATTINH, tup, Anum_pg_statistic_stakind5, &isNull); + nulls[Anum_user_table_stakind5 - 1] = isNull; + values[Anum_user_table_staop1 - 1] = + SysCacheGetAttr(STATRELKINDATTINH, tup, Anum_pg_statistic_staop1, &isNull); + nulls[Anum_user_table_staop1 - 1] = isNull; + values[Anum_user_table_staop2 - 1] = + SysCacheGetAttr(STATRELKINDATTINH, tup, Anum_pg_statistic_staop2, &isNull); + nulls[Anum_user_table_staop2 - 1] = isNull; + values[Anum_user_table_staop3 - 1] = + SysCacheGetAttr(STATRELKINDATTINH, tup, Anum_pg_statistic_staop3, &isNull); + nulls[Anum_user_table_staop3 - 1] = isNull; + values[Anum_user_table_staop4 - 1] = + SysCacheGetAttr(STATRELKINDATTINH, tup, Anum_pg_statistic_staop4, &isNull); + nulls[Anum_user_table_staop4 - 1] = isNull; + values[Anum_user_table_staop5 - 1] = + SysCacheGetAttr(STATRELKINDATTINH, tup, Anum_pg_statistic_staop5, &isNull); + nulls[Anum_user_table_staop5 - 1] = isNull; + values[Anum_user_table_stanumbers1 - 1] = + SysCacheGetAttr(STATRELKINDATTINH, tup, Anum_pg_statistic_stanumbers1, &isNull); + nulls[Anum_user_table_stanumbers1 - 1] = isNull; + values[Anum_user_table_stanumbers2 - 1] = + SysCacheGetAttr(STATRELKINDATTINH, tup, Anum_pg_statistic_stanumbers2, &isNull); + nulls[Anum_user_table_stanumbers2 - 1] = isNull; + values[Anum_user_table_stanumbers3 - 1] = + SysCacheGetAttr(STATRELKINDATTINH, tup, Anum_pg_statistic_stanumbers3, &isNull); + nulls[Anum_user_table_stanumbers3 - 1] = isNull; + values[Anum_user_table_stanumbers4 - 1] = + SysCacheGetAttr(STATRELKINDATTINH, tup, Anum_pg_statistic_stanumbers4, &isNull); + nulls[Anum_user_table_stanumbers4 - 1] = isNull; + values[Anum_user_table_stanumbers5 - 1] = + SysCacheGetAttr(STATRELKINDATTINH, tup, Anum_pg_statistic_stanumbers5, &isNull); + nulls[Anum_user_table_stanumbers5 - 1] = isNull; + values[Anum_user_table_stavalues1 - 1] = + SysCacheGetAttr(STATRELKINDATTINH, tup, Anum_pg_statistic_stavalues1, &isNull); + nulls[Anum_user_table_stavalues1 - 1] = isNull; + values[Anum_user_table_stavalues2 - 1] = + SysCacheGetAttr(STATRELKINDATTINH, tup, Anum_pg_statistic_stavalues2, &isNull); + nulls[Anum_user_table_stavalues2 - 1] = isNull; + values[Anum_user_table_stavalues3 - 1] = + SysCacheGetAttr(STATRELKINDATTINH, tup, Anum_pg_statistic_stavalues3, &isNull); + nulls[Anum_user_table_stavalues3 - 1] = isNull; + values[Anum_user_table_stavalues4 - 1] = + SysCacheGetAttr(STATRELKINDATTINH, tup, Anum_pg_statistic_stavalues4, &isNull); + nulls[Anum_user_table_stavalues4 - 1] = isNull; + values[Anum_user_table_stavalues5 - 1] = + SysCacheGetAttr(STATRELKINDATTINH, tup, Anum_pg_statistic_stavalues5, &isNull); + nulls[Anum_user_table_stavalues5 - 1] = isNull; + values[Anum_user_table_stadndistinct - 1] = + SysCacheGetAttr(STATRELKINDATTINH, tup, Anum_pg_statistic_stadndistinct, &isNull); + nulls[Anum_user_table_stadndistinct - 1] = isNull; + values[Anum_user_table_staextinfo - 1] = + SysCacheGetAttr(STATRELKINDATTINH, tup, Anum_pg_statistic_staextinfo, &isNull); + nulls[Anum_user_table_staextinfo - 1] = isNull; + + bool exists = false; + HeapTuple tuple; + ScanKeyData skey[4]; + + ScanKeyInit(&skey[0], Anum_user_table_starelid, BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(relid)); + ScanKeyInit(&skey[1], Anum_user_table_statype, BTEqualStrategyNumber, F_CHAREQ, + CharGetDatum(statype)); + ScanKeyInit(&skey[2], Anum_user_table_starelkind, BTEqualStrategyNumber, F_CHAREQ, + CharGetDatum(relkind)); + ScanKeyInit(&skey[3], Anum_user_table_staattnum, BTEqualStrategyNumber, F_INT2EQ, + Int16GetDatum(attnum)); + + TableScanDesc scan = heap_beginscan(starel, SnapshotNow, 4, skey); + while (HeapTupleIsValid(tuple = heap_getnext(scan, ForwardScanDirection))) { + exists = true; + replaces[Anum_user_table_reltuples - 1] = false; + replaces[Anum_user_table_relpages - 1] = false; + replaces[Anum_user_table_partid - 1] = false; + newtup = heap_modify_tuple(tuple, RelationGetDescr(starel), values, nulls, replaces); + simple_heap_update(starel, &newtup->t_self, newtup); + break; + } + + if (!exists) { + newtup = heap_form_tuple(RelationGetDescr(starel), values, nulls); + (void)simple_heap_insert(starel, newtup); + } + + CatalogUpdateIndexes(starel, newtup); + heap_endscan(scan); + heap_freetuple_ext(newtup); + heap_close(starel, RowExclusiveLock); +} + +static void export_pg_class_stats(Oid namespaceid, Oid relid, Oid starelid, char statype) +{ + Datum values[Natts_user_table]; + bool nulls[Natts_user_table]; + bool replaces[Natts_user_table]; + Relation starel = heap_open(starelid, RowExclusiveLock); + errno_t rc = 0; + HeapTuple tup = NULL, newtup = NULL; + bool isNull; + + rc = memset_s(values, sizeof(values), 0, sizeof(values)); + securec_check(rc, "\0", "\0"); + rc = memset_s(nulls, sizeof(nulls), true, sizeof(nulls)); + securec_check(rc, "\0", "\0"); + rc = memset_s(replaces, sizeof(replaces), false, sizeof(replaces)); + securec_check(rc, "\0", "\0"); + + values[Anum_user_table_namespaceid - 1] = ObjectIdGetDatum(namespaceid); + nulls[Anum_user_table_namespaceid - 1] = false; + values[Anum_user_table_starelid - 1] = ObjectIdGetDatum(relid); + nulls[Anum_user_table_starelid - 1] = false; + values[Anum_user_table_statype - 1] = CharGetDatum(statype); + nulls[Anum_user_table_statype - 1] = false; + + tup = SearchSysCache1(RELOID, ObjectIdGetDatum(relid)); + if (HeapTupleIsValid(tup)) { + values[Anum_user_table_reltuples - 1] = + SysCacheGetAttr(RELOID, tup, Anum_pg_class_reltuples, &isNull); + nulls[Anum_user_table_reltuples - 1] = isNull; + values[Anum_user_table_relpages - 1] = + SysCacheGetAttr(RELOID, tup, Anum_pg_class_relpages, &isNull); + nulls[Anum_user_table_relpages - 1] = isNull; + + ReleaseSysCache(tup); + } + + bool exists = false; + HeapTuple tuple; + ScanKeyData skey[2]; + + ScanKeyInit(&skey[0], Anum_pg_statistic_history_starelid, BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(relid)); + ScanKeyInit(&skey[1], Anum_pg_statistic_history_statype, BTEqualStrategyNumber, F_CHAREQ, + CharGetDatum(statype)); + + TableScanDesc scan = heap_beginscan(starel, SnapshotNow, 2, skey); + while (HeapTupleIsValid(tuple = heap_getnext(scan, ForwardScanDirection))) { + exists = true; + replaces[Anum_user_table_reltuples - 1] = true; + replaces[Anum_user_table_relpages - 1] = true; + newtup = heap_modify_tuple(tuple, RelationGetDescr(starel), values, nulls, replaces); + simple_heap_update(starel, &newtup->t_self, newtup); + break; + } + + if (!exists) { + newtup = heap_form_tuple(RelationGetDescr(starel), values, nulls); + (void)simple_heap_insert(starel, newtup); + } + + CatalogUpdateIndexes(starel, newtup); + heap_endscan(scan); + heap_freetuple_ext(newtup); + heap_close(starel, RowExclusiveLock); +} + +static void export_pg_partition_stats(Oid namespaceid, Oid relid, Oid partid, Oid starelid, char statype) +{ + Datum values[Natts_user_table]; + bool nulls[Natts_user_table]; + bool replaces[Natts_user_table]; + Relation starel = heap_open(starelid, RowExclusiveLock); + errno_t rc = 0; + HeapTuple tup = NULL, newtup = NULL; + bool isNull; + + rc = memset_s(values, sizeof(values), 0, sizeof(values)); + securec_check(rc, "\0", "\0"); + rc = memset_s(nulls, sizeof(nulls), true, sizeof(nulls)); + securec_check(rc, "\0", "\0"); + rc = memset_s(replaces, sizeof(replaces), false, sizeof(replaces)); + securec_check(rc, "\0", "\0"); + + values[Anum_user_table_namespaceid - 1] = ObjectIdGetDatum(namespaceid); + nulls[Anum_user_table_namespaceid - 1] = false; + values[Anum_user_table_starelid - 1] = ObjectIdGetDatum(relid); + nulls[Anum_user_table_starelid - 1] = false; + values[Anum_user_table_partid - 1] = ObjectIdGetDatum(partid); + nulls[Anum_user_table_partid - 1] = false; + values[Anum_user_table_statype - 1] = CharGetDatum(statype); + nulls[Anum_user_table_statype - 1] = false; + + tup = SearchSysCache1(PARTRELID, ObjectIdGetDatum(partid)); + if (HeapTupleIsValid(tup)) { + values[Anum_user_table_reltuples - 1] = + SysCacheGetAttr(PARTRELID, tup, Anum_pg_partition_reltuples, &isNull); + nulls[Anum_user_table_reltuples - 1] = isNull; + values[Anum_user_table_relpages - 1] = + SysCacheGetAttr(PARTRELID, tup, Anum_pg_partition_relpages, &isNull); + nulls[Anum_user_table_relpages - 1] = isNull; + ReleaseSysCache(tup); + } + + bool exists = false; + HeapTuple tuple; + ScanKeyData skey[3]; + + ScanKeyInit(&skey[0], Anum_user_table_starelid, BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(relid)); + ScanKeyInit(&skey[1], Anum_user_table_partid, BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(partid)); + ScanKeyInit(&skey[2], Anum_user_table_statype, BTEqualStrategyNumber, F_CHAREQ, + CharGetDatum(statype)); + + TableScanDesc scan = heap_beginscan(starel, SnapshotNow, 3, skey); + while (HeapTupleIsValid(tuple = heap_getnext(scan, ForwardScanDirection))) { + exists = true; + replaces[Anum_user_table_reltuples - 1] = true; + replaces[Anum_user_table_relpages - 1] = true; + newtup = heap_modify_tuple(tuple, RelationGetDescr(starel), values, nulls, replaces); + simple_heap_update(starel, &newtup->t_self, newtup); + break; + } + + if (!exists) { + newtup = heap_form_tuple(RelationGetDescr(starel), values, nulls); + (void)simple_heap_insert(starel, newtup); + } + + CatalogUpdateIndexes(starel, newtup); + heap_endscan(scan); + heap_freetuple_ext(newtup); + heap_close(starel, RowExclusiveLock); +} + +static void export_column_stats(Oid namespaceid, Oid relid, Oid starelid, char relkind, int attnum) +{ + HeapTuple tuple = SearchSysCache4(STATRELKINDATTINH, ObjectIdGetDatum(relid), CharGetDatum(relkind), + Int16GetDatum(attnum), BoolGetDatum(false)); + if (HeapTupleIsValid(tuple)) { + export_stats(namespaceid, relid, starelid, relkind, STATYPE_COLUMN, attnum, false, tuple); + ReleaseSysCache(tuple); + } +} + +Datum gs_export_column_stats(PG_FUNCTION_ARGS) +{ + char* ownname = text_to_cstring(PG_GETARG_TEXT_P(0)); + char* tabname = text_to_cstring(PG_GETARG_TEXT_P(1)); + char* colname = text_to_cstring(PG_GETARG_TEXT_P(2)); + char* stattab = text_to_cstring(PG_GETARG_TEXT_P(3)); + char* statown = NULL; + + if (!PG_ARGISNULL(4)) { + statown = text_to_cstring(PG_GETARG_TEXT_P(4)); + } else { + statown = pstrdup(ownname); + } + + Oid namespaceid = LookupNamespaceNoError(ownname); + Oid relid = get_relname_relid(tabname, namespaceid); + if (!OidIsValid(relid)) { + ereport(ERROR, (errmsg("Relation \"%s\".\"%s\" is not exists", ownname, tabname))); + } + + if (!pg_class_ownercheck(relid, GetUserId()) && !initialuser()) { + ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("Permission denied. Only dba or table owner can export stats."))); + } + + Oid starelid = get_relname_relid(stattab, LookupNamespaceNoError(statown)); + if (!OidIsValid(starelid)) { + ereport(ERROR, (errmsg("Relation \"%s\".\"%s\" is not exists", statown, stattab))); + } + + HeapTuple tup = SearchSysCache2(ATTNAME, ObjectIdGetDatum(relid), NameGetDatum(colname)); + if (HeapTupleIsValid(tup)) { + char relkind = check_rel_is_partitioned(relid) ? STARELKIND_PARTITION : STARELKIND_CLASS; + Form_pg_attribute attrtup = (Form_pg_attribute)GETSTRUCT(tup); + export_column_stats(namespaceid, relid, starelid, relkind, attrtup->attnum); + ReleaseSysCache(tup); + } else { + ereport(ERROR, (errmsg("Column \"%s\" is not exists in relation %s.%s", colname, ownname, tabname))); + } + + pfree_ext(ownname); + pfree_ext(tabname); + pfree_ext(colname); + pfree_ext(stattab); + + PG_RETURN_VOID(); +} + +Datum gs_export_index_stats(PG_FUNCTION_ARGS) +{ + char* ownname = text_to_cstring(PG_GETARG_TEXT_P(0)); + char* indname = text_to_cstring(PG_GETARG_TEXT_P(1)); + char* stattab = text_to_cstring(PG_GETARG_TEXT_P(2)); + char* statown = NULL; + + if (!PG_ARGISNULL(3)) { + statown = text_to_cstring(PG_GETARG_TEXT_P(3)); + } else { + statown = pstrdup(ownname); + } + + Oid namespaceid = LookupNamespaceNoError(ownname); + HeapTuple tup = SearchSysCache2(RELNAMENSP, NameGetDatum(indname), ObjectIdGetDatum(namespaceid)); + if (HeapTupleIsValid(tup)) { + Oid indexid = InvalidOid; + bool isNull; + Datum dat; + int2vector indkey; + HeapTuple indexTuple; + Oid indrelid; + + dat = SysCacheGetAttr(RELNAMENSP, tup, ObjectIdAttributeNumber, &isNull); + if (!isNull) { + indexid = DatumGetObjectId(dat); + } + + indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexid)); + if (HeapTupleIsValid(indexTuple)) { + Form_pg_index indextup = (Form_pg_index)GETSTRUCT(indexTuple); + indrelid = indextup->indrelid; + if (!pg_class_ownercheck(indrelid, GetUserId()) && !initialuser()) { + ReleaseSysCache(indexTuple); + ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("Permission denied. Only dba or table owner can import stats."))); + } + + Oid starelid = get_relname_relid(stattab, LookupNamespaceNoError(statown)); + if (!OidIsValid(starelid)) { + ereport(ERROR, (errmsg("Relation \"%s\".\"%s\" is not exists", statown, stattab))); + } + + indkey = indextup->indkey; + char relkind = check_rel_is_partitioned(indrelid) ? STARELKIND_PARTITION : STARELKIND_CLASS; + for (int i = 0; i < indkey.dim1; i++) { + export_column_stats(namespaceid, indrelid, starelid, relkind, indkey.values[i]); + } + ReleaseSysCache(indexTuple); + } else { + ereport(ERROR, (errmsg("Index \"%s\".\"%s\" is not exists", ownname, indname))); + } + + ReleaseSysCache(tup); + } + + pfree_ext(ownname); + pfree_ext(indname); + pfree_ext(stattab); + + PG_RETURN_VOID(); +} + +static void export_subpartition_stats(Relation rel, Oid namespaceid, Oid relid, char* partname, Oid starelid) +{ + List* subpartids = RelationGetSubPartitionOidList(rel); + ListCell* lc; + foreach (lc, subpartids) { + Oid subpartid = lfirst_oid(lc); + if (partname == NULL || strcmp(getPartitionName(subpartid, false), partname) == 0) { + export_pg_partition_stats(namespaceid, relid, subpartid, starelid, STATYPE_PARTITION); + } + } +} + +static void export_table_stats(char* ownname, char* tabname, char* partname, char* statown, char* stattab, bool cascade) +{ + Oid namespaceid = LookupNamespaceNoError(ownname); + Oid relid = get_relname_relid(tabname, namespaceid); + if (!OidIsValid(relid)) { + ereport(ERROR, (errmsg("Relation \"%s\".\"%s\" is not exists", ownname, tabname))); + } + + if (!pg_class_ownercheck(relid, GetUserId()) && !initialuser()) { + ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("Permission denied. Only dba or table owner can export stats."))); + } + + Oid starelid = get_relname_relid(stattab, LookupNamespaceNoError(statown)); + if (!OidIsValid(starelid) && stattab) { + ereport(ERROR, (errmsg("Relation \"%s\".\"%s\" is not exists", statown, stattab))); + } + + export_pg_class_stats(namespaceid, relid, starelid, STATYPE_RELATION); + + Relation rel = heap_open(relid, AccessShareLock); + int attnums = RelationGetNumberOfAttributes(rel); + char relkind = check_rel_is_partitioned(relid) ? STARELKIND_PARTITION : STARELKIND_CLASS; + bool is_index_column[attnums]; + int2vector indkey; + errno_t rc = 0; + + rc = memset_s(is_index_column, sizeof(is_index_column), false, sizeof(is_index_column)); + securec_check(rc, "\0", "\0"); + + List* indexOids = RelationGetIndexList(rel); + ListCell* lc = NULL; + foreach (lc, indexOids) { + Oid indexOid = lfirst_oid(lc); + HeapTuple indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexOid)); + if (HeapTupleIsValid(indexTuple)) { + Form_pg_index indextup = (Form_pg_index)GETSTRUCT(indexTuple); + indkey = indextup->indkey; + for (int i = 0; i < indkey.dim1; i++) { + is_index_column[indkey.values[i] - 1] = true; + if (cascade) { + export_column_stats(namespaceid, relid, starelid, relkind, indkey.values[i]); + } + } + ReleaseSysCache(indexTuple); + } + } + + for (int i = 1; i <= attnums && !is_index_column[i - 1]; i++) { + export_column_stats(namespaceid, relid, starelid, relkind, i); + } + + List* partids = relationGetPartitionOidList(rel); + foreach (lc, partids) { + Oid partid = lfirst_oid(lc); + if (partname == NULL || strcmp(getPartitionName(partid, false), partname) == 0) { + export_pg_partition_stats(namespaceid, relid, partid, starelid, STATYPE_PARTITION); + } + + export_subpartition_stats(rel, namespaceid, relid, partname, starelid); + } + + heap_close(rel, AccessShareLock); + + list_free_ext(indexOids); + list_free_ext(partids); +} + +Datum gs_export_table_stats(PG_FUNCTION_ARGS) +{ + char* ownname = text_to_cstring(PG_GETARG_TEXT_P(0)); + char* tabname = text_to_cstring(PG_GETARG_TEXT_P(1)); + char* partname = NULL; + char* stattab = text_to_cstring(PG_GETARG_TEXT_P(3)); + char* statown = NULL; + bool cascade = PG_GETARG_BOOL(5); + + if (!PG_ARGISNULL(2)) { + partname = text_to_cstring(PG_GETARG_TEXT_P(2)); + } + + if (!PG_ARGISNULL(4)) { + statown = text_to_cstring(PG_GETARG_TEXT_P(4)); + } else { + statown = pstrdup(ownname); + } + + export_table_stats(ownname, tabname, partname, statown, stattab, cascade); + + pfree_ext(ownname); + pfree_ext(tabname); + pfree_ext(partname); + pfree_ext(stattab); + pfree_ext(statown); + + PG_RETURN_VOID(); +} + +Datum gs_export_schema_stats(PG_FUNCTION_ARGS) +{ + char* ownname = text_to_cstring(PG_GETARG_TEXT_P(0)); + char* stattab = text_to_cstring(PG_GETARG_TEXT_P(1)); + char* statown = NULL; + + if (!PG_ARGISNULL(2)) { + statown = text_to_cstring(PG_GETARG_TEXT_P(2)); + } else { + statown = pstrdup(ownname); + } + + List* relnames = GetRelationsInSchema(ownname); + ListCell* lc; + foreach(lc, relnames) { + char* relname = (char*)lfirst(lc); + export_table_stats(ownname, relname, NULL, statown, stattab, true); + } + + pfree_ext(ownname); + pfree_ext(stattab); + pfree_ext(statown); + list_free(relnames); + + PG_RETURN_VOID(); +} + +static void import_pg_statistic_stats(HeapTuple tuple, TupleDesc tupdesc, Oid relid, char relkind, int attnum) +{ + Datum values[Natts_pg_statistic]; + bool nulls[Natts_pg_statistic]; + bool replaces[Natts_pg_statistic]; + HeapTuple tup = NULL; + HeapTuple oldtuple = NULL; + bool isNull; + errno_t rc = 0; + + Relation rel = heap_open(StatisticRelationId, RowExclusiveLock); + oldtuple = SearchSysCache4(STATRELKINDATTINH, ObjectIdGetDatum(relid), CharGetDatum(relkind), + Int16GetDatum(attnum), BoolGetDatum(false)); + + rc = memset_s(values, sizeof(values), 0, sizeof(values)); + securec_check(rc, "\0", "\0"); + rc = memset_s(nulls, sizeof(nulls), false, sizeof(nulls)); + securec_check(rc, "\0", "\0"); + rc = memset_s(replaces, sizeof(replaces), true, sizeof(replaces)); + securec_check(rc, "\0", "\0"); + + values[Anum_pg_statistic_starelid - 1] = ObjectIdGetDatum(relid); + values[Anum_pg_statistic_starelkind - 1] = CharGetDatum(relkind); + values[Anum_pg_statistic_staattnum - 1] = Int16GetDatum(attnum); + values[Anum_pg_statistic_stainherit - 1] = BoolGetDatum(false); + values[Anum_pg_statistic_stanullfrac - 1] = heap_getattr(tuple, Anum_user_table_stanullfrac, tupdesc, &isNull); + nulls[Anum_pg_statistic_stanullfrac - 1] = isNull; + values[Anum_pg_statistic_stawidth - 1] = heap_getattr(tuple, Anum_user_table_stawidth, tupdesc, &isNull); + nulls[Anum_pg_statistic_stawidth - 1] = isNull; + values[Anum_pg_statistic_stadistinct - 1] = heap_getattr(tuple, Anum_user_table_stadistinct, tupdesc, &isNull); + nulls[Anum_pg_statistic_stadistinct - 1] = isNull; + values[Anum_pg_statistic_stakind1 - 1] = heap_getattr(tuple, Anum_user_table_stakind1, tupdesc, &isNull); + nulls[Anum_pg_statistic_stakind1 - 1] = isNull; + values[Anum_pg_statistic_stakind2 - 1] = heap_getattr(tuple, Anum_user_table_stakind2, tupdesc, &isNull); + nulls[Anum_pg_statistic_stakind2 - 1] = isNull; + values[Anum_pg_statistic_stakind3 - 1] = heap_getattr(tuple, Anum_user_table_stakind3, tupdesc, &isNull); + nulls[Anum_pg_statistic_stakind3 - 1] = isNull; + values[Anum_pg_statistic_stakind4 - 1] = heap_getattr(tuple, Anum_user_table_stakind4, tupdesc, &isNull); + nulls[Anum_pg_statistic_stakind4 - 1] = isNull; + values[Anum_pg_statistic_stakind5 - 1] = heap_getattr(tuple, Anum_user_table_stakind5, tupdesc, &isNull); + nulls[Anum_pg_statistic_stakind5 - 1] = isNull; + values[Anum_pg_statistic_staop1 - 1] = heap_getattr(tuple, Anum_user_table_staop1, tupdesc, &isNull); + nulls[Anum_pg_statistic_staop1 - 1] = isNull; + values[Anum_pg_statistic_staop2 - 1] = heap_getattr(tuple, Anum_user_table_staop2, tupdesc, &isNull); + nulls[Anum_pg_statistic_staop2 - 1] = isNull; + values[Anum_pg_statistic_staop3 - 1] = heap_getattr(tuple, Anum_user_table_staop3, tupdesc, &isNull); + nulls[Anum_pg_statistic_staop3 - 1] = isNull; + values[Anum_pg_statistic_staop4 - 1] = heap_getattr(tuple, Anum_user_table_staop4, tupdesc, &isNull); + nulls[Anum_pg_statistic_staop4 - 1] = isNull; + values[Anum_pg_statistic_staop5 - 1] = heap_getattr(tuple, Anum_user_table_staop5, tupdesc, &isNull); + nulls[Anum_pg_statistic_staop5 - 1] = isNull; + values[Anum_pg_statistic_stanumbers1 - 1] = heap_getattr(tuple, Anum_user_table_stanumbers1, tupdesc, &isNull); + nulls[Anum_pg_statistic_stanumbers1 - 1] = isNull; + values[Anum_pg_statistic_stanumbers2 - 1] = heap_getattr(tuple, Anum_user_table_stanumbers2, tupdesc, &isNull); + nulls[Anum_pg_statistic_stanumbers2 - 1] = isNull; + values[Anum_pg_statistic_stanumbers3 - 1] = heap_getattr(tuple, Anum_user_table_stanumbers3, tupdesc, &isNull); + nulls[Anum_pg_statistic_stanumbers3 - 1] = isNull; + values[Anum_pg_statistic_stanumbers4 - 1] = heap_getattr(tuple, Anum_user_table_stanumbers4, tupdesc, &isNull); + nulls[Anum_pg_statistic_stanumbers4 - 1] = isNull; + values[Anum_pg_statistic_stanumbers5 - 1] = heap_getattr(tuple, Anum_user_table_stanumbers5, tupdesc, &isNull); + nulls[Anum_pg_statistic_stanumbers5 - 1] = isNull; + values[Anum_pg_statistic_stavalues1 - 1] = heap_getattr(tuple, Anum_user_table_stavalues1, tupdesc, &isNull); + nulls[Anum_pg_statistic_stavalues1 - 1] = isNull; + values[Anum_pg_statistic_stavalues2 - 1] = heap_getattr(tuple, Anum_user_table_stavalues2, tupdesc, &isNull); + nulls[Anum_pg_statistic_stavalues2 - 1] = isNull; + values[Anum_pg_statistic_stavalues3 - 1] = heap_getattr(tuple, Anum_user_table_stavalues3, tupdesc, &isNull); + nulls[Anum_pg_statistic_stavalues3 - 1] = isNull; + values[Anum_pg_statistic_stavalues4 - 1] = heap_getattr(tuple, Anum_user_table_stavalues4, tupdesc, &isNull); + nulls[Anum_pg_statistic_stavalues4 - 1] = isNull; + values[Anum_pg_statistic_stavalues5 - 1] = heap_getattr(tuple, Anum_user_table_stavalues5, tupdesc, &isNull); + nulls[Anum_pg_statistic_stavalues5 - 1] = isNull; + values[Anum_pg_statistic_stadndistinct - 1] = heap_getattr(tuple, Anum_user_table_stadndistinct, tupdesc, &isNull); + nulls[Anum_pg_statistic_stadndistinct - 1] = isNull; + values[Anum_pg_statistic_staextinfo - 1] = heap_getattr(tuple, Anum_user_table_staextinfo, tupdesc, &isNull); + nulls[Anum_pg_statistic_staextinfo - 1] = isNull; + + if (HeapTupleIsValid(oldtuple)) { + tup = heap_modify_tuple(oldtuple,RelationGetDescr(rel), values, nulls, replaces); + simple_heap_update(rel, &tup->t_self, tup); + ReleaseSysCache(oldtuple); + } else { + tup = heap_form_tuple(RelationGetDescr(rel), values, nulls); + (void)simple_heap_insert(rel, tup); + } + + CatalogUpdateIndexes(rel, tup); + heap_freetuple_ext(tup); + heap_close(rel, RowExclusiveLock); +} + +static TimestampTz get_last_current_analyzetime(void) +{ + SysScanDesc sysscan; + ScanKeyData skey[1]; + HeapTuple tuple = NULL; + TimestampTz result = 0; + + ScanKeyInit(&skey[0], Anum_pg_statistic_history_current_analyzetime, BTLessEqualStrategyNumber, F_TIMESTAMP_LE, + TimestampTzGetDatum(GetCurrentTimestamp())); + + Relation rel = heap_open(StatisticHistoryRelationId, AccessShareLock); + sysscan = systable_beginscan(rel, StatisticHistoryCurrentAnalyzetimeIndexId, true, SnapshotNow, 1, skey); + while (HeapTupleIsValid(tuple = systable_getnext(sysscan))) { + Form_pg_statistic_history statshistup = (Form_pg_statistic_history)GETSTRUCT(tuple); + TimestampTz current_analyzetime = DatumGetTimestampTz(statshistup->current_analyzetime); + if (current_analyzetime > result) { + result = current_analyzetime; + } + } + + systable_endscan(sysscan); + heap_close(rel, AccessShareLock); + return result; +} + +static void import_pg_statistic_hisotry_stats(HeapTuple tuple, TupleDesc tupdesc, Oid namespaceid, Oid relid, + char relkind, int attnum) +{ + Datum values[Natts_pg_statistic_history]; + bool nulls[Natts_pg_statistic_history]; + bool replaces[Natts_pg_statistic_history]; + HeapTuple tup = NULL; + bool isNull; + errno_t rc = 0; + + Relation rel = heap_open(StatisticHistoryRelationId, RowExclusiveLock); + TimestampTz last_analyzetime = get_last_current_analyzetime(); + + rc = memset_s(values, sizeof(values), 0, sizeof(values)); + securec_check(rc, "\0", "\0"); + rc = memset_s(nulls, sizeof(nulls), true, sizeof(nulls)); + securec_check(rc, "\0", "\0"); + rc = memset_s(replaces, sizeof(replaces), true, sizeof(replaces)); + securec_check(rc, "\0", "\0"); + + values[Anum_pg_statistic_history_namespaceid - 1] = ObjectIdGetDatum(namespaceid); + nulls[Anum_pg_statistic_history_namespaceid - 1] = false; + values[Anum_pg_statistic_history_starelid - 1] = ObjectIdGetDatum(relid); + nulls[Anum_pg_statistic_history_starelid - 1] = false; + nulls[Anum_pg_statistic_history_partid - 1] = true; + values[Anum_pg_statistic_history_statype - 1] = + heap_getattr(tuple, Anum_user_table_statype, tupdesc, &isNull); + nulls[Anum_pg_statistic_history_statype - 1] = isNull; + values[Anum_pg_statistic_history_last_analyzetime - 1] = TimestampTzGetDatum(last_analyzetime); + nulls[Anum_pg_statistic_history_stanullfrac - 1] = last_analyzetime == 0 ? true : false; + values[Anum_pg_statistic_history_current_analyzetime - 1] = TimestampTzGetDatum(GetCurrentTimestamp()); + nulls[Anum_pg_statistic_history_stanullfrac - 1] = false; + values[Anum_pg_statistic_history_starelkind - 1] = CharGetDatum(relkind); + nulls[Anum_pg_statistic_history_starelkind - 1] = false; + values[Anum_pg_statistic_history_staattnum - 1] = Int16GetDatum(attnum); + nulls[Anum_pg_statistic_history_staattnum - 1] = isNull; + values[Anum_pg_statistic_history_stainherit - 1] = + heap_getattr(tuple, Anum_user_table_stainherit, tupdesc, &isNull); + nulls[Anum_pg_statistic_history_stainherit - 1] = isNull; + values[Anum_pg_statistic_history_stanullfrac - 1] = + heap_getattr(tuple, Anum_user_table_stanullfrac, tupdesc, &isNull); + nulls[Anum_pg_statistic_history_stanullfrac - 1] = isNull; + values[Anum_pg_statistic_history_stawidth - 1] = + heap_getattr(tuple, Anum_user_table_stawidth, tupdesc, &isNull); + nulls[Anum_pg_statistic_history_stawidth - 1] = isNull; + values[Anum_pg_statistic_history_stadistinct - 1] = + heap_getattr(tuple, Anum_user_table_stadistinct, tupdesc, &isNull); + nulls[Anum_pg_statistic_history_reltuples - 1] = isNull; + values[Anum_pg_statistic_history_reltuples - 1] = + heap_getattr(tuple, Anum_user_table_relpages, tupdesc, &isNull); + nulls[Anum_pg_statistic_history_relpages - 1] = isNull; + values[Anum_pg_statistic_history_stakind1 - 1] = + heap_getattr(tuple, Anum_user_table_stakind1, tupdesc, &isNull); + nulls[Anum_pg_statistic_history_stakind1 - 1] = isNull; + values[Anum_pg_statistic_history_stakind2 - 1] = + heap_getattr(tuple, Anum_user_table_stakind2, tupdesc, &isNull); + nulls[Anum_pg_statistic_history_stakind2 - 1] = isNull; + values[Anum_pg_statistic_history_stakind3 - 1] = + heap_getattr(tuple, Anum_user_table_stakind3, tupdesc, &isNull); + nulls[Anum_pg_statistic_history_stakind3 - 1] = isNull; + values[Anum_pg_statistic_history_stakind4 - 1] = + heap_getattr(tuple, Anum_user_table_stakind4, tupdesc, &isNull); + nulls[Anum_pg_statistic_history_stakind4 - 1] = isNull; + values[Anum_pg_statistic_history_stakind5 - 1] = + heap_getattr(tuple, Anum_user_table_stakind5, tupdesc, &isNull); + nulls[Anum_pg_statistic_history_stakind5 - 1] = isNull; + values[Anum_pg_statistic_history_staop1 - 1] = + heap_getattr(tuple, Anum_user_table_staop1, tupdesc, &isNull); + nulls[Anum_pg_statistic_history_staop1 - 1] = isNull; + values[Anum_pg_statistic_history_staop2 - 1] = + heap_getattr(tuple, Anum_user_table_staop2, tupdesc, &isNull); + nulls[Anum_pg_statistic_history_staop2 - 1] = isNull; + values[Anum_pg_statistic_history_staop3 - 1] = + heap_getattr(tuple, Anum_user_table_staop3, tupdesc, &isNull); + nulls[Anum_pg_statistic_history_staop3 - 1] = isNull; + values[Anum_pg_statistic_history_staop4 - 1] = + heap_getattr(tuple, Anum_user_table_staop4, tupdesc, &isNull); + nulls[Anum_pg_statistic_history_staop4 - 1] = isNull; + values[Anum_pg_statistic_history_staop5 - 1] = + heap_getattr(tuple, Anum_user_table_staop5, tupdesc, &isNull); + nulls[Anum_pg_statistic_history_staop5 - 1] = isNull; + values[Anum_pg_statistic_history_stanumbers1 - 1] = + heap_getattr(tuple, Anum_user_table_stanumbers1, tupdesc, &isNull); + nulls[Anum_pg_statistic_history_stanumbers1 - 1] = isNull; + values[Anum_pg_statistic_history_stanumbers2 - 1] = + heap_getattr(tuple, Anum_user_table_stanumbers2, tupdesc, &isNull); + nulls[Anum_pg_statistic_history_stanumbers2 - 1] = isNull; + values[Anum_pg_statistic_history_stanumbers3 - 1] = + heap_getattr(tuple, Anum_user_table_stanumbers3, tupdesc, &isNull); + nulls[Anum_pg_statistic_history_stanumbers3 - 1] = isNull; + values[Anum_pg_statistic_history_stanumbers4 - 1] = + heap_getattr(tuple, Anum_user_table_stanumbers4, tupdesc, &isNull); + nulls[Anum_pg_statistic_history_stanumbers4 - 1] = isNull; + values[Anum_pg_statistic_history_stanumbers5 - 1] = + heap_getattr(tuple, Anum_user_table_stanumbers5, tupdesc, &isNull); + nulls[Anum_pg_statistic_history_stanumbers5 - 1] = isNull; + values[Anum_pg_statistic_history_stavalues1 - 1] = + heap_getattr(tuple, Anum_user_table_stavalues1, tupdesc, &isNull); + nulls[Anum_pg_statistic_history_stavalues1 - 1] = isNull; + values[Anum_pg_statistic_history_stavalues2 - 1] = + heap_getattr(tuple, Anum_user_table_stavalues2, tupdesc, &isNull); + nulls[Anum_pg_statistic_history_stavalues2 - 1] = isNull; + values[Anum_pg_statistic_history_stavalues3 - 1] = + heap_getattr(tuple, Anum_user_table_stavalues3, tupdesc, &isNull); + nulls[Anum_pg_statistic_history_stavalues3 - 1] = isNull; + values[Anum_pg_statistic_history_stavalues4 - 1] = + heap_getattr(tuple, Anum_user_table_stavalues4, tupdesc, &isNull); + nulls[Anum_pg_statistic_history_stavalues4 - 1] = isNull; + values[Anum_pg_statistic_history_stavalues5 - 1] = + heap_getattr(tuple, Anum_user_table_stavalues5, tupdesc, &isNull); + nulls[Anum_pg_statistic_history_stavalues5 - 1] = isNull; + values[Anum_pg_statistic_history_stadndistinct - 1] = + heap_getattr(tuple, Anum_user_table_stadndistinct, tupdesc, &isNull); + nulls[Anum_pg_statistic_history_stadndistinct - 1] = isNull; + values[Anum_pg_statistic_history_staextinfo - 1] = + heap_getattr(tuple, Anum_user_table_staextinfo, tupdesc, &isNull); + nulls[Anum_pg_statistic_history_staextinfo - 1] = isNull; + + ScanKeyData skey[4]; + SysScanDesc sysscan; + bool exists = false; + + ScanKeyInit(&skey[0], Anum_pg_statistic_history_starelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relid)); + ScanKeyInit(&skey[1], Anum_pg_statistic_history_statype, BTEqualStrategyNumber, F_CHAREQ, + CharGetDatum(STATYPE_COLUMN)); + ScanKeyInit(&skey[2], Anum_pg_statistic_history_starelkind, BTEqualStrategyNumber, F_CHAREQ, + CharGetDatum(relkind)); + ScanKeyInit(&skey[3], Anum_pg_statistic_history_staattnum, BTEqualStrategyNumber, F_INT2EQ, + Int16GetDatum(attnum)); + + sysscan = systable_beginscan(rel, StatisticHistoryTabStatypeRelkindAttnumIndexId, true, SnapshotNow, 4, skey); + while (HeapTupleIsValid(tuple = systable_getnext(sysscan))) { + replaces[Anum_pg_statistic_history_stalocktype - 1] = false; + replaces[Anum_pg_statistic_history_reltuples - 1] = false; + replaces[Anum_pg_statistic_history_relpages - 1] = false; + exists = true; + tup = heap_modify_tuple(tuple, RelationGetDescr(rel), values, nulls, replaces); + simple_heap_update(rel, &tup->t_self, tup); + break; + } + + if (!exists) { + tup = heap_form_tuple(RelationGetDescr(rel), values, nulls); + (void)simple_heap_insert(rel, tup); + } + + CatalogUpdateIndexes(rel, tup); + systable_endscan(sysscan); + heap_freetuple_ext(tup); + heap_close(rel, RowExclusiveLock); +} + +static void import_pg_class_stats(Oid relid, HeapTuple tuple, TupleDesc tupdesc) +{ + Datum values[Natts_pg_class]; + bool nulls[Natts_pg_class]; + bool replaces[Natts_pg_class]; + Relation rel = heap_open(RelationRelationId, RowExclusiveLock); + errno_t rc = 0; + HeapTuple tup = NULL; + bool isNull; + + HeapTuple oldtup = SearchSysCache1(RELOID, ObjectIdGetDatum(relid)); + rc = memset_s(values, sizeof(values), 0, sizeof(values)); + securec_check(rc, "\0", "\0"); + rc = memset_s(nulls, sizeof(nulls), false, sizeof(nulls)); + securec_check(rc, "\0", "\0"); + rc = memset_s(replaces, sizeof(replaces), false, sizeof(replaces)); + securec_check(rc, "\0", "\0"); + + values[Anum_pg_class_reltuples - 1] = heap_getattr(tuple, Anum_user_table_reltuples, tupdesc, &isNull); + nulls[Anum_pg_class_reltuples - 1] = isNull; + values[Anum_pg_class_relpages - 1] = heap_getattr(tuple, Anum_user_table_relpages, tupdesc, &isNull); + nulls[Anum_pg_class_relpages - 1] = isNull; + + if (HeapTupleIsValid(oldtup)) { + replaces[Anum_pg_class_reltuples - 1] = true; + replaces[Anum_pg_class_relpages - 1] = true; + tup = heap_modify_tuple(oldtup, RelationGetDescr(rel), values, nulls, replaces); + simple_heap_update(rel, &tup->t_self, tup); + ReleaseSysCache(oldtup); + } else { + tup = heap_form_tuple(RelationGetDescr(rel), values, nulls); + (void)simple_heap_insert(rel, tup); + } + + CatalogUpdateIndexes(rel, tup); + heap_freetuple_ext(tup); + heap_close(rel, RowExclusiveLock); +} + +static void import_pg_partition_stats(Oid partid, HeapTuple tuple, TupleDesc tupdesc) +{ + Datum values[Natts_pg_partition]; + bool nulls[Natts_pg_partition]; + bool replaces[Natts_pg_partition]; + Relation rel = heap_open(PartitionRelationId, RowExclusiveLock); + errno_t rc = 0; + HeapTuple tup = NULL; + bool isNull; + + HeapTuple oldtup = SearchSysCache1(PARTRELID, ObjectIdGetDatum(partid)); + rc = memset_s(values, sizeof(values), 0, sizeof(values)); + securec_check(rc, "\0", "\0"); + rc = memset_s(nulls, sizeof(nulls), false, sizeof(nulls)); + securec_check(rc, "\0", "\0"); + rc = memset_s(replaces, sizeof(replaces), false, sizeof(replaces)); + securec_check(rc, "\0", "\0"); + + values[Anum_pg_partition_reltuples - 1] = heap_getattr(tuple, Anum_user_table_reltuples, tupdesc, &isNull); + nulls[Anum_pg_partition_reltuples - 1] = isNull; + values[Anum_pg_partition_relpages - 1] = heap_getattr(tuple, Anum_user_table_relpages, tupdesc, &isNull); + nulls[Anum_pg_partition_relpages - 1] = isNull; + + if (HeapTupleIsValid(oldtup)) { + replaces[Anum_pg_partition_reltuples - 1] = true; + replaces[Anum_pg_partition_relpages - 1] = true; + tup = heap_modify_tuple(oldtup, RelationGetDescr(rel), values, nulls, replaces); + simple_heap_update(rel, &tup->t_self, tup); + ReleaseSysCache(oldtup); + } else { + tup = heap_form_tuple(RelationGetDescr(rel), values, nulls); + (void)simple_heap_insert(rel, tup); + } + + CatalogUpdateIndexes(rel, tup); + heap_freetuple_ext(tup); + heap_close(rel, RowExclusiveLock); +} + +static void import_stats(HeapTuple tuple, TupleDesc tupdesc, Oid namespaceid, Oid relid, char relkind, int attnum) +{ + import_pg_statistic_stats(tuple, tupdesc, relid, relkind, attnum); + if (t_thrd.proc->workingVersionNum >= STATISTIC_HISTORY_VERSION_NUMBER) { + import_pg_statistic_hisotry_stats(tuple, tupdesc, namespaceid, relid, relkind, attnum); + } else { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Working Version Num less than %u does not support read/write pg_statistic_history systable.", + STATISTIC_HISTORY_VERSION_NUMBER))); + } +} + +static void import_column_stats(Oid namespaceid, Oid relid, Relation starel, TupleDesc tupdesc, char relkind, AttrNumber attnum) +{ + HeapTuple tuple = NULL; + ScanKeyData skey[4]; + TableScanDesc scan; + + ScanKeyInit(&skey[0], Anum_user_table_starelid, BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(relid)); + ScanKeyInit(&skey[1], Anum_user_table_statype, BTEqualStrategyNumber, F_CHAREQ, + CharGetDatum(STATYPE_COLUMN)); + ScanKeyInit(&skey[2], Anum_user_table_starelkind, BTEqualStrategyNumber, F_CHAREQ, + CharGetDatum(relkind)); + ScanKeyInit(&skey[3], Anum_user_table_staattnum, BTEqualStrategyNumber, F_INT2EQ, + Int16GetDatum(attnum)); + + scan = heap_beginscan(starel, SnapshotNow, 4, skey); + while (HeapTupleIsValid(tuple = heap_getnext(scan, ForwardScanDirection))) { + import_stats(tuple, tupdesc, namespaceid, relid, relkind, attnum); + } + + heap_endscan(scan); +} + +static void check_table_is_locked(Oid relid, char* ownname, char* tabname, char* indname, bool force) +{ + SysScanDesc sysscan; + SysScanDesc scan; + ScanKeyData skey[2]; + HeapTuple tuple = NULL; + + ScanKeyInit(&skey[0], Anum_pg_statistic_lock_relid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relid)); + ScanKeyInit(&skey[1], Anum_pg_statistic_lock_stalocktype, BTEqualStrategyNumber, F_CHAREQ, + CharGetDatum(STALOCKTYPE_RELATION)); + + Relation rel = heap_open(StatisticLockRelationId, AccessShareLock); + sysscan = systable_beginscan(rel, StatisticLockRelTypeIndexId, true, SnapshotNow, 2, skey); + while (HeapTupleIsValid(tuple = systable_getnext(sysscan))) { + if (!force) { + if (tabname != NULL) { + ereport(ERROR, (errmsg("Relation \"%s\".\"%s\" has been locked", ownname, tabname))); + } else { + ereport(ERROR, + (errmsg("The table corresponding to index \"%s\".\"%s\" has been locked", ownname, indname))); + } + } + } + + systable_endscan(sysscan); + + ScanKeyInit(&skey[0], Anum_pg_statistic_lock_namespace, BTEqualStrategyNumber, F_TEXTEQ, + CStringGetTextDatum(ownname)); + ScanKeyInit(&skey[1], Anum_pg_statistic_lock_stalocktype, BTEqualStrategyNumber, F_CHAREQ, + CharGetDatum(STALOCKTYPE_SCHEMA)); + + scan = systable_beginscan(rel, StatisticLockNamespacTypeIndexId, true, SnapshotNow, 2, skey); + while (HeapTupleIsValid(tuple = systable_getnext(scan))) { + if (!force) { + ereport(ERROR, (errmsg("Schema \"%s\" has been locked", ownname))); + } + } + + systable_endscan(scan); + heap_close(rel, AccessShareLock); +} + +Datum gs_import_column_stats(PG_FUNCTION_ARGS) +{ + char* ownname = text_to_cstring(PG_GETARG_TEXT_P(0)); + char* tabname = text_to_cstring(PG_GETARG_TEXT_P(1)); + char* colname = text_to_cstring(PG_GETARG_TEXT_P(2)); + char* stattab = text_to_cstring(PG_GETARG_TEXT_P(3)); + char* statown = NULL; + bool force = PG_GETARG_BOOL(5); + + if (!PG_ARGISNULL(4)) { + statown = text_to_cstring(PG_GETARG_TEXT_P(4)); + } else { + statown = pstrdup(ownname); + } + + Oid namespaceid = LookupNamespaceNoError(ownname); + Oid relid = get_relname_relid(tabname, namespaceid); + if (!OidIsValid(relid)) { + ereport(ERROR, (errmsg("Relation \"%s\".\"%s\" is not exists", ownname, tabname))); + } + + if (!pg_class_ownercheck(relid, GetUserId()) && !initialuser()) { + ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("Permission denied. Only dba or table owner can import stats."))); + } + + if (t_thrd.proc->workingVersionNum >= STATISTIC_HISTORY_VERSION_NUMBER) { + check_table_is_locked(relid, ownname, tabname, NULL, force); + } else { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Working Version Num less than %u does not support read/write pg_statistic_lock systable.", + STATISTIC_HISTORY_VERSION_NUMBER))); + } + + HeapTuple tup = SearchSysCache2(ATTNAME, ObjectIdGetDatum(relid), NameGetDatum(colname)); + if (HeapTupleIsValid(tup)) { + Form_pg_attribute attrtup = (Form_pg_attribute)GETSTRUCT(tup); + Oid starelid = get_relname_relid(stattab, LookupNamespaceNoError(statown)); + if (!OidIsValid(starelid)) { + ereport(ERROR, (errmsg("Relation \"%s\".\"%s\" is not exists", statown, stattab))); + } + + Relation starel = heap_open(starelid, AccessShareLock); + TupleDesc tupdesc = RelationGetDescr(starel); + char relkind = check_rel_is_partitioned(relid) ? STARELKIND_PARTITION : STARELKIND_CLASS; + import_column_stats(namespaceid, relid, starel, tupdesc, relkind, attrtup->attnum); + heap_close(starel, AccessShareLock); + ReleaseSysCache(tup); + } else { + ereport(ERROR, (errmsg("Column \"%s\" is not exists in relation %s.%s", colname, ownname, tabname))); + } + + pfree_ext(ownname); + pfree_ext(tabname); + pfree_ext(colname); + pfree_ext(stattab); + pfree_ext(statown); + + PG_RETURN_VOID(); +} + +Datum gs_import_index_stats(PG_FUNCTION_ARGS) +{ + char* ownname = text_to_cstring(PG_GETARG_TEXT_P(0)); + char* indname = text_to_cstring(PG_GETARG_TEXT_P(1)); + char* stattab = text_to_cstring(PG_GETARG_TEXT_P(2)); + char* statown = NULL; + bool force = PG_GETARG_BOOL(4); + + if (!PG_ARGISNULL(3)) { + statown = text_to_cstring(PG_GETARG_TEXT_P(3)); + } else { + statown = pstrdup(ownname); + } + + Oid namespaceid = LookupNamespaceNoError(ownname); + HeapTuple tup = SearchSysCache2(RELNAMENSP, NameGetDatum(indname), ObjectIdGetDatum(namespaceid)); + if (HeapTupleIsValid(tup)) { + Oid indexid = InvalidOid; + bool isNull; + Datum dat; + int2vector indkey; + HeapTuple indexTuple = NULL; + Oid indrelid; + + dat = SysCacheGetAttr(RELNAMENSP, tup, ObjectIdAttributeNumber, &isNull); + if (!isNull) { + indexid = DatumGetObjectId(dat); + } + ReleaseSysCache(tup); + + indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexid)); + if (HeapTupleIsValid(indexTuple)) { + Form_pg_index indextup = (Form_pg_index)GETSTRUCT(indexTuple); + indrelid = indextup->indrelid; + if (!pg_class_ownercheck(indrelid, GetUserId()) && !initialuser()) { + ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("Permission denied. Only dba or table owner can import stats."))); + } + + if (t_thrd.proc->workingVersionNum >= STATISTIC_HISTORY_VERSION_NUMBER) { + check_table_is_locked(indrelid, ownname, NULL, indname, force); + } else { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg( + "Working Version Num less than %u does not support read/write pg_statistic_lock systable.", + STATISTIC_HISTORY_VERSION_NUMBER))); + } + + indkey = indextup->indkey; + Oid starelid = get_relname_relid(stattab, LookupNamespaceNoError(statown)); + if (!OidIsValid(starelid)) { + ereport(ERROR, (errmsg("Relation \"%s\".\"%s\" is not exists", statown, stattab))); + } + + Relation starel = heap_open(starelid, AccessShareLock); + TupleDesc tupdesc = RelationGetDescr(starel); + char relkind = check_rel_is_partitioned(indrelid) ? STARELKIND_PARTITION : STARELKIND_CLASS; + for (int i = 0; i < indkey.dim1; i++) { + import_column_stats(namespaceid, indrelid, starel, tupdesc, relkind, indkey.values[i]); + } + + heap_close(starel, AccessShareLock); + ReleaseSysCache(indexTuple); + } + } else { + ereport(ERROR, (errmsg("Index \"%s\".\"%s\" is not exists", ownname, indname))); + } + + pfree_ext(ownname); + pfree_ext(indname); + pfree_ext(stattab); + pfree_ext(statown); + + PG_RETURN_VOID(); +} + +static void import_subpartition_stats(Relation rel, Oid relid, char* partname, Relation starel, TupleDesc tupdesc) +{ + ScanKeyData skey[3]; + HeapTuple tuple = NULL; + ListCell* lc; + + List* subpartids = RelationGetSubPartitionOidList(rel); + foreach (lc, subpartids) { + Oid subpartid = lfirst_oid(lc); + + ScanKeyInit(&skey[0], Anum_user_table_starelid, BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(relid)); + ScanKeyInit(&skey[1], Anum_user_table_partid, BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(subpartid)); + ScanKeyInit(&skey[2], Anum_user_table_statype, BTEqualStrategyNumber, F_CHAREQ, + CharGetDatum(STATYPE_PARTITION)); + + TableScanDesc scan = heap_beginscan(starel, SnapshotNow, 3, skey); + if (partname == NULL || strcmp(getPartitionName(subpartid, false), partname) == 0) { + while (HeapTupleIsValid(tuple = heap_getnext(scan, ForwardScanDirection))) { + import_pg_partition_stats(subpartid, tuple, tupdesc); + } + } + } +} + +static void import_table_stats(char* ownname, char* tabname, char* partname, char* stattab, bool cascade, + char* statown, bool force) +{ + Oid namespaceid = LookupNamespaceNoError(ownname); + Oid relid = get_relname_relid(tabname, namespaceid); + if (!OidIsValid(relid)) { + ereport(ERROR, (errmsg("Relation \"%s\".\"%s\" is not exists", ownname, tabname))); + } + + if (!pg_class_ownercheck(relid, GetUserId()) && !initialuser()) { + ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("Permission denied. Only dba or table owner can import stats."))); + } + + if (t_thrd.proc->workingVersionNum >= STATISTIC_HISTORY_VERSION_NUMBER) { + check_table_is_locked(relid, ownname, tabname, NULL, force); + } else { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Working Version Num less than %u does not support read/write pg_statistic_lock systable.", + STATISTIC_HISTORY_VERSION_NUMBER))); + } + + Oid starelid = get_relname_relid(stattab, LookupNamespaceNoError(statown)); + if (!OidIsValid(starelid)) { + ereport(ERROR, (errmsg("Relation \"%s\".\"%s\" is not exists", statown, stattab))); + } + + HeapTuple tuple = NULL; + ScanKeyData skey[2]; + + ScanKeyInit(&skey[0], Anum_user_table_starelid, BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(relid)); + ScanKeyInit(&skey[1], Anum_user_table_starelkind, BTEqualStrategyNumber, F_CHAREQ, + CharGetDatum(STATYPE_RELATION)); + + Relation starel = heap_open(starelid, AccessShareLock); + TupleDesc tupdesc = RelationGetDescr(starel); + TableScanDesc scan = heap_beginscan(starel, SnapshotNow, 2, skey); + while (HeapTupleIsValid(tuple = heap_getnext(scan, ForwardScanDirection))) { + import_pg_class_stats(relid, tuple, tupdesc); + } + + heap_endscan(scan); + + Relation rel = heap_open(relid, AccessShareLock); + int attnums = RelationGetNumberOfAttributes(rel); + char relkind = check_rel_is_partitioned(relid) ? STARELKIND_PARTITION : STARELKIND_CLASS; + bool is_index_column[attnums]; + errno_t rc = 0; + + rc = memset_s(is_index_column, sizeof(is_index_column), false, sizeof(is_index_column)); + securec_check(rc, "\0", "\0"); + + List* indexOids = RelationGetIndexList(rel); + ListCell* lc = NULL; + foreach (lc, indexOids) { + Oid indexOid = lfirst_oid(lc); + HeapTuple indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexOid)); + if (HeapTupleIsValid(indexTuple)) { + Form_pg_index indextup = (Form_pg_index)GETSTRUCT(indexTuple); + int2vector indkey = indextup->indkey; + for (int i = 0; i < indkey.dim1; i++) { + is_index_column[indkey.values[i] - 1] = true; + if (cascade) { + import_column_stats(namespaceid, relid, starel, tupdesc, relkind, indkey.values[i]); + } + } + ReleaseSysCache(indexTuple); + } + } + + for (int i = 1; i <= attnums && !is_index_column[i - 1]; i++) { + import_column_stats(namespaceid, relid, starel, tupdesc, relkind, i); + } + + List* partids = relationGetPartitionOidList(rel); + foreach (lc, partids) { + Oid partid = lfirst_oid(lc); + ScanKeyData sskey[3]; + + ScanKeyInit(&sskey[0], Anum_user_table_starelid, BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(relid)); + ScanKeyInit(&sskey[1], Anum_user_table_partid, BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(partid)); + ScanKeyInit(&sskey[2], Anum_user_table_statype, BTEqualStrategyNumber, F_CHAREQ, + CharGetDatum(STATYPE_PARTITION)); + + scan = heap_beginscan(starel, SnapshotNow, 3, sskey); + if (partname == NULL || strcmp(getPartitionName(partid, false), partname) == 0) { + while (HeapTupleIsValid(tuple = heap_getnext(scan, ForwardScanDirection))) { + import_pg_partition_stats(partid, tuple, tupdesc); + } + } + heap_endscan(scan); + + import_subpartition_stats(rel, relid, partname, starel, tupdesc); + } + + heap_close(rel, AccessShareLock); + heap_close(starel, AccessShareLock); + + list_free_ext(indexOids); + list_free_ext(partids); +} + +Datum gs_import_table_stats(PG_FUNCTION_ARGS) +{ + char* ownname = text_to_cstring(PG_GETARG_TEXT_P(0)); + char* tabname = text_to_cstring(PG_GETARG_TEXT_P(1)); + char* partname = NULL; + char* stattab = text_to_cstring(PG_GETARG_TEXT_P(3)); + bool cascade = PG_GETARG_BOOL(4); + char* statown = NULL; + bool force = PG_GETARG_BOOL(6); + + if (!PG_ARGISNULL(2)) { + partname = text_to_cstring(PG_GETARG_TEXT_P(2)); + } + + if (!PG_ARGISNULL(5)) { + statown = text_to_cstring(PG_GETARG_TEXT_P(6)); + } else { + statown = pstrdup(ownname); + } + + import_table_stats(ownname, tabname, partname, stattab, cascade, statown, force); + + pfree_ext(ownname); + pfree_ext(tabname); + pfree_ext(partname); + pfree_ext(stattab); + pfree_ext(statown); + + PG_RETURN_VOID(); +} + +Datum gs_import_schema_stats(PG_FUNCTION_ARGS) +{ + char* ownname = text_to_cstring(PG_GETARG_TEXT_P(0)); + char* stattab = text_to_cstring(PG_GETARG_TEXT_P(1)); + char* statown = NULL; + bool force = PG_GETARG_BOOL(3); + + if (!PG_ARGISNULL(2)) { + statown = text_to_cstring(PG_GETARG_TEXT_P(2)); + } else { + statown = pstrdup(ownname); + } + + List* relnames = GetRelationsInSchema(ownname); + ListCell* lc; + foreach(lc, relnames) { + char* relname = (char*)lfirst(lc); + import_table_stats(ownname, relname, NULL, stattab, true, statown, force); + } + + pfree_ext(ownname); + pfree_ext(stattab); + pfree_ext(statown); + list_free(relnames); + + PG_RETURN_VOID(); +} + +static void delete_pg_statistic_stats(Oid relid, char relkind, int attnum) +{ + Relation rel = heap_open(StatisticRelationId, RowExclusiveLock); + HeapTuple tuple = SearchSysCache4(STATRELKINDATTINH, ObjectIdGetDatum(relid), CharGetDatum(relkind), + Int16GetDatum(attnum), BoolGetDatum(false)); + if (HeapTupleIsValid(tuple)) { + simple_heap_delete(rel, &tuple->t_self); + ReleaseSysCache(tuple); + } + + heap_close(rel, RowExclusiveLock); +} + +static void delete_user_table_column_stats(Oid relid, Oid starelid, char relkind, int attnum) +{ + HeapTuple tuple = NULL; + ScanKeyData skey[4]; + + ScanKeyInit(&skey[0], Anum_user_table_starelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relid)); + ScanKeyInit(&skey[1], Anum_user_table_statype, BTEqualStrategyNumber, F_CHAREQ, CharGetDatum(STATYPE_COLUMN)); + ScanKeyInit(&skey[2], Anum_user_table_starelkind, BTEqualStrategyNumber, F_CHAREQ, CharGetDatum(relkind)); + ScanKeyInit(&skey[3], Anum_user_table_staattnum, BTEqualStrategyNumber, F_INT2EQ, Int16GetDatum(attnum)); + + Relation starel = heap_open(starelid, RowExclusiveLock); + TableScanDesc scan = heap_beginscan(starel, SnapshotNow, 4, skey); + while (HeapTupleIsValid(tuple = heap_getnext(scan, ForwardScanDirection))) { + simple_heap_delete(starel, &tuple->t_self); + } + + heap_endscan(scan); + heap_close(starel, AccessShareLock); +} + +static void delete_column_stats(Oid relid, Oid starelid, char relkind, int attnum) +{ + delete_pg_statistic_stats(relid, relkind, attnum); + if (OidIsValid(starelid)) { + delete_user_table_column_stats(relid, starelid, relkind, attnum); + } +} + +static Oid get_user_table_relid(char* statown, char* stattab, char* statid) +{ + Oid starelid = InvalidOid; + if (stattab) { + starelid = get_relname_relid(stattab, SchemaNameGetSchemaOid(statown, true)); + } else if (statid) { + starelid = atooid(statid); + } + + if (!OidIsValid(starelid) && (stattab || statid)) { + ereport(ERROR, (errmsg("The specified user table does not exist"))); + } + + return starelid; +} + +Datum gs_delete_column_stats(PG_FUNCTION_ARGS) +{ + char* ownname = text_to_cstring(PG_GETARG_TEXT_P(0)); + char* tabname = text_to_cstring(PG_GETARG_TEXT_P(1)); + char* colname = text_to_cstring(PG_GETARG_TEXT_P(2)); + bool force = PG_GETARG_BOOL(6); + char* stattab = NULL; + char* statid = NULL; + char* statown = NULL; + + if (!PG_ARGISNULL(3)) { + stattab = text_to_cstring(PG_GETARG_TEXT_P(3)); + } + + if (!PG_ARGISNULL(4)) { + statid = text_to_cstring(PG_GETARG_TEXT_P(4)); + } + + if (!PG_ARGISNULL(5)) { + statown = text_to_cstring(PG_GETARG_TEXT_P(5)); + } + + Oid namespaceid = LookupNamespaceNoError(ownname); + Oid relid = get_relname_relid(tabname, namespaceid); + if (!OidIsValid(relid)) { + ereport(ERROR, (errmsg("Relation %s.%s is not exists", ownname, tabname))); + } + + if (!pg_class_ownercheck(relid, GetUserId()) && !initialuser()) { + ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("Permission denied. Only dba or table owner can delete stats."))); + } + + if (t_thrd.proc->workingVersionNum >= STATISTIC_HISTORY_VERSION_NUMBER) { + check_table_is_locked(relid, ownname, tabname, NULL, force); + } else { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Working Version Num less than %u does not support read/write pg_statistic_lock systable.", + STATISTIC_HISTORY_VERSION_NUMBER))); + } + + HeapTuple tup = SearchSysCache2(ATTNAME, ObjectIdGetDatum(relid), NameGetDatum(colname)); + if (HeapTupleIsValid(tup)) { + Form_pg_attribute attrtup = (Form_pg_attribute)GETSTRUCT(tup); + Oid starelid = get_user_table_relid(statown, stattab, statid); + char relkind = check_rel_is_partitioned(relid) ? STARELKIND_PARTITION : STARELKIND_CLASS; + delete_column_stats(relid, starelid, relkind, attrtup->attnum); + ReleaseSysCache(tup); + } else { + ereport(ERROR, (errmsg("Column \"%s\" is not exists in relation %s.%s", colname, ownname, tabname))); + } + + pfree_ext(ownname); + pfree_ext(tabname); + pfree_ext(colname); + pfree_ext(stattab); + + PG_RETURN_VOID(); +} + +Datum gs_delete_index_stats(PG_FUNCTION_ARGS) +{ + char* ownname = text_to_cstring(PG_GETARG_TEXT_P(0)); + char* indname = text_to_cstring(PG_GETARG_TEXT_P(1)); + bool force = PG_GETARG_BOOL(5); + char* stattab = NULL; + char* statid = NULL; + char* statown = NULL; + + if (!PG_ARGISNULL(2)) { + stattab = text_to_cstring(PG_GETARG_TEXT_P(2)); + } + + if (!PG_ARGISNULL(3)) { + statid = text_to_cstring(PG_GETARG_TEXT_P(3)); + } + + if (!PG_ARGISNULL(4)) { + statown = text_to_cstring(PG_GETARG_TEXT_P(4)); + } + + Oid namespaceid = LookupNamespaceNoError(ownname); + HeapTuple tup = SearchSysCache2(RELNAMENSP, NameGetDatum(indname), ObjectIdGetDatum(namespaceid)); + if (HeapTupleIsValid(tup)) { + Oid indexid = InvalidOid; + bool isNull; + Datum dat; + int2vector indkey; + HeapTuple indexTuple; + Oid indrelid; + + dat = SysCacheGetAttr(RELNAMENSP, tup, ObjectIdAttributeNumber, &isNull); + if (!isNull) { + indexid = DatumGetObjectId(dat); + } + ReleaseSysCache(tup); + + indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexid)); + if (HeapTupleIsValid(indexTuple)) { + Form_pg_index indextup = (Form_pg_index)GETSTRUCT(indexTuple); + indrelid = indextup->indrelid; + indkey = indextup->indkey; + if (!pg_class_ownercheck(indrelid, GetUserId()) && !initialuser()) { + ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("Permission denied. Only dba or table owner can delete stats."))); + } + + if (t_thrd.proc->workingVersionNum >= STATISTIC_HISTORY_VERSION_NUMBER) { + check_table_is_locked(indrelid, ownname, NULL, indname, force); + } else { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg( + "Working Version Num less than %u does not support read/write pg_statistic_lock systable.", + STATISTIC_HISTORY_VERSION_NUMBER))); + } + + Oid starelid = get_user_table_relid(statown, stattab, statid); + char relkind = check_rel_is_partitioned(indrelid) ? STARELKIND_PARTITION : STARELKIND_CLASS; + for (int i = 0; i < indkey.dim1; i++) { + delete_column_stats(indrelid, starelid, relkind, indkey.values[i]); + } + + ReleaseSysCache(indexTuple); + } + } + + pfree_ext(ownname); + pfree_ext(indname); + pfree_ext(stattab); + + PG_RETURN_VOID(); +} + +static void delete_pg_class_stats(Oid relid) +{ + Datum values[Natts_pg_class]; + bool nulls[Natts_pg_class]; + bool replaces[Natts_pg_class]; + Relation rel = heap_open(RelationRelationId, RowExclusiveLock); + errno_t rc = 0; + HeapTuple tup = NULL; + + HeapTuple tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid)); + rc = memset_s(values, sizeof(values), 0, sizeof(values)); + securec_check(rc, "\0", "\0"); + rc = memset_s(nulls, sizeof(nulls), false, sizeof(nulls)); + securec_check(rc, "\0", "\0"); + rc = memset_s(replaces, sizeof(replaces), false, sizeof(replaces)); + securec_check(rc, "\0", "\0"); + + replaces[Anum_pg_class_reltuples - 1] = true; + values[Anum_pg_class_reltuples - 1] = Float8GetDatum(0); + replaces[Anum_pg_class_relpages - 1] = true; + values[Anum_pg_class_relpages - 1] = Float8GetDatum(0); + + tup = heap_modify_tuple(tuple, RelationGetDescr(rel), values, nulls, replaces); + simple_heap_update(rel, &tup->t_self, tup); + + ReleaseSysCache(tuple); + + CatalogUpdateIndexes(rel, tup); + heap_freetuple_ext(tup); + heap_close(rel, RowExclusiveLock); +} + +static void delete_pg_partition_stats(Oid partid) +{ + Datum values[Natts_pg_partition]; + bool nulls[Natts_pg_partition]; + bool replaces[Natts_pg_partition]; + Relation rel = heap_open(PartitionRelationId, RowExclusiveLock); + errno_t rc = 0; + HeapTuple tup = NULL; + + HeapTuple tuple = SearchSysCache1(PARTRELID, ObjectIdGetDatum(partid)); + rc = memset_s(values, sizeof(values), 0, sizeof(values)); + securec_check(rc, "\0", "\0"); + rc = memset_s(nulls, sizeof(nulls), false, sizeof(nulls)); + securec_check(rc, "\0", "\0"); + rc = memset_s(replaces, sizeof(replaces), false, sizeof(replaces)); + securec_check(rc, "\0", "\0"); + + replaces[Anum_pg_partition_reltuples - 1] = true; + values[Anum_pg_partition_reltuples - 1] = Float8GetDatum(0); + replaces[Anum_pg_partition_reltuples - 1] = true; + values[Anum_pg_partition_reltuples - 1] = Float8GetDatum(0); + + if (HeapTupleIsValid(tuple)) { + tup = heap_modify_tuple(tuple, RelationGetDescr(rel), values, nulls, replaces); + simple_heap_update(rel, &tup->t_self, tup); + ReleaseSysCache(tuple); + + CatalogUpdateIndexes(rel, tup); + } + + heap_freetuple_ext(tup); + heap_close(rel, RowExclusiveLock); +} + +static void delete_user_table_stats(Oid relid, Oid starelid) +{ + HeapTuple tuple = NULL; + ScanKeyData skey[2]; + + ScanKeyInit(&skey[0], Anum_user_table_starelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relid)); + ScanKeyInit(&skey[1], Anum_user_table_statype, BTEqualStrategyNumber, F_CHAREQ, CharGetDatum(STATYPE_RELATION)); + + Relation starel = heap_open(starelid, RowExclusiveLock); + TableScanDesc scan = heap_beginscan(starel, SnapshotNow, 2, skey); + while (HeapTupleIsValid(tuple = heap_getnext(scan, ForwardScanDirection))) { + simple_heap_delete(starel, &tuple->t_self); + } + + heap_endscan(scan); + heap_close(starel, AccessShareLock); +} + +static void delete_user_table_partition_stats(Oid relid, Oid partid, Oid starelid) +{ + HeapTuple tuple = NULL; + ScanKeyData skey[3]; + + ScanKeyInit(&skey[0], Anum_user_table_starelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relid)); + ScanKeyInit(&skey[1], Anum_user_table_partid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(partid)); + ScanKeyInit(&skey[2], Anum_user_table_statype, BTEqualStrategyNumber, F_CHAREQ, CharGetDatum(STATYPE_RELATION)); + + Relation starel = heap_open(starelid, RowExclusiveLock); + TableScanDesc scan = heap_beginscan(starel, SnapshotNow, 3, skey); + while (HeapTupleIsValid(tuple = heap_getnext(scan, ForwardScanDirection))) { + simple_heap_delete(starel, &tuple->t_self); + } + + heap_endscan(scan); + heap_close(starel, AccessShareLock); +} + +static void delete_subpartition_stats(Relation rel, Oid relid, char* partname, Oid starelid) +{ + List* subpartids = RelationGetSubPartitionOidList(rel); + ListCell* lc; + foreach (lc, subpartids) { + Oid subpartid = lfirst_oid(lc); + if (partname == NULL || strcmp(getPartitionName(subpartid, false), partname) == 0) { + delete_pg_partition_stats(subpartid); + if (OidIsValid(starelid)) { + delete_user_table_partition_stats(relid, subpartid, starelid); + } + } + } +} + +static void delete_table_stats(char* ownname, char* tabname, char* partname, char* stattab, char* statid, + bool cascade_parts, bool cascade_columns, bool cascade_indexes, char* statown, bool force) +{ + Oid namespaceid = LookupNamespaceNoError(ownname); + Oid relid = get_relname_relid(tabname, namespaceid); + if (!OidIsValid(relid)) { + ereport(ERROR, (errmsg("relation \"%s\".\"%s\" is not exists", ownname, tabname))); + } + + if (!pg_class_ownercheck(relid, GetUserId()) && !initialuser()) { + ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("Permission denied. Only dba or table owner can delete stats."))); + } + + if (t_thrd.proc->workingVersionNum >= STATISTIC_HISTORY_VERSION_NUMBER) { + check_table_is_locked(relid, ownname, tabname, NULL, force); + } else { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Working Version Num less than %u does not support read/write pg_statistic_lock systable.", + STATISTIC_HISTORY_VERSION_NUMBER))); + } + + delete_pg_class_stats(relid); + Oid starelid = get_user_table_relid(statown, stattab, statid); + if (OidIsValid(starelid)) { + delete_user_table_stats(relid, starelid); + } + + Relation rel = heap_open(relid, AccessShareLock); + int attnums = RelationGetNumberOfAttributes(rel); + char relkind = check_rel_is_partitioned(relid) ? STARELKIND_PARTITION : STARELKIND_CLASS; + bool is_index_column[attnums]; + errno_t rc = 0; + + rc = memset_s(is_index_column, sizeof(is_index_column), false, sizeof(is_index_column)); + securec_check(rc, "\0", "\0"); + + int2vector indkey; + List* indexOids = RelationGetIndexList(rel); + ListCell* lc; + foreach (lc, indexOids) { + Oid indexOid = lfirst_oid(lc); + HeapTuple indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexOid)); + if (HeapTupleIsValid(indexTuple)) { + Form_pg_index indextup = (Form_pg_index)GETSTRUCT(indexTuple); + indkey = indextup->indkey; + for (int i = 0; i < indkey.dim1; i++) { + is_index_column[indkey.values[i] - 1] = true; + if (cascade_indexes) { + delete_column_stats(indexOid, starelid, relkind, indkey.values[i]); + } + } + ReleaseSysCache(indexTuple); + } + } + + if (cascade_columns) { + for (int i = 1; i <= attnums && !is_index_column[i - 1]; i++) { + delete_column_stats(relid, starelid, relkind, i); + } + } + + if (cascade_parts) { + List* partids = relationGetPartitionOidList(rel); + ListCell* lc; + foreach (lc, partids) { + Oid partid = lfirst_oid(lc); + if (partname == NULL || strcmp(getPartitionName(partid, false), partname) == 0) { + delete_pg_partition_stats(partid); + if (OidIsValid(starelid)) { + delete_user_table_partition_stats(relid, partid, starelid); + } + } + + delete_subpartition_stats(rel, relid, partname, starelid); + } + + list_free_ext(partids); + } + + heap_close(rel, AccessShareLock); + list_free_ext(indexOids); +} + +Datum gs_delete_table_stats(PG_FUNCTION_ARGS) +{ + char* ownname = text_to_cstring(PG_GETARG_TEXT_P(0)); + char* tabname = text_to_cstring(PG_GETARG_TEXT_P(1)); + char* partname = NULL; + char* stattab = NULL; + char* statid = NULL; + bool cascade_parts = PG_GETARG_BOOL(5); + bool cascade_columns = PG_GETARG_BOOL(6); + bool cascade_indexes = PG_GETARG_BOOL(7); + char* statown = NULL; + bool force = PG_GETARG_BOOL(9); + + if (!PG_ARGISNULL(2)) { + partname = text_to_cstring(PG_GETARG_TEXT_P(2)); + } + + if (!PG_ARGISNULL(3)) { + stattab = text_to_cstring(PG_GETARG_TEXT_P(3)); + } + + if (!PG_ARGISNULL(4)) { + statid = text_to_cstring(PG_GETARG_TEXT_P(4)); + } + + if (!PG_ARGISNULL(8)) { + statown = text_to_cstring(PG_GETARG_TEXT_P(8)); + } + + delete_table_stats(ownname, tabname, partname, stattab, statid, cascade_parts, cascade_columns, cascade_indexes, + statown, force); + + pfree_ext(ownname); + pfree_ext(tabname); + pfree_ext(partname); + pfree_ext(stattab); + pfree_ext(statid); + pfree_ext(statown); + + PG_RETURN_VOID(); +} + +Datum gs_delete_schema_stats(PG_FUNCTION_ARGS) +{ + char* ownname = text_to_cstring(PG_GETARG_TEXT_P(0)); + bool force = PG_GETARG_BOOL(4); + char* stattab = NULL; + char* statid = NULL; + char* statown = NULL; + + if (!PG_ARGISNULL(1)) { + stattab = text_to_cstring(PG_GETARG_TEXT_P(1)); + } + + if (!PG_ARGISNULL(2)) { + statid = text_to_cstring(PG_GETARG_TEXT_P(2)); + } + + if (!PG_ARGISNULL(3)) { + statown = text_to_cstring(PG_GETARG_TEXT_P(3)); + } + + List* relnames = GetRelationsInSchema(ownname); + ListCell* lc; + foreach(lc, relnames) { + char* relname = (char*)lfirst(lc); + delete_table_stats(ownname, relname, NULL, stattab, statid, true, true, true, statown, force); + } + + pfree_ext(ownname); + pfree_ext(stattab); + pfree_ext(statid); + pfree_ext(statown); + + PG_RETURN_VOID(); +} + +static void set_pg_statistic_stats(Oid relid, char relkind, int2 attnum, Numeric distcnt, Numeric nullcnt, + bool distcnt_is_null, bool nullcnt_is_null) +{ + Relation rel_stats = heap_open(StatisticRelationId, RowExclusiveLock); + HeapTuple tuple = SearchSysCache4(STATRELKINDATTINH, ObjectIdGetDatum(relid), CharGetDatum(relkind), + Int16GetDatum(attnum), BoolGetDatum(false)); + if (HeapTupleIsValid(tuple)) { + HeapTuple copytup = heap_copytuple(tuple); + Form_pg_statistic statstup = (Form_pg_statistic)GETSTRUCT(copytup); + if (!distcnt_is_null) { + statstup->stadistinct = DatumGetFloat4(DirectFunctionCall1(numeric_float4, NumericGetDatum(distcnt))); + } + + if (!nullcnt_is_null) { + Numeric sumcnt = + DatumGetNumeric(DirectFunctionCall2(numeric_add, NumericGetDatum(nullcnt), NumericGetDatum(distcnt))); + Numeric nullfrac = + DatumGetNumeric(DirectFunctionCall2(numeric_mul, NumericGetDatum(nullcnt), NumericGetDatum(sumcnt))); + statstup->stanullfrac = DatumGetFloat4(DirectFunctionCall1(numeric_float4, NumericGetDatum(nullfrac))); + } + + simple_heap_update(rel_stats, ©tup->t_self, copytup); + CatalogUpdateIndexes(rel_stats, copytup); + heap_freetuple_ext(copytup); + ReleaseSysCache(tuple); + } + + heap_close(rel_stats, RowExclusiveLock); +} + +static void set_user_table_column_stats(Oid relid, char relkind, Oid starelid, int2 attnum, Numeric distcnt, + Numeric nullcnt, bool distcnt_is_null, bool nullcnt_is_null) +{ + HeapTuple tuple = NULL; + ScanKeyData skey[4]; + TableScanDesc scan; + + ScanKeyInit(&skey[0], Anum_user_table_starelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relid)); + ScanKeyInit(&skey[1], Anum_user_table_statype, BTEqualStrategyNumber, F_CHAREQ, CharGetDatum(STATYPE_COLUMN)); + ScanKeyInit(&skey[2], Anum_user_table_starelkind, BTEqualStrategyNumber, F_CHAREQ, CharGetDatum(relkind)); + ScanKeyInit(&skey[3], Anum_user_table_staattnum, BTEqualStrategyNumber, F_INT2EQ, Int16GetDatum(attnum)); + + Relation starel = heap_open(starelid, RowExclusiveLock); + scan = heap_beginscan(starel, SnapshotNow, 4, skey); + while (HeapTupleIsValid(tuple = heap_getnext(scan, ForwardScanDirection))) { + Datum values[Natts_pg_statistic]; + bool nulls[Natts_pg_statistic]; + bool replaces[Natts_pg_statistic]; + errno_t rc = 0; + + rc = memset_s(values, sizeof(values), 0, sizeof(values)); + securec_check(rc, "\0", "\0"); + rc = memset_s(nulls, sizeof(nulls), false, sizeof(nulls)); + securec_check(rc, "\0", "\0"); + rc = memset_s(replaces, sizeof(replaces), false, sizeof(replaces)); + securec_check(rc, "\0", "\0"); + + if (!distcnt_is_null) { + replaces[Anum_user_table_stadistinct - 1] = true; + values[Anum_user_table_stadistinct - 1] = DirectFunctionCall1(numeric_float4, NumericGetDatum(distcnt)); + } + + if (!nullcnt_is_null) { + Numeric sumcnt = DatumGetNumeric(DirectFunctionCall2(numeric_add, NumericGetDatum(nullcnt), + NumericGetDatum(distcnt))); + Numeric nullfrac = DatumGetNumeric(DirectFunctionCall2(numeric_mul, NumericGetDatum(nullcnt), + NumericGetDatum(sumcnt))); + replaces[Anum_user_table_stanullfrac - 1] = true; + values[Anum_user_table_stanullfrac - 1] = DirectFunctionCall1(numeric_float4, NumericGetDatum(nullfrac)); + } + + HeapTuple tup = heap_modify_tuple(tuple, RelationGetDescr(starel), values, nulls, replaces); + simple_heap_update(starel, &tup->t_self, tup); + CatalogUpdateIndexes(starel, tup); + } + + heap_endscan(scan); + heap_close(starel, RowExclusiveLock); +} + +static void set_column_stats(Oid relid, Oid starelid, char relkind, int2 attnum, Numeric distcnt, Numeric nullcnt, + bool distcnt_is_null, bool nullcnt_is_null) +{ + set_pg_statistic_stats(relid, relkind, attnum, distcnt, nullcnt, distcnt_is_null, nullcnt_is_null); + if (OidIsValid(starelid)) { + set_user_table_column_stats(relid, relkind, starelid, attnum, distcnt, nullcnt, distcnt_is_null, nullcnt_is_null); + } +} + +Datum gs_set_column_stats(PG_FUNCTION_ARGS) +{ + char* ownname = text_to_cstring(PG_GETARG_TEXT_P(0)); + char* tabname = text_to_cstring(PG_GETARG_TEXT_P(1)); + char* colname = text_to_cstring(PG_GETARG_TEXT_P(2)); + char* stattab = NULL; + char* statid = NULL; + Numeric distcnt; + Numeric nullcnt; + char* statown = NULL; + bool force = false; + bool distcnt_is_null = PG_ARGISNULL(5); + bool nullcnt_is_null = PG_ARGISNULL(6); + + if (!PG_ARGISNULL(3)) { + stattab = text_to_cstring(PG_GETARG_TEXT_P(3)); + } + + if (!PG_ARGISNULL(4)) { + statid = text_to_cstring(PG_GETARG_TEXT_P(4)); + } + + if (!distcnt_is_null) { + distcnt = PG_GETARG_NUMERIC(5); + } + + if (!nullcnt_is_null) { + nullcnt = PG_GETARG_NUMERIC(6); + } + + if (!PG_ARGISNULL(7)) { + statown = text_to_cstring(PG_GETARG_TEXT_P(7)); + } + + Oid namespaceid = LookupNamespaceNoError(ownname); + Oid relid = get_relname_relid(tabname, namespaceid); + if (!OidIsValid(relid)) { + ereport(ERROR, (errmsg("Relation \"%s\".\"%s\" is not exists", ownname, tabname))); + } + + if (!pg_class_ownercheck(relid, GetUserId()) && !initialuser()) { + ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("Permission denied. Only dba or table owner can set stats."))); + } + + if (t_thrd.proc->workingVersionNum >= STATISTIC_HISTORY_VERSION_NUMBER) { + check_table_is_locked(relid, ownname, tabname, NULL, force); + } else { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Working Version Num less than %u does not support read/write pg_statistic_lock systable.", + STATISTIC_HISTORY_VERSION_NUMBER))); + } + + HeapTuple tup = SearchSysCache2(ATTNAME, ObjectIdGetDatum(relid), NameGetDatum(colname)); + if (HeapTupleIsValid(tup)) { + Form_pg_attribute attrtup = (Form_pg_attribute)GETSTRUCT(tup); + Oid starelid = get_user_table_relid(statown, stattab, statid); + char relkind = check_rel_is_partitioned(relid) ? STARELKIND_PARTITION : STARELKIND_CLASS; + if (!distcnt_is_null || !nullcnt_is_null) { + set_column_stats(relid, starelid, relkind, attrtup->attnum, distcnt, nullcnt, distcnt_is_null, nullcnt_is_null); + } + + ReleaseSysCache(tup); + } else { + ereport(ERROR, (errmsg("Column \"%s\" is not exists in relation %s.%s", colname, ownname, tabname))); + } + + pfree_ext(ownname); + pfree_ext(tabname); + pfree_ext(colname); + pfree_ext(stattab); + pfree_ext(statid); + pfree_ext(statown); + + PG_RETURN_VOID(); +} + +Datum gs_set_index_stats(PG_FUNCTION_ARGS) +{ + char* ownname = text_to_cstring(PG_GETARG_TEXT_P(0)); + char* indname = text_to_cstring(PG_GETARG_TEXT_P(1)); + bool force = PG_GETARG_BOOL(6); + char* stattab = NULL; + char* statid = NULL; + Numeric numdist; + char* statown = NULL; + HeapTuple tup = NULL; + bool numdist_is_null = PG_ARGISNULL(4); + + if (!PG_ARGISNULL(2)) { + stattab = text_to_cstring(PG_GETARG_TEXT_P(2)); + } + + if (!PG_ARGISNULL(3)) { + statid = text_to_cstring(PG_GETARG_TEXT_P(3)); + } + + if (!numdist_is_null) { + numdist = PG_GETARG_NUMERIC(4); + } + + if (!PG_ARGISNULL(5)) { + statown = text_to_cstring(PG_GETARG_TEXT_P(5)); + } + + Oid namespaceid = LookupNamespaceNoError(ownname); + tup = SearchSysCache2(RELNAMENSP, NameGetDatum(indname), ObjectIdGetDatum(namespaceid)); + if (HeapTupleIsValid(tup)) { + Oid indexid = InvalidOid; + bool isNull; + int2vector indkey; + HeapTuple indexTuple = NULL; + Oid indrelid = InvalidOid; + + Datum dat = SysCacheGetAttr(RELNAMENSP, tup, ObjectIdAttributeNumber, &isNull); + if (!isNull) { + indexid = DatumGetObjectId(dat); + } + ReleaseSysCache(tup); + + indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexid)); + if (HeapTupleIsValid(indexTuple)) { + Form_pg_index indextup = (Form_pg_index)GETSTRUCT(indexTuple); + indrelid = indextup->indrelid; + if (!pg_class_ownercheck(indrelid, GetUserId()) && !initialuser()) { + ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("Permission denied. Only dba or table owner can delete stats."))); + } + + if (t_thrd.proc->workingVersionNum >= STATISTIC_HISTORY_VERSION_NUMBER) { + check_table_is_locked(indrelid, ownname, NULL, indname, force); + } else { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg( + "Working Version Num less than %u does not support read/write pg_statistic_lock systable.", + STATISTIC_HISTORY_VERSION_NUMBER))); + } + + indkey = indextup->indkey; + Oid starelid = get_user_table_relid(statown, stattab, statid); + char relkind = check_rel_is_partitioned(indrelid) ? STARELKIND_PARTITION : STARELKIND_CLASS; + for (int i = 0; i < indkey.dim1; i++) { + if (!numdist_is_null) { + set_column_stats(indexid, starelid, relkind, indkey.values[i], numdist, 0, false, true); + } + } + + ReleaseSysCache(indexTuple); + } + } else { + ereport(ERROR, (errmsg("Index \"%s\".\"%s\" is not exists", ownname, indname))); + } + + pfree_ext(ownname); + pfree_ext(indname); + pfree_ext(stattab); + pfree_ext(statid); + pfree_ext(statown); + + PG_RETURN_VOID(); +} + +static void set_pg_class_stats(Oid relid, Datum numrows, Datum numblks, bool numrows_is_null, + bool numblks_is_null) +{ + Datum values[Natts_pg_class]; + bool nulls[Natts_pg_class]; + bool replaces[Natts_pg_class]; + Relation rel = heap_open(RelationRelationId, RowExclusiveLock); + errno_t rc = 0; + HeapTuple tup = NULL; + + HeapTuple oldtuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid)); + rc = memset_s(values, sizeof(values), 0, sizeof(values)); + securec_check(rc, "\0", "\0"); + rc = memset_s(nulls, sizeof(nulls), false, sizeof(nulls)); + securec_check(rc, "\0", "\0"); + rc = memset_s(replaces, sizeof(replaces), false, sizeof(replaces)); + securec_check(rc, "\0", "\0"); + + if (!numrows_is_null) { + replaces[Anum_pg_class_reltuples - 1] = true; + values[Anum_pg_class_reltuples - 1] = numrows; + } + + if (!numblks_is_null) { + replaces[Anum_pg_class_relpages - 1] = true; + values[Anum_pg_class_relpages - 1] = numblks; + } + + if (HeapTupleIsValid(oldtuple)) { + tup = heap_modify_tuple(oldtuple, RelationGetDescr(rel), values, nulls, replaces); + simple_heap_update(rel, &tup->t_self, tup); + ReleaseSysCache(oldtuple); + CatalogUpdateIndexes(rel, tup); + } + + heap_freetuple_ext(tup); + heap_close(rel, RowExclusiveLock); +} + +static void set_pg_partition_stats(Oid partid, Datum numrows, Datum numblks, bool numrows_is_null, bool numblks_is_null) +{ + Datum values[Natts_pg_partition]; + bool nulls[Natts_pg_partition]; + bool replaces[Natts_pg_partition]; + Relation rel = heap_open(PartitionRelationId, RowExclusiveLock); + errno_t rc = 0; + HeapTuple tup = NULL; + + HeapTuple oldtuple = SearchSysCache1(PARTRELID, ObjectIdGetDatum(partid)); + rc = memset_s(values, sizeof(values), 0, sizeof(values)); + securec_check(rc, "\0", "\0"); + rc = memset_s(nulls, sizeof(nulls), false, sizeof(nulls)); + securec_check(rc, "\0", "\0"); + rc = memset_s(replaces, sizeof(replaces), false, sizeof(replaces)); + securec_check(rc, "\0", "\0"); + + if (!numrows_is_null) { + replaces[Anum_pg_partition_reltuples - 1] = true; + values[Anum_pg_partition_reltuples - 1] = numrows; + } + + if (!numblks_is_null) { + replaces[Anum_pg_partition_reltuples - 1] = true; + values[Anum_pg_partition_reltuples - 1] = numblks; + } + + if (HeapTupleIsValid(oldtuple)) { + tup = heap_modify_tuple(oldtuple, RelationGetDescr(rel), values, nulls, replaces); + simple_heap_update(rel, &tup->t_self, tup); + ReleaseSysCache(oldtuple); + CatalogUpdateIndexes(rel, tup); + } + + heap_freetuple_ext(tup); + heap_close(rel, RowExclusiveLock); +} + +static void set_user_table_stats(Oid starelid, Oid relid, Datum numrows, Datum numblks, bool numrows_is_null, + bool numblks_is_null) +{ + HeapTuple tuple = NULL; + ScanKeyData skey[2]; + + ScanKeyInit(&skey[0], Anum_user_table_starelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relid)); + ScanKeyInit(&skey[1], Anum_user_table_statype, BTEqualStrategyNumber, F_CHAREQ, CharGetDatum(STATYPE_RELATION)); + + Relation starel = heap_open(starelid, RowExclusiveLock); + TableScanDesc scan = heap_beginscan(starel, SnapshotNow, 2, skey); + TupleDesc tupdesc = RelationGetDescr(starel); + while (HeapTupleIsValid(tuple = heap_getnext(scan, ForwardScanDirection))) { + Datum values[Natts_user_table]; + bool nulls[Natts_user_table]; + bool replaces[Natts_user_table]; + errno_t rc = 0; + + rc = memset_s(values, sizeof(values), 0, sizeof(values)); + securec_check(rc, "\0", "\0"); + rc = memset_s(nulls, sizeof(nulls), false, sizeof(nulls)); + securec_check(rc, "\0", "\0"); + rc = memset_s(replaces, sizeof(replaces), false, sizeof(replaces)); + securec_check(rc, "\0", "\0"); + + if (!numrows_is_null) { + replaces[Anum_user_table_relpages - 1] = true; + values[Anum_user_table_relpages - 1] = numrows; + } + + if (!numblks_is_null) { + replaces[Anum_user_table_reltuples - 1] = true; + values[Anum_user_table_reltuples - 1] = numblks; + } + + HeapTuple tup = heap_modify_tuple(tuple, tupdesc, values, nulls, replaces); + simple_heap_update(starel, &tup->t_self, tup); + + CatalogUpdateIndexes(starel, tup); + } + + heap_endscan(scan); + heap_close(starel, AccessShareLock); +} + +static void set_user_table_partition_stats(Oid starelid, Oid relid, Oid partid, float8 numrows, float8 numblks, + bool numrows_is_null, bool numblks_is_null) +{ + HeapTuple tuple = NULL; + ScanKeyData skey[3]; + + ScanKeyInit(&skey[0], Anum_user_table_starelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relid)); + ScanKeyInit(&skey[1], Anum_user_table_partid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(partid)); + ScanKeyInit(&skey[2], Anum_user_table_statype, BTEqualStrategyNumber, F_CHAREQ, CharGetDatum(STATYPE_PARTITION)); + + Relation starel = heap_open(starelid, RowExclusiveLock); + TableScanDesc scan = heap_beginscan(starel, SnapshotNow, 3, skey); + TupleDesc tupdesc = RelationGetDescr(starel); + while (HeapTupleIsValid(tuple = heap_getnext(scan, ForwardScanDirection))) { + Datum values[Natts_user_table]; + bool nulls[Natts_user_table]; + bool replaces[Natts_user_table]; + errno_t rc = 0; + + rc = memset_s(values, sizeof(values), 0, sizeof(values)); + securec_check(rc, "\0", "\0"); + rc = memset_s(nulls, sizeof(nulls), false, sizeof(nulls)); + securec_check(rc, "\0", "\0"); + rc = memset_s(replaces, sizeof(replaces), false, sizeof(replaces)); + securec_check(rc, "\0", "\0"); + + if (!numrows_is_null) { + replaces[Anum_user_table_relpages - 1] = true; + values[Anum_user_table_relpages - 1] = Float8GetDatum(numrows); + } + + if (!numblks_is_null) { + replaces[Anum_user_table_reltuples - 1] = true; + values[Anum_user_table_reltuples - 1] = Float8GetDatum(numblks); + } + + HeapTuple tup = heap_modify_tuple(tuple, tupdesc, values, nulls, replaces); + simple_heap_update(starel, &tup->t_self, tup); + + CatalogUpdateIndexes(starel, tup); + } + + heap_endscan(scan); + heap_close(starel, AccessShareLock); +} + +static void set_subpartition_stats(Relation rel, Oid relid, char* partname, Oid starelid, Datum numrows, Datum numblks, + bool numrows_is_null, bool numblks_is_null) +{ + List* subpartids = RelationGetSubPartitionOidList(rel); + ListCell* lc; + foreach (lc, subpartids) { + Oid subpartid = lfirst_oid(lc); + if (partname == NULL || strcmp(getPartitionName(subpartid, false), partname) == 0) { + if (!numrows_is_null || !numblks_is_null) { + set_pg_partition_stats(subpartid, numrows, numblks, numrows_is_null, numblks_is_null); + if (OidIsValid(starelid)) { + set_user_table_partition_stats(starelid, relid, subpartid, numrows, numblks, numrows_is_null, + numblks_is_null); + } + } + } + } +} + +Datum gs_set_table_stats(PG_FUNCTION_ARGS) +{ + char* ownname = text_to_cstring(PG_GETARG_TEXT_P(0)); + char* tabname = text_to_cstring(PG_GETARG_TEXT_P(1)); + bool force = PG_GETARG_BOOL(8); + char* partname = NULL; + char* stattab = NULL; + char* statid = NULL; + Datum numrows; + Datum numblks; + char* statown = NULL; + bool numrows_is_null = PG_ARGISNULL(5); + bool numblks_is_null = PG_ARGISNULL(6); + + if (!PG_ARGISNULL(2)) { + partname = text_to_cstring(PG_GETARG_TEXT_P(2)); + } + + if (!PG_ARGISNULL(3)) { + stattab = text_to_cstring(PG_GETARG_TEXT_P(3)); + } + + if (!PG_ARGISNULL(4)) { + statid = text_to_cstring(PG_GETARG_TEXT_P(4)); + } + + if (!numrows_is_null) { + numrows = DirectFunctionCall1(numeric_float8, PG_GETARG_DATUM(5)); + } + + if (!numblks_is_null) { + numblks = DirectFunctionCall1(numeric_float8, PG_GETARG_DATUM(6)); + } + + if (!PG_ARGISNULL(7)) { + statown = text_to_cstring(PG_GETARG_TEXT_P(7)); + } + + Oid namespaceid = LookupNamespaceNoError(ownname); + Oid relid = get_relname_relid(tabname, namespaceid); + if (!OidIsValid(relid)) { + ereport(ERROR, (errmsg("Relation \"%s\".\"%s\" is not exists", ownname, tabname))); + } + + if (!pg_class_ownercheck(relid, GetUserId()) && !initialuser()) { + ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("Permission denied. Only dba or table owner can set stats."))); + } + + if (t_thrd.proc->workingVersionNum >= STATISTIC_HISTORY_VERSION_NUMBER) { + check_table_is_locked(relid, ownname, tabname, NULL, force); + } else { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Working Version Num less than %u does not support read/write pg_statistic_lock systable.", + STATISTIC_HISTORY_VERSION_NUMBER))); + } + + Oid starelid = get_user_table_relid(statown, stattab, statid); + if (!numrows_is_null || !numblks_is_null) { + set_pg_class_stats(relid, numrows, numblks, numrows_is_null, numblks_is_null); + if (OidIsValid(starelid)) { + set_user_table_stats(starelid, relid, numrows, numblks, numrows_is_null, numblks_is_null); + } + } + + Relation rel = heap_open(relid, AccessShareLock); + List* partids = relationGetPartitionOidList(rel); + ListCell* lc = NULL; + foreach (lc, partids) { + Oid partid = lfirst_oid(lc); + if (partname == NULL || strcmp(getPartitionName(partid, false), partname) == 0) { + if (!numrows_is_null || !numblks_is_null) { + set_pg_partition_stats(partid, numrows, numblks, numrows_is_null, numblks_is_null); + if (OidIsValid(starelid)) { + set_user_table_partition_stats(starelid, relid, partid, numrows, numblks, numrows_is_null, + numblks_is_null); + } + } + } + + set_subpartition_stats(rel, relid, partname, starelid, numrows, numblks, numrows_is_null, numblks_is_null); + } + + heap_close(rel, AccessShareLock); + + pfree_ext(ownname); + pfree_ext(tabname); + pfree_ext(partname); + pfree_ext(stattab); + pfree_ext(statid); + pfree_ext(statown); + list_free_ext(partids); + + PG_RETURN_VOID(); +} + +static void restore_pg_class_stats(Form_pg_statistic_history statshistup) +{ + Datum values[Natts_pg_class]; + bool nulls[Natts_pg_class]; + bool replaces[Natts_pg_class]; + Relation rel = heap_open(RelationRelationId, RowExclusiveLock); + errno_t rc = 0; + HeapTuple tup = NULL; + + HeapTuple oldtuple = SearchSysCache1(RELOID, ObjectIdGetDatum(statshistup->starelid)); + rc = memset_s(values, sizeof(values), 0, sizeof(values)); + securec_check(rc, "\0", "\0"); + rc = memset_s(nulls, sizeof(nulls), false, sizeof(nulls)); + securec_check(rc, "\0", "\0"); + rc = memset_s(replaces, sizeof(replaces), false, sizeof(replaces)); + securec_check(rc, "\0", "\0"); + + values[Anum_pg_class_relpages - 1] = Float8GetDatum(statshistup->relpages); + values[Anum_pg_class_reltuples - 1] = Float8GetDatum(statshistup->reltuples); + + if (HeapTupleIsValid(oldtuple)) { + replaces[Anum_pg_class_relpages - 1] = true; + replaces[Anum_pg_class_reltuples - 1] = true; + tup = heap_modify_tuple(oldtuple, RelationGetDescr(rel), values, nulls, replaces); + simple_heap_update(rel, &tup->t_self, tup); + ReleaseSysCache(oldtuple); + } else { + tup = heap_form_tuple(RelationGetDescr(rel), values, nulls); + (void)simple_heap_insert(rel, tup); + } + + CatalogUpdateIndexes(rel, tup); + heap_freetuple_ext(tup); + heap_close(rel, RowExclusiveLock); +} + +static void restore_pg_partition_stats(Form_pg_statistic_history statshistup) +{ + Datum values[Natts_pg_partition]; + bool nulls[Natts_pg_partition]; + bool replaces[Natts_pg_partition]; + Relation rel = heap_open(PartitionRelationId, RowExclusiveLock); + errno_t rc = 0; + HeapTuple tup = NULL; + + HeapTuple oldtuple = SearchSysCache1(PARTRELID, ObjectIdGetDatum(statshistup->partid)); + rc = memset_s(values, sizeof(values), 0, sizeof(values)); + securec_check(rc, "\0", "\0"); + rc = memset_s(nulls, sizeof(nulls), false, sizeof(nulls)); + securec_check(rc, "\0", "\0"); + rc = memset_s(replaces, sizeof(replaces), false, sizeof(replaces)); + securec_check(rc, "\0", "\0"); + + values[Anum_pg_partition_relpages - 1] = Float8GetDatum(statshistup->relpages); + values[Anum_pg_partition_reltuples - 1] = Float8GetDatum(statshistup->reltuples); + + if (HeapTupleIsValid(oldtuple)) { + replaces[Anum_pg_partition_relpages - 1] = true; + replaces[Anum_pg_partition_reltuples - 1] = true; + tup = heap_modify_tuple(oldtuple, RelationGetDescr(rel), values, nulls, replaces); + simple_heap_update(rel, &tup->t_self, tup); + ReleaseSysCache(oldtuple); + } else { + tup = heap_form_tuple(RelationGetDescr(rel), values, nulls); + (void)simple_heap_insert(rel, tup); + } + + CatalogUpdateIndexes(rel, tup); + heap_freetuple_ext(tup); + heap_close(rel, RowExclusiveLock); +} + +static void restore_pg_statistic_stats(Form_pg_statistic_history statshistup, Oid relid, char relkind, int2 attnum, + bool inh) +{ + Datum values[Natts_pg_statistic]; + bool nulls[Natts_pg_statistic]; + Relation rel = heap_open(StatisticRelationId, RowExclusiveLock); + HeapTuple tup = NULL; + errno_t rc = 0; + + rc = memset_s(values, sizeof(values), 0, sizeof(values)); + securec_check(rc, "\0", "\0"); + rc = memset_s(nulls, sizeof(nulls), false, sizeof(nulls)); + securec_check(rc, "\0", "\0"); + + HeapTuple oldtuple = SearchSysCache4(STATRELKINDATTINH, ObjectIdGetDatum(relid), CharGetDatum(relkind), + Int16GetDatum(attnum), BoolGetDatum(inh)); + values[Anum_pg_statistic_starelid - 1] = ObjectIdGetDatum(relid); + values[Anum_pg_statistic_starelkind - 1] = CharGetDatum(relkind); + values[Anum_pg_statistic_staattnum - 1] = Int16GetDatum(attnum); + values[Anum_pg_statistic_stainherit - 1] = BoolGetDatum(inh); + values[Anum_pg_statistic_stanullfrac - 1] = Float4GetDatum(statshistup->stanullfrac); + values[Anum_pg_statistic_stawidth - 1] = Int32GetDatum(statshistup->stawidth); + values[Anum_pg_statistic_stadistinct - 1] = Float4GetDatum(statshistup->stadistinct); + values[Anum_pg_statistic_stakind1 - 1] = Int16GetDatum(statshistup->stakind1); + values[Anum_pg_statistic_stakind2 - 1] = Int16GetDatum(statshistup->stakind2); + values[Anum_pg_statistic_stakind3 - 1] = Int16GetDatum(statshistup->stakind3); + values[Anum_pg_statistic_stakind4 - 1] = Int16GetDatum(statshistup->stakind4); + values[Anum_pg_statistic_stakind5 - 1] = Int16GetDatum(statshistup->stakind5); + values[Anum_pg_statistic_staop1 - 1] = ObjectIdGetDatum(statshistup->staop1); + values[Anum_pg_statistic_staop2 - 1] = ObjectIdGetDatum(statshistup->staop2); + values[Anum_pg_statistic_staop3 - 1] = ObjectIdGetDatum(statshistup->staop3); + values[Anum_pg_statistic_staop4 - 1] = ObjectIdGetDatum(statshistup->staop4); + values[Anum_pg_statistic_staop5 - 1] = ObjectIdGetDatum(statshistup->staop5); + values[Anum_pg_statistic_staextinfo] = 0; + nulls[Anum_pg_statistic_staextinfo] = true; +#ifdef CATALOG_VARLEN + values[Anum_pg_statistic_stanumbers1 - 1] = PointerGetDatum(statshistup->stanumbers1); + values[Anum_pg_statistic_stanumbers2 - 1] = PointerGetDatum(statshistup->stanumbers2); + values[Anum_pg_statistic_stanumbers3 - 1] = PointerGetDatum(statshistup->stanumbers3); + values[Anum_pg_statistic_stanumbers4 - 1] = PointerGetDatum(statshistup->stanumbers4); + values[Anum_pg_statistic_stanumbers5 - 1] = PointerGetDatum(statshistup->stanumbers5); + values[Anum_pg_statistic_stavalues1 - 1] = PointerGetDatum(statshistup->stavalues1); + values[Anum_pg_statistic_stavalues2 - 1] = PointerGetDatum(statshistup->stavalues2); + values[Anum_pg_statistic_stavalues3 - 1] = PointerGetDatum(statshistup->stavalues3); + values[Anum_pg_statistic_stavalues4 - 1] = PointerGetDatum(statshistup->stavalues4); + values[Anum_pg_statistic_stavalues5 - 1] = PointerGetDatum(statshistup->stavalues5); + values[Anum_pg_statistic_stadndistinct - 1] = Float4GetDatum(statshistup->stadndistinct); +#else + for (int i = Anum_pg_statistic_stanumbers1 - 1; i < Anum_pg_statistic_stadndistinct; i++) { + nulls[i] = true; + } +#endif + + if (HeapTupleIsValid(oldtuple)) { + bool replaces[Natts_pg_statistic]; + errno_t rc = memset_s(replaces, sizeof(replaces), true, sizeof(replaces)); + securec_check(rc, "\0", "\0"); + + tup = heap_modify_tuple(oldtuple, RelationGetDescr(rel), values, nulls, replaces); + simple_heap_update(rel, &tup->t_self, tup); + ReleaseSysCache(oldtuple); + } else { + tup = heap_form_tuple(RelationGetDescr(rel), values, nulls); + (void)simple_heap_insert(rel, tup); + } + + CatalogUpdateIndexes(rel, tup); + heap_freetuple_ext(tup); + heap_close(rel, RowExclusiveLock); +} + +static TimestampTz get_recent_timestamp(TimestampTz as_of_timestamp, Oid namespaceid, Oid relid) +{ + SysScanDesc sysscan; + HeapTuple tuple; + ScanKeyData skey[2]; + + ScanKeyInit(&skey[0], Anum_pg_statistic_history_namespaceid, BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(namespaceid)); + ScanKeyInit(&skey[1], Anum_pg_statistic_history_starelid, BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(relid)); + + Relation rel = heap_open(StatisticHistoryRelationId, AccessShareLock); + sysscan = systable_beginscan(rel, StatisticHistoryNamespacTabIndexId, true, SnapshotNow, 2, skey); + TimestampTz offset = as_of_timestamp; + TimestampTz result = as_of_timestamp; + while (HeapTupleIsValid((tuple = systable_getnext(sysscan)))) { + Form_pg_statistic_history statshistup = (Form_pg_statistic_history)GETSTRUCT(tuple); + TimestampTz current_analyzetime = DatumGetTimestampTz(statshistup->current_analyzetime); + if (current_analyzetime == as_of_timestamp) { + result = as_of_timestamp; + ReleaseSysCache(tuple); + break; + } else if (current_analyzetime < as_of_timestamp && as_of_timestamp - current_analyzetime < offset) { + offset = as_of_timestamp - current_analyzetime; + result = current_analyzetime; + } + } + + systable_endscan(sysscan); + heap_close(rel, AccessShareLock); + + return result; +} + +static void restore_table_stats(char* ownname, char* tabname, TimestampTz as_of_timestamp, bool force) +{ + Oid namespaceid = LookupNamespaceNoError(ownname); + Oid relid = get_relname_relid(tabname, namespaceid); + if (!OidIsValid(relid)) { + ereport(ERROR, (errmsg("Relation \"%s\".\"%s\" is not exists", ownname, tabname))); + } + + if (t_thrd.proc->workingVersionNum >= STATISTIC_HISTORY_VERSION_NUMBER) { + check_table_is_locked(relid, ownname, tabname, NULL, force); + } else { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Working Version Num less than %u does not support read/write pg_statistic_lock systable.", + STATISTIC_HISTORY_VERSION_NUMBER))); + } + + ScanKeyData skey[2]; + SysScanDesc scan; + HeapTuple tup = NULL; + + ScanKeyInit(&skey[0], Anum_pg_statistic_history_namespaceid, BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(namespaceid)); + ScanKeyInit(&skey[1], Anum_pg_statistic_history_starelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relid)); + + Relation rel = heap_open(StatisticHistoryRelationId, AccessShareLock); + scan = systable_beginscan(rel, StatisticHistoryNamespacTabIndexId, true, SnapshotNow, 2, skey); + TimestampTz recent_timestamp = get_recent_timestamp(as_of_timestamp, namespaceid, relid); + while (HeapTupleIsValid((tup = systable_getnext(scan)))) { + Form_pg_statistic_history statshistup = (Form_pg_statistic_history)GETSTRUCT(tup); + TimestampTz current_analyzetime = DatumGetTimestampTz(statshistup->current_analyzetime); + if (current_analyzetime == recent_timestamp) { + if (statshistup->statype == STATYPE_RELATION) { + restore_pg_class_stats(statshistup); + } else if (statshistup->statype == STATYPE_COLUMN) { + restore_pg_statistic_stats(statshistup, relid, statshistup->starelkind, statshistup->staattnum, + statshistup->stainherit); + } else { + restore_pg_partition_stats(statshistup); + } + } + } + + systable_endscan(scan); + heap_close(rel, AccessShareLock); +} + +Datum gs_restore_table_stats(PG_FUNCTION_ARGS) +{ + char* ownname = text_to_cstring(PG_GETARG_TEXT_P(0)); + char* tabname = text_to_cstring(PG_GETARG_TEXT_P(1)); + TimestampTz as_of_timestamp = PG_GETARG_TIMESTAMPTZ(2); + bool force = PG_GETARG_BOOL(3); + + restore_table_stats(ownname, tabname, as_of_timestamp, force); + + pfree_ext(ownname); + pfree_ext(tabname); + + PG_RETURN_VOID(); +} + +Datum gs_restore_schema_stats(PG_FUNCTION_ARGS) +{ + char* ownname = text_to_cstring(PG_GETARG_TEXT_P(0)); + TimestampTz as_of_timestamp = PG_GETARG_TIMESTAMPTZ(1); + bool force = PG_GETARG_BOOL(2); + + List* relnames = GetRelationsInSchema(ownname); + ListCell* lc; + foreach(lc, relnames) { + char* relname = (char*)lfirst(lc); + restore_table_stats(ownname, relname, as_of_timestamp, force); + } + + pfree_ext(ownname); + + PG_RETURN_VOID(); +} + +static TimestampTz get_current_analyzetime(Oid namespaceid, Oid starelid) +{ + if (t_thrd.proc->workingVersionNum >= STATISTIC_HISTORY_VERSION_NUMBER) { + SysScanDesc sysscan; + ScanKeyData skey[2]; + HeapTuple tuple = NULL; + + ScanKeyInit(&skey[0], Anum_pg_statistic_history_namespaceid, BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(namespaceid)); + ScanKeyInit(&skey[1], Anum_pg_statistic_history_starelid, BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(starelid)); + + TimestampTz result = 0; + Relation rel = heap_open(StatisticHistoryRelationId, AccessShareLock); + sysscan = systable_beginscan(rel, StatisticHistoryNamespacTabIndexId, true, SnapshotNow, 2, skey); + while (HeapTupleIsValid(tuple = systable_getnext(sysscan))) { + Form_pg_statistic_history statshistup = (Form_pg_statistic_history)GETSTRUCT(tuple); + TimestampTz current_analyzetime = DatumGetTimestampTz(statshistup->current_analyzetime); + if (current_analyzetime > result) { + result = current_analyzetime; + } + } + + systable_endscan(sysscan); + heap_close(rel, AccessShareLock); + + return result; + } else { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Working Version Num less than %u does not support read/write pg_statistic_history systable.", + STATISTIC_HISTORY_VERSION_NUMBER))); + } +} + +static void gather_table_stats(char* ownname, char* tabname, char* partname, char* stattab, char* statid, + char* statown, bool force) +{ + Oid namespaceid = LookupNamespaceNoError(ownname); + Oid relid = get_relname_relid(tabname, namespaceid); + if (!OidIsValid(relid)) { + ereport(ERROR, (errmsg("Relation \"%s\".\"%s\" is not exists", ownname, tabname))); + } + + if (t_thrd.proc->workingVersionNum >= STATISTIC_HISTORY_VERSION_NUMBER) { + check_table_is_locked(relid, ownname, tabname, NULL, force); + } else { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Working Version Num less than %u does not support read/write pg_statistic_lock systable.", + STATISTIC_HISTORY_VERSION_NUMBER))); + } + + StringInfo execute_sql; + VacuumStmt* stmt; + execute_sql = makeStringInfo(); + TimestampTz current_analyzetime; + TimestampTz last_analyzetime; + + Oid starelid = get_user_table_relid(statown, stattab, statid); + current_analyzetime = get_current_analyzetime(namespaceid, starelid); + + if (partname != NULL) { + appendStringInfo(execute_sql, "VACCUM ANALYZE %s.%s;", quote_identifier(ownname), quote_identifier(tabname)); + } else { + appendStringInfo(execute_sql, "ANALYZE %s.%s;", quote_identifier(ownname), quote_identifier(tabname)); + } + + List* parsetree_list = NULL; + ListCell* parsetree_item = NULL; + parsetree_list = raw_parser(execute_sql->data, NULL); + foreach (parsetree_item, parsetree_list) { + Node* parsetree = (Node*)lfirst(parsetree_item); + stmt = (VacuumStmt*)parsetree; + } + vacuum(stmt, InvalidOid, true, NULL, true); + + if (OidIsValid(starelid)) { + last_analyzetime = get_current_analyzetime(namespaceid, starelid); + if (last_analyzetime == current_analyzetime) { + ereport(NOTICE, (errmsg("Data collection succeeded."))); + export_table_stats(ownname, tabname, partname, statown, stattab, true); + restore_table_stats(ownname, tabname, last_analyzetime, force); + } + } + + list_free(parsetree_list); + resetStringInfo(execute_sql); +} + +Datum gs_gather_table_stats(PG_FUNCTION_ARGS) +{ + char* ownname = text_to_cstring(PG_GETARG_TEXT_P(0)); + char* tabname = text_to_cstring(PG_GETARG_TEXT_P(1)); + bool force = PG_GETARG_BOOL(6); + char* partname = NULL; + char* stattab = NULL; + char* statid = NULL; + char* statown = NULL; + + if (!PG_ARGISNULL(2)) { + partname = text_to_cstring(PG_GETARG_TEXT_P(2)); + } + + if (!PG_ARGISNULL(3)) { + stattab = text_to_cstring(PG_GETARG_TEXT_P(3)); + } + + if (!PG_ARGISNULL(4)) { + statid = text_to_cstring(PG_GETARG_TEXT_P(4)); + } + + if (!PG_ARGISNULL(5)) { + statown = text_to_cstring(PG_GETARG_TEXT_P(5)); + } else { + statown = pstrdup(u_sess->attr.attr_common.namespace_current_schema); + } + + gather_table_stats(ownname, tabname, partname, stattab, statid, statown, force); + + pfree_ext(ownname); + pfree_ext(tabname); + pfree_ext(partname); + pfree_ext(stattab); + pfree_ext(statid); + pfree_ext(statown); + + PG_RETURN_VOID(); +} + +Datum gs_gather_index_stats(PG_FUNCTION_ARGS) +{ + char* ownname = text_to_cstring(PG_GETARG_TEXT_P(0)); + char* indname = text_to_cstring(PG_GETARG_TEXT_P(1)); + bool force = PG_GETARG_BOOL(6); + char* partname = NULL; + char* stattab = NULL; + char* statid = NULL; + char* statown = NULL; + + if (!PG_ARGISNULL(2)) { + partname = text_to_cstring(PG_GETARG_TEXT_P(2)); + } + + if (!PG_ARGISNULL(3)) { + stattab = text_to_cstring(PG_GETARG_TEXT_P(3)); + } + + if (!PG_ARGISNULL(4)) { + statid = text_to_cstring(PG_GETARG_TEXT_P(4)); + } + + if (!PG_ARGISNULL(5)) { + statown = text_to_cstring(PG_GETARG_TEXT_P(5)); + } + + Oid namespaceid = LookupNamespaceNoError(ownname); + HeapTuple tup = SearchSysCache2(RELNAMENSP, NameGetDatum(indname), ObjectIdGetDatum(namespaceid)); + if (HeapTupleIsValid(tup)) { + Oid indexid = InvalidOid; + bool isNull; + Datum dat; + HeapTuple indexTuple; + + dat = SysCacheGetAttr(RELNAMENSP, tup, ObjectIdAttributeNumber, &isNull); + if (!isNull) { + indexid = DatumGetObjectId(dat); + } + + indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexid)); + if (HeapTupleIsValid(indexTuple)) { + Form_pg_index indextup = (Form_pg_index)GETSTRUCT(indexTuple); + gather_table_stats(ownname, get_rel_name(indextup->indrelid), partname, stattab, statid, statown, force); + ReleaseSysCache(indexTuple); + } + ReleaseSysCache(tup); + } + + pfree_ext(ownname); + pfree_ext(indname); + pfree_ext(partname); + pfree_ext(stattab); + pfree_ext(statid); + pfree_ext(statown); + + PG_RETURN_VOID(); +} + +Datum gs_gather_database_stats(PG_FUNCTION_ARGS) +{ + if (!pg_database_ownercheck(u_sess->proc_cxt.MyDatabaseId, GetUserId()) && + !superuser() && !initialuser()) { + ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("Permission denied. Only database owner or dba can gather database stats."))); + } + + char* stattab = NULL; + char* statid = NULL; + char* statown = NULL; + bool gather_sys = true; + + if (!PG_ARGISNULL(0)) { + stattab = text_to_cstring(PG_GETARG_TEXT_P(0)); + } + + if (!PG_ARGISNULL(1)) { + statid = text_to_cstring(PG_GETARG_TEXT_P(1)); + } + + if (!PG_ARGISNULL(2)) { + statown = text_to_cstring(PG_GETARG_TEXT_P(2)); + } + + if (!PG_ARGISNULL(3)) { + gather_sys = PG_GETARG_BOOL(3); + } + + TableScanDesc scan; + HeapTuple tuple = NULL; + + Relation rel = heap_open(NamespaceRelationId, AccessShareLock); + scan = heap_beginscan(rel, SnapshotNow, 0, NULL); + while (HeapTupleIsValid(tuple = heap_getnext(scan, ForwardScanDirection))) { + bool isNull; + Datum dat = SysCacheGetAttr(NAMESPACEOID, tuple, ObjectIdAttributeNumber, &isNull); + Oid namespaceid = DatumGetObjectId(dat); + Form_pg_namespace nsptup = (Form_pg_namespace)GETSTRUCT(tuple); + char* ownname = nsptup->nspname.data; + List* relnames_list = GetRelationsInSchema(ownname); + ListCell* lc; + foreach(lc, relnames_list) { + char* relname = (char*)lfirst(lc); + if (!is_sys_table(get_relname_relid(relname, namespaceid)) || gather_sys) { + gather_table_stats(ownname, relname, NULL, stattab, statid, statown, true); + } + } + } + + heap_endscan(scan); + heap_close(rel, AccessShareLock); + + pfree_ext(stattab); + pfree_ext(statid); + pfree_ext(statown); + + PG_RETURN_VOID(); +} + +Datum get_stats_history_availability(PG_FUNCTION_ARGS) +{ + if (t_thrd.proc->workingVersionNum >= STATISTIC_HISTORY_VERSION_NUMBER) { + SysScanDesc sysscan; + HeapTuple tuple = NULL; + ScanKeyData skey[1]; + + ScanKeyInit(&skey[0], Anum_pg_statistic_history_current_analyzetime, BTLessEqualStrategyNumber, F_TIMESTAMP_LE, + TimestampTzGetDatum(GetCurrentTimestamp())); + + TimestampTz result = GetCurrentTimestamp(); + Relation rel = heap_open(StatisticHistoryRelationId, AccessShareLock); + sysscan = systable_beginscan(rel, StatisticHistoryCurrentAnalyzetimeIndexId, true, SnapshotNow, 1, skey); + while (HeapTupleIsValid(tuple = systable_getnext(sysscan))) { + Form_pg_statistic_history statshistup = (Form_pg_statistic_history)GETSTRUCT(tuple); + TimestampTz current_analyzetime = DatumGetTimestampTz(statshistup->current_analyzetime); + if (current_analyzetime < result) { + result = current_analyzetime; + } + } + + systable_endscan(sysscan); + heap_close(rel, AccessShareLock); + + PG_RETURN_TIMESTAMPTZ(result); + } else { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Working Version Num less than %u does not support read/write pg_statistic_history systable.", + STATISTIC_HISTORY_VERSION_NUMBER))); + } +} + +Datum get_stats_history_retention(PG_FUNCTION_ARGS) +{ + PG_RETURN_INT32(u_sess->attr.attr_sql.gms_stats_history_retention); +} + +Datum purge_stats(PG_FUNCTION_ARGS) +{ + if (!superuser() && !initialuser()) { + ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("Permission denied. Only dba or initial user can purge stats."))); + } + + TimestampTz before_timestamp; + TimestampTz retention; + SysScanDesc sysscan; + HeapTuple tuple = NULL; + Relation rel; + ScanKeyData skey[1]; + + if (PG_ARGISNULL(0)) { +#ifdef HAVE_INT64_TIMESTAMP + retention = u_sess->attr.attr_sql.gms_stats_history_retention * USECS_PER_DAY; +#else + retention = u_sess->attr.attr_sql.gms_stats_history_retention * SECS_PER_DAY; +#endif + before_timestamp = GetCurrentTimestamp() - retention; + } else { + before_timestamp = PG_GETARG_TIMESTAMPTZ(0); + } + + if (t_thrd.proc->workingVersionNum >= STATISTIC_HISTORY_VERSION_NUMBER) { + ScanKeyInit(&skey[0], Anum_pg_statistic_history_current_analyzetime, BTLessStrategyNumber, F_TIMESTAMP_LT, + TimestampTzGetDatum(before_timestamp)); + + rel = heap_open(StatisticHistoryRelationId, AccessShareLock); + sysscan = systable_beginscan(rel, StatisticHistoryCurrentAnalyzetimeIndexId, true, SnapshotNow, 1, skey); + while (HeapTupleIsValid(tuple = systable_getnext(sysscan))) { + simple_heap_delete(rel, &tuple->t_self); + } + + systable_endscan(sysscan); + heap_close(rel, AccessShareLock); + } else { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Working Version Num less than %u does not support read/write pg_statistic_history systable.", + STATISTIC_HISTORY_VERSION_NUMBER))); + } + + PG_RETURN_VOID(); +} \ No newline at end of file diff --git a/contrib/gms_stats/gms_stats.h b/contrib/gms_stats/gms_stats.h index 5e4c65eab8640e4458d7b19d515f307813e7f5f2..0f5f9b52c7d5908ef00d1effe185cf5f3e7b8b49 100644 --- a/contrib/gms_stats/gms_stats.h +++ b/contrib/gms_stats/gms_stats.h @@ -12,4 +12,37 @@ #define GMS_STATS_H extern "C" Datum gs_analyze_schema_tables(PG_FUNCTION_ARGS); +extern "C" Datum gs_create_stat_table(PG_FUNCTION_ARGS); +extern "C" Datum gs_drop_stat_table(PG_FUNCTION_ARGS); +extern "C" Datum gs_lock_schema_stats(PG_FUNCTION_ARGS); +extern "C" Datum gs_lock_table_stats(PG_FUNCTION_ARGS); +extern "C" Datum gs_lock_partition_stats(PG_FUNCTION_ARGS); +extern "C" Datum gs_unlock_schema_stats(PG_FUNCTION_ARGS); +extern "C" Datum gs_unlock_table_stats(PG_FUNCTION_ARGS); +extern "C" Datum gs_unlock_partition_stats(PG_FUNCTION_ARGS); +extern "C" Datum gs_export_column_stats(PG_FUNCTION_ARGS); +extern "C" Datum gs_export_index_stats(PG_FUNCTION_ARGS); +extern "C" Datum gs_export_table_stats(PG_FUNCTION_ARGS); +extern "C" Datum gs_export_schema_stats(PG_FUNCTION_ARGS); +extern "C" Datum gs_import_column_stats(PG_FUNCTION_ARGS); +extern "C" Datum gs_import_index_stats(PG_FUNCTION_ARGS); +extern "C" Datum gs_import_table_stats(PG_FUNCTION_ARGS); +extern "C" Datum gs_import_schema_stats(PG_FUNCTION_ARGS); +extern "C" Datum gs_delete_column_stats(PG_FUNCTION_ARGS); +extern "C" Datum gs_delete_index_stats(PG_FUNCTION_ARGS); +extern "C" Datum gs_delete_table_stats(PG_FUNCTION_ARGS); +extern "C" Datum gs_delete_schema_stats(PG_FUNCTION_ARGS); +extern "C" Datum gs_set_column_stats(PG_FUNCTION_ARGS); +extern "C" Datum gs_set_index_stats(PG_FUNCTION_ARGS); +extern "C" Datum gs_set_table_stats(PG_FUNCTION_ARGS); +extern "C" Datum gs_restore_table_stats(PG_FUNCTION_ARGS); +extern "C" Datum gs_restore_schema_stats(PG_FUNCTION_ARGS); +extern "C" Datum gs_gather_table_stats(PG_FUNCTION_ARGS); +extern "C" Datum gs_gather_index_stats(PG_FUNCTION_ARGS); +extern "C" Datum gs_gather_database_stats(PG_FUNCTION_ARGS); +extern "C" Datum get_stats_history_availability(PG_FUNCTION_ARGS); +extern "C" Datum get_stats_history_retention(PG_FUNCTION_ARGS); +extern "C" Datum purge_stats(PG_FUNCTION_ARGS); + +#define atooid(x) ((Oid)strtoul((x), NULL, 10)) #endif diff --git a/contrib/gms_stats/sql/gms_stats.sql b/contrib/gms_stats/sql/gms_stats.sql index 4cc3b86dfb88137229d30887ada25934a1521bce..824e8ecb10a78596639a7c7939b8e8f3a3f4fff7 100644 --- a/contrib/gms_stats/sql/gms_stats.sql +++ b/contrib/gms_stats/sql/gms_stats.sql @@ -24,3 +24,436 @@ call gms_stats.gather_schema_stats('gms_stats_test'); select schemaname, tablename, attname, avg_width, most_common_vals, most_common_freqs from pg_stats where schemaname='gms_stats_test' order by tablename, attname; drop schema gms_stats_test cascade; + +create schema sc_stats; +set current_schema = sc_stats; + +create table t_stats (id int primary key, c2 text); + +insert into t_stats values (generate_series(1, 100), 'aabbcc'); +insert into t_stats values (generate_series(101, 200), '123dfg'); +insert into t_stats values (generate_series(201, 300), '人面桃花相映红'); +insert into t_stats values (generate_series(301, 400), 'fortunate'); +insert into t_stats values (generate_series(401, 500), 'open@gauss'); +insert into t_stats values (generate_series(501, 600), '127.0.0.1'); +insert into t_stats values (generate_series(601, 700), '!@#$!%#!'); +insert into t_stats values (generate_series(701, 800), '[1,2,3,4]'); +insert into t_stats values (generate_series(801, 900), '{"name":"张三","age":18}'); +insert into t_stats values (generate_series(901, 1000), ''); + +create table t_stats2 (id int primary key, c2 text); + +-- 创建用户表 +call gms_stats.create_stat_table('sc_stats', 't_temp_stats'); + +\d sc_stats.t_temp_stats + +-- 收集统计信息 +call gms_stats.gather_database_stats(); + +call gms_stats.lock_table_stats('sc_stats', 't_stats'); + +select namespace, t2.relname as tablename, partname, stalocktype +from pg_statistic_lock t1 +left join pg_class t2 on t1.relid = t2.oid +where namespace = 'sc_stats' and tablename = 't_stats' +order by tablename, stalocktype; + +call gms_stats.gather_table_stats('sc_stats', 't_stats'); +call gms_stats.gather_table_stats('sc_stats', 't_stats', force:=true); + +select schemaname, tablename, attname, avg_width, most_common_vals, most_common_freqs from pg_stats where schemaname='sc_stats' and tablename = 't_stats' order by tablename, attname; + +select t2.nspname as schemaname, t3.relname as tablename, partid, statype, starelkind, staattnum, stalocktype +from pg_statistic_history t1 +left join pg_namespace t2 on t1.namespaceid = t2.oid +left join pg_class t3 on t1.starelid = t3.oid +where schemaname = 'sc_stats' and tablename = 't_stats' +order by tablename, staattnum; + +call gms_stats.unlock_table_stats('sc_stats', 't_stats'); + +select namespace, t2.relname as tablename, partname, stalocktype +from pg_statistic_lock t1 +left join pg_class t2 on t1.relid = t2.oid +order by tablename, stalocktype; + +call gms_stats.gather_table_stats('sc_stats', 't_stats'); +call gms_stats.gather_index_stats('sc_stats', 't_stats_pkey'); +call gms_stats.set_table_stats('sc_stats', 't_stats', numrows:=1010); + +select reltuples, relpages +from pg_class t1 +left join pg_namespace t2 on t1.relnamespace = t2.oid +where t2.nspname = 'sc_stats' and relname = 't_stats'; + +call gms_stats.set_index_stats('sc_stats', 't_stats_pkey', numdist:=1010); +call gms_stats.set_column_stats('sc_stats', 't_stats', 'c2', distcnt:=1010); + +call gms_stats.gather_table_stats('sc_stats', 't_stats'); + +select schemaname, tablename, attname, avg_width, most_common_vals, most_common_freqs from pg_stats where schemaname='sc_stats' and tablename = 't_stats' order by tablename, attname; + +select t2.nspname as schemaname, t3.relname as tablename, partid, statype, starelkind, staattnum, stalocktype, t1.reltuples as reltuples, t1.relpages as relpages +from pg_statistic_history t1 +left join pg_namespace t2 on t1.namespaceid = t2.oid +left join pg_class t3 on t1.starelid = t3.oid +where schemaname = 'sc_stats' and tablename = 't_stats' +order by tablename, staattnum; + +call gms_stats.gather_table_stats('sc_stats', 't_stats2', stattab:='t_temp_stats'); + +select t2.nspname as schemaname, t3.relname as tablename, statype, starelkind, staattnum, stainherit, stanullfrac, stawidth, stadistinct, t1.reltuples as reltuples, t1.relpages as relpages +from sc_stats.t_temp_stats t1 +left join pg_namespace t2 on t1.namespaceid = t2.oid +left join pg_class t3 on t1.starelid = t3.oid +where schemaname = 'sc_stats' and tablename = 't_stats2' +order by tablename, staattnum; + +-- 导出数据 +call gms_stats.lock_schema_stats('sc_stats'); + +select * from pg_statistic_lock where namespace = 'sc_stats' and stalocktype = 's'; + +call gms_stats.export_column_stats('sc_stats', 't_stats2', 'c2', stattab:='t_temp_stats'); + +select t2.nspname as schemaname, t3.relname as tablename, statype, starelkind, staattnum, stainherit, stanullfrac, stawidth, stadistinct, t1.reltuples as reltuples, t1.relpages as relpages +from sc_stats.t_temp_stats t1 +left join pg_namespace t2 on t1.namespaceid = t2.oid +left join pg_class t3 on t1.starelid = t3.oid +where schemaname = 'sc_stats' and tablename = 't_stats2' +order by tablename, staattnum; + +call gms_stats.export_index_stats('sc_stats', 't_stats_pkey', stattab:='t_temp_stats'); + +select t2.nspname as schemaname, t3.relname as tablename, statype, starelkind, staattnum, stainherit, stanullfrac, stawidth, stadistinct, t1.reltuples as reltuples, t1.relpages as relpages +from sc_stats.t_temp_stats t1 +left join pg_namespace t2 on t1.namespaceid = t2.oid +left join pg_class t3 on t1.starelid = t3.oid +where schemaname = 'sc_stats' and tablename = 't_stats' +order by tablename, staattnum; + +call gms_stats.export_table_stats('sc_stats', 't_stats', stattab:='t_temp_stats'); + +select t2.nspname as schemaname, t3.relname as tablename, statype, starelkind, staattnum, stainherit, stanullfrac, stawidth, stadistinct, t1.reltuples as reltuples, t1.relpages as relpages +from sc_stats.t_temp_stats t1 +left join pg_namespace t2 on t1.namespaceid = t2.oid +left join pg_class t3 on t1.starelid = t3.oid +where schemaname = 'sc_stats' and tablename = 't_stats' +order by tablename, staattnum; + +call gms_stats.unlock_schema_stats('sc_stats'); +select * from pg_statistic_lock where namespace = 'sc_stats' and stalocktype = 's'; +call gms_stats.export_column_stats('sc_stats', 't_stats2', 'c2', stattab:='t_temp_stats'); + +select t2.nspname as schemaname, t3.relname as tablename, statype, starelkind, staattnum, stainherit, stanullfrac, stawidth, stadistinct, t1.reltuples as reltuples, t1.relpages as relpages +from sc_stats.t_temp_stats t1 +left join pg_namespace t2 on t1.namespaceid = t2.oid +left join pg_class t3 on t1.starelid = t3.oid +where schemaname = 'sc_stats' and tablename = 't_stats2' +order by tablename, staattnum; + +call gms_stats.export_index_stats('sc_stats', 't_stats_pkey', stattab:='t_temp_stats'); + +select t2.nspname as schemaname, t3.relname as tablename, statype, starelkind, staattnum, stainherit, stanullfrac, stawidth, stadistinct, t1.reltuples as reltuples, t1.relpages as relpages +from sc_stats.t_temp_stats t1 +left join pg_namespace t2 on t1.namespaceid = t2.oid +left join pg_class t3 on t1.starelid = t3.oid +where schemaname = 'sc_stats' and tablename = 't_stats' +order by tablename, staattnum; + +call gms_stats.export_table_stats('sc_stats', 't_stats', stattab:='t_temp_stats'); + +select t2.nspname as schemaname, t3.relname as tablename, statype, starelkind, staattnum, stainherit, stanullfrac, stawidth, stadistinct, t1.reltuples as reltuples, t1.relpages as relpages +from sc_stats.t_temp_stats t1 +left join pg_namespace t2 on t1.namespaceid = t2.oid +left join pg_class t3 on t1.starelid = t3.oid +where schemaname = 'sc_stats' and tablename = 't_stats' +order by tablename, staattnum; + +call gms_stats.export_schema_stats('sc_stats', stattab:='t_temp_stats'); + +select t2.nspname as schemaname, t3.relname as tablename, statype, starelkind, staattnum, stainherit, stanullfrac, stawidth, stadistinct, t1.reltuples as reltuples, t1.relpages as relpages +from sc_stats.t_temp_stats t1 +left join pg_namespace t2 on t1.namespaceid = t2.oid +left join pg_class t3 on t1.starelid = t3.oid +where schemaname = 'sc_stats' +order by tablename, staattnum; + +call gms_stats.gather_table_stats('sc_stats', 't_stats', stattab:='t_temp_stats'); +call gms_stats.gather_table_stats('sc_stats', 't_stats2', stattab:='t_temp_stats'); + +call gms_stats.import_column_stats('sc_stats', 't_stats2', 'c2', stattab:='t_temp_stats'); +call gms_stats.import_index_stats('sc_stats', 't_stats_pkey', stattab:='t_temp_stats'); +call gms_stats.import_table_stats('sc_stats', 't_stats', stattab:='t_temp_stats'); +call gms_stats.import_schema_stats('sc_stats', stattab:='t_temp_stats'); + +select gms_stats.get_stats_history_retention; + +-- 清除恢复 +call gms_stats.purge_stats(to_timestamp('2024-08-26 13:26:15', 'yyyy-MM-dd hh24:mi:ss')); +call gms_stats.restore_table_stats('sc_stats', 't_stats', to_timestamp('2024-08-22 13:26:15', 'yyyy-MM-dd hh24:mi:ss')); +call gms_stats.restore_schema_stats('sc_stats', to_timestamp('2024-08-22 13:26:15', 'yyyy-MM-dd hh24:mi:ss')); + +-- 删除统计信息 +call gms_stats.delete_column_stats('sc_stats', 't_stats', 'c2'); +call gms_stats.delete_index_stats('sc_stats', 't_stats_pkey'); +call gms_stats.delete_table_stats('sc_stats', 't_stats'); +call gms_stats.delete_schema_stats('sc_stats'); + +BEGIN +gms_stats.create_stat_table('sc_stats', 'STAT_TAB', 'pg_default'); +exception +when others then +NULL; +end; +/ + +BEGIN +gms_stats.drop_stat_table('sc_stats', 'STAT_TAB'); +exception +when others then +NULL; +end; +/ + +create table t1(c1 int, c2 date); +BEGIN +gms_stats.delete_column_stats('sc_stats', 't1', 'c1'); +exception +when others then +NULL; +end; +/ + +DECLARE +ownname VARCHAR2(200); +indname VARCHAR2(200); +BEGIN +gms_stats.delete_index_stats( +ownname, +indname); +exception +when others then +NULL; +end; +/ + +BEGIN +gms_stats.DELETE_SCHEMA_STATS('sc_stats'); +END; +/ + +BEGIN +gms_stats.delete_table_stats('sc_stats', 't1'); +exception +when others then +NULL; +end; +/ + +BEGIN +gms_stats.drop_stat_table('sc_stats', 't1'); +exception +when others then +NULL; +end; +/ + +CREATE TABLE example_data ( +id NUMBER, +value NUMBER +); +BEGIN +gms_stats.CREATE_STAT_TABLE('sc_stats','stat_t'); +gms_stats.EXPORT_COLUMN_STATS('sc_stats','example_data','id', NULL,'stat_t'); +END; +/ +DROP TABLE stat_t; +DROP TABLE example_data; + +CREATE TABLE example_data ( +id NUMBER, +value NUMBER +); +CREATE index idx on example_data(id); +BEGIN +gms_stats.CREATE_STAT_TABLE('sc_stats','stat_t'); +gms_stats.EXPORT_INDEX_STATS('sc_stats', 'idx', NULL, 'stat_t'); +END; +/ +DROP INDEX idx; +DROP TABLE stat_t; +DROP TABLE example_data; + +BEGIN +gms_stats.CREATE_STAT_TABLE('sc_stats','stat_t'); +gms_stats.EXPORT_SCHEMA_STATS(stattab => 'stat_t', ownname => 'sc_stats'); +END; +/ +DROP TABLE stat_t; + +BEGIN +gms_stats.export_table_stats('sc_stats', 't_stats', NULL, 'STAT_TAB', NULL, TRUE); +exception +when others then +NULL; +end; +/ + +SELECT gms_stats.GET_STATS_HISTORY_RETENTION; + +CREATE TABLE example_data ( +id NUMBER, +value NUMBER +); +BEGIN +gms_stats.CREATE_STAT_TABLE('sc_stats','stat_t'); +gms_stats.IMPORT_COLUMN_STATS(ownname => 'sc_stats', stattab => 'stat_t', tabname => 'example_data', colname => 'id'); +END; +/ +DROP TABLE stat_t; +DROP TABLE example_data; + +CREATE TABLE example_data ( +id NUMBER, +value NUMBER +); +CREATE index idx on example_data(id); +BEGIN +gms_stats.CREATE_STAT_TABLE('sc_stats','stat_t'); +gms_stats.EXPORT_INDEX_STATS('sc_stats', 'idx', NULL, 'stat_t'); +gms_stats.IMPORT_INDEX_STATS('sc_stats', 'idx', NULL, 'stat_t'); +gms_stats.DROP_STAT_TABLE('sc_stats', 'stat_t'); +END; +/ +DROP INDEX idx; +DROP TABLE example_data; + +BEGIN +gms_stats.CREATE_STAT_TABLE('sc_stats','stat_t'); +gms_stats.EXPORT_SCHEMA_STATS(stattab => 'stat_t', ownname => 'sc_stats'); +gms_stats.IMPORT_SCHEMA_STATS(stattab => 'stat_t', ownname => 'sc_stats'); +gms_stats.DROP_STAT_TABLE('sc_stats', 'stat_t'); +END; +/ + +BEGIN +gms_stats.import_table_stats('public', 'servers', stattab => 'STAT_TAB'); +exception +when others then +NULL; +end; +/ + +CREATE TABLE sales ( +sales_id NUMBER, +sales_date DATE, +amount NUMBER +) +PARTITION BY RANGE (sales_date) ( +PARTITION sales_q1 VALUES LESS THAN (TO_DATE('2022-04-01', 'YYYY-MM-DD')), +PARTITION sales_q2 VALUES LESS THAN (TO_DATE('2022-07-01', 'YYYY-MM-DD')), +PARTITION sales_q3 VALUES LESS THAN (TO_DATE('2022-10-01', 'YYYY-MM-DD')), +PARTITION sales_q4 VALUES LESS THAN (MAXVALUE) +); +BEGIN +gms_stats.LOCK_PARTITION_STATS('sc_stats','sales','sales_q1'); +gms_stats.UNLOCK_PARTITION_STATS('sc_stats','sales','sales_q1'); +END; +/ +DROP TABLE sales; + +BEGIN +gms_stats.LOCK_SCHEMA_STATS('sc_stats'); +gms_stats.UNLOCK_SCHEMA_STATS('sc_stats'); +END; +/ + +CREATE TABLE sales ( +sales_id NUMBER, +sales_date DATE, +amount NUMBER +) +PARTITION BY RANGE (sales_date) ( +PARTITION sales_q1 VALUES LESS THAN (TO_DATE('2022-04-01', 'YYYY-MM-DD')), +PARTITION sales_q2 VALUES LESS THAN (TO_DATE('2022-07-01', 'YYYY-MM-DD')), +PARTITION sales_q3 VALUES LESS THAN (TO_DATE('2022-10-01', 'YYYY-MM-DD')), +PARTITION sales_q4 VALUES LESS THAN (MAXVALUE) +); +BEGIN +gms_stats.LOCK_TABLE_STATS('sc_stats','sales'); +gms_stats.UNLOCK_TABLE_STATS('sc_stats','sales'); +END; +/ +DROP TABLE sales; + +BEGIN +gms_stats.PURGE_STATS (null); +END; +/ + +CREATE TABLE example_data ( +id NUMBER, +value NUMBER +); +CREATE index idx on example_data(id); +BEGIN +gms_stats.SET_INDEX_STATS('sc_stats', 'idx'); +END; +/ +DROP INDEX IDX; +DROP TABLE example_data; + +BEGIN +gms_stats.set_table_stats('sc_stats', 'EMPLOYEES', numrows=> 1000, numblks=>100); +exception +when others then +NULL; +end; +/ + +CREATE TABLE sales ( +sales_id NUMBER, +sales_date DATE, +amount NUMBER +) +PARTITION BY RANGE (sales_date) ( +PARTITION sales_q1 VALUES LESS THAN (TO_DATE('2022-04-01', 'YYYY-MM-DD')), +PARTITION sales_q2 VALUES LESS THAN (TO_DATE('2022-07-01', 'YYYY-MM-DD')), +PARTITION sales_q3 VALUES LESS THAN (TO_DATE('2022-10-01', 'YYYY-MM-DD')), +PARTITION sales_q4 VALUES LESS THAN (MAXVALUE) +); +BEGIN +gms_stats.LOCK_PARTITION_STATS('sc_stats','sales','sales_q1'); +gms_stats.UNLOCK_PARTITION_STATS('sc_stats','sales','sales_q1'); +END; +/ +DROP TABLE sales; + +BEGIN +gms_stats.LOCK_SCHEMA_STATS('sc_stats'); +gms_stats.UNLOCK_SCHEMA_STATS('sc_stats'); +END; +/ + +CREATE TABLE sales ( +sales_id NUMBER, +sales_date DATE, +amount NUMBER +) +PARTITION BY RANGE (sales_date) ( +PARTITION sales_q1 VALUES LESS THAN (TO_DATE('2022-04-01', 'YYYY-MM-DD')), +PARTITION sales_q2 VALUES LESS THAN (TO_DATE('2022-07-01', 'YYYY-MM-DD')), +PARTITION sales_q3 VALUES LESS THAN (TO_DATE('2022-10-01', 'YYYY-MM-DD')), +PARTITION sales_q4 VALUES LESS THAN (MAXVALUE) +); +BEGIN +gms_stats.LOCK_TABLE_STATS('sc_stats','sales'); +gms_stats.UNLOCK_TABLE_STATS('sc_stats','sales'); +END; +/ +DROP TABLE sales; + +reset current_schema; +drop schema sc_stats cascade; \ No newline at end of file diff --git a/src/bin/gs_guc/cluster_guc.conf b/src/bin/gs_guc/cluster_guc.conf index 678d3925066daf434692d67828332efbfb773f9d..cfc76f16d8b97d6d818ef16ff9c6ce28a2db0bfd 100755 --- a/src/bin/gs_guc/cluster_guc.conf +++ b/src/bin/gs_guc/cluster_guc.conf @@ -983,6 +983,7 @@ max_replication_slots|int|0,262143|NULL|NULL| autovacuum_freeze_max_age|int64|100000,576460752303423487|NULL|NULL| autovacuum_max_workers|int|0,262143|NULL|NULL| track_activity_query_size|int|100,102400|NULL|NULL| +gms_stats_history_retention|int|-1,365000|d|NULL| event_source|string|0,0|NULL|NULL| memorypool_enable|bool|0,0|NULL|NULL| enable_memory_limit|bool|0,0|NULL|NULL| diff --git a/src/common/backend/catalog/CMakeLists.txt b/src/common/backend/catalog/CMakeLists.txt index 5b4294537a1876ff5d491df236a125e7a034d7da..5dc78a001749f646ec6c04f312d56e855cfab53e 100755 --- a/src/common/backend/catalog/CMakeLists.txt +++ b/src/common/backend/catalog/CMakeLists.txt @@ -15,7 +15,7 @@ set(POSTGRES_BKI_SRCS_S @pg_object.h @pg_synonym.h @toasting.h @indexing.h @gs_obsscaninfo.h @pg_directory.h @pg_hashbucket.h @gs_global_chain.h @gs_global_config.h @pg_streaming_stream.h @pg_streaming_cont_query.h @pg_streaming_reaper_status.h @gs_matview.h @gs_matview_dependency.h @gs_matview_log.h @pgxc_slice.h @gs_opt_model.h @pg_recyclebin.h @pg_snapshot.h @gs_model.h @gs_dependencies.h @gs_dependencies_obj.h @gs_package.h @gs_job_argument.h @gs_job_attribute.h @pg_uid.h @gs_db_privilege.h -@pg_replication_origin.h @pg_publication.h @pg_publication_rel.h @pg_subscription.h @gs_sql_patch.h @pg_subscription_rel.h @pg_object_type.h @pg_proc_ext.h" +@pg_replication_origin.h @pg_publication.h @pg_publication_rel.h @pg_subscription.h @gs_sql_patch.h @pg_subscription_rel.h @pg_object_type.h @pg_proc_ext.h @pg_statistic_history.h @pg_statistic_lock.h" ) diff --git a/src/common/backend/catalog/Makefile b/src/common/backend/catalog/Makefile index 90462a6e3f6970f9b08a18ab6578ab55d671b02c..be061992366d8257556b44b60a5016bc9d48323e 100644 --- a/src/common/backend/catalog/Makefile +++ b/src/common/backend/catalog/Makefile @@ -62,7 +62,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\ gs_matview_dependency.h gs_matview_log.h pgxc_slice.h gs_opt_model.h gs_dependencies.h gs_dependencies_obj.h gs_package.h gs_model.h\ pg_recyclebin.h pg_snapshot.h gs_job_argument.h gs_job_attribute.h pg_uid.h gs_db_privilege.h\ pg_replication_origin.h pg_publication.h pg_publication_rel.h pg_subscription.h gs_sql_patch.h\ - pg_subscription_rel.h pg_proc_ext.h pg_object_type.h \ + pg_subscription_rel.h pg_proc_ext.h pg_object_type.h pg_statistic_history.h pg_statistic_lock.h\ ) # location of Catalog.pm diff --git a/src/common/backend/utils/cache/relcache.cpp b/src/common/backend/utils/cache/relcache.cpp index 45a7499226b2a35d70ec20089c2dd3e988675446..e933fa5c91257de0737de8dbdd9ecb5d45f1ce2c 100755 --- a/src/common/backend/utils/cache/relcache.cpp +++ b/src/common/backend/utils/cache/relcache.cpp @@ -112,6 +112,8 @@ #include "catalog/pg_shseclabel.h" #include "catalog/pg_statistic.h" #include "catalog/pg_statistic_ext.h" +#include "catalog/pg_statistic_history.h" +#include "catalog/pg_statistic_lock.h" #include "catalog/pg_subscription.h" #include "catalog/pg_synonym.h" #include "catalog/pg_tablespace.h" @@ -355,6 +357,10 @@ static const FormData_pg_attribute Desc_pg_object_type[Natts_pg_object_type] = { static const FormData_pg_attribute Desc_pg_subscription_rel[Natts_pg_subscription_rel] = {Schema_pg_subscription_rel}; static const FormData_pg_attribute Desc_gs_sql_patch_origin[Natts_gs_sql_patch] = {Schema_gs_sql_patch}; static const FormData_pg_attribute Desc_pg_proc_ext[Natts_pg_proc_ext] = {Schema_pg_proc_ext}; +static const FormData_pg_attribute Desc_pg_statistic_history[Natts_pg_statistic_history] = { + Schema_pg_statistic_history +}; +static const FormData_pg_attribute Desc_pg_statistic_lock[Natts_pg_statistic_lock] = {Schema_pg_statistic_lock}; /* Please add to the array in ascending order of oid value */ static struct CatalogRelationBuildParam catalogBuildParam[CATALOG_NUM] = {{DefaultAclRelationId, @@ -868,6 +874,24 @@ static struct CatalogRelationBuildParam catalogBuildParam[CATALOG_NUM] = {{Defau Desc_pg_directory, false, true}, + {StatisticHistoryRelationId, + "pg_statistic_history", + StatisticHistoryRelation_Rowtype_Id, + false, + false, + Natts_pg_statistic_history, + Desc_pg_statistic_history, + false, + true}, + {StatisticLockRelationId, + "pg_statistic_lock", + StatisticLockRelation_Rowtype_Id, + false, + false, + Natts_pg_statistic_lock, + Desc_pg_statistic_lock, + false, + true}, {DbPrivilegeId, "gs_db_privilege", DbPrivilege_Rowtype_Id, diff --git a/src/common/backend/utils/init/globals.cpp b/src/common/backend/utils/init/globals.cpp index a796666474c4754d0985c44df62fa9e37559b395..1641f0aee0c5f82f1515f0fcc6c7523771b7f9b9 100644 --- a/src/common/backend/utils/init/globals.cpp +++ b/src/common/backend/utils/init/globals.cpp @@ -77,12 +77,13 @@ bool will_shutdown = false; * ********************************************/ -const uint32 GRAND_VERSION_NUM = 93025; +const uint32 GRAND_VERSION_NUM = 93026; /******************************************** * 2.VERSION NUM FOR EACH FEATURE * Please write indescending order. ********************************************/ +const uint32 STATISTIC_HISTORY_VERSION_NUMBER = 93026; const uint32 IGNORE_NULLS_VERSION_NUMBER = 93025; const uint32 DATAVEC_VERSION_NUMBER = 93019; const uint32 HTAP_VERSION_NUMBER = 93015; diff --git a/src/common/backend/utils/misc/guc/guc_sql.cpp b/src/common/backend/utils/misc/guc/guc_sql.cpp index f59644deb496b7dfdaba3f3ce5f41f6e35578716..5829a08527609438563b72c31413974578500ade 100755 --- a/src/common/backend/utils/misc/guc/guc_sql.cpp +++ b/src/common/backend/utils/misc/guc/guc_sql.cpp @@ -2551,6 +2551,19 @@ static void InitSqlConfigureNamesInt() NULL, NULL, NULL}, + {{"gms_stats_history_retention", + PGC_SUSET, + NODE_ALL, + STATS, + gettext_noop("Config statistic history data retention days."), + NULL}, + &u_sess->attr.attr_sql.gms_stats_history_retention, + 31, + -1, + 365000, + NULL, + NULL, + NULL}, #ifndef ENABLE_MULTIPLE_NODES {{"pldebugger_timeout", PGC_USERSET, diff --git a/src/gausskernel/optimizer/commands/analyze.cpp b/src/gausskernel/optimizer/commands/analyze.cpp index 2d53d7369b6a3d40935b2dfb64f0f9147c75b18d..f68b9e949c7216a22b8582f466a8992e5add7dbe 100755 --- a/src/gausskernel/optimizer/commands/analyze.cpp +++ b/src/gausskernel/optimizer/commands/analyze.cpp @@ -37,6 +37,7 @@ #include "catalog/pg_namespace.h" #include "catalog/pg_statistic.h" #include "catalog/pg_statistic_ext.h" +#include "catalog/pg_statistic_history.h" #include "catalog/pg_hashbucket_fn.h" #include "catalog/namespace.h" #include "catalog/storage_gtt.h" @@ -4271,6 +4272,149 @@ void releaseSourceAfterDelteOrUpdateAttStats( ResourceOwnerDelete(asOwner); } +static void update_stats_history_catalog(MemoryContext oldcontext, Oid relid, char relkind, bool inh, + VacAttrStats* stats) +{ + HeapTuple stup = NULL; + HeapTuple tuple = NULL; + SysScanDesc sysscan; + int i, k, n; + errno_t rc = 0; + + Datum values[Natts_pg_statistic_history]; + bool nulls[Natts_pg_statistic_history]; + bool replaces[Natts_pg_statistic_history]; + + rc = memset_s(values, sizeof(values), 0, sizeof(values)); + securec_check(rc, "\0", "\0"); + rc = memset_s(nulls, sizeof(nulls), false, sizeof(nulls)); + securec_check(rc, "\0", "\0"); + rc = memset_s(replaces, sizeof(replaces), true, sizeof(replaces)); + securec_check(rc, "\0", "\0"); + + TimestampTz last_analyzetime = 0; + Relation pgstat = heap_open(StatisticHistoryRelationId, RowExclusiveLock); + sysscan = systable_beginscan(pgstat, StatisticHistoryCurrentAnalyzetimeIndexId, true, SnapshotNow, 0, NULL); + while (HeapTupleIsValid(tuple = systable_getnext(sysscan))) { + Form_pg_statistic_history statshistup = (Form_pg_statistic_history)GETSTRUCT(tuple); + TimestampTz time = DatumGetTimestampTz(statshistup->current_analyzetime); + if (time > last_analyzetime) { + last_analyzetime = time; + } + } + systable_endscan(sysscan); + + AttrNumber attnum = stats->attrs[0]->attnum; + values[Anum_pg_statistic_history_namespaceid - 1] = ObjectIdGetDatum(GetNamespaceIdbyRelId(relid)); + values[Anum_pg_statistic_history_starelid - 1] = ObjectIdGetDatum(relid); + values[Anum_pg_statistic_history_statype - 1] = ObjectIdGetDatum(STATYPE_COLUMN); + values[Anum_pg_statistic_history_last_analyzetime - 1] = TimestampTzGetDatum(last_analyzetime); + values[Anum_pg_statistic_history_current_analyzetime - 1] = TimestampTzGetDatum(GetCurrentTimestamp()); + values[Anum_pg_statistic_history_starelkind - 1] = CharGetDatum(relkind); + values[Anum_pg_statistic_history_staattnum - 1] = Int16GetDatum(attnum); + values[Anum_pg_statistic_history_stainherit - 1] = BoolGetDatum(inh); + values[Anum_pg_statistic_history_stanullfrac - 1] = Float4GetDatum(stats->stanullfrac); + values[Anum_pg_statistic_history_stawidth - 1] = Int32GetDatum(stats->stawidth); + values[Anum_pg_statistic_history_stadistinct - 1] = Float4GetDatum(stats->stadistinct); + values[Anum_pg_statistic_history_stadndistinct - 1] = Float4GetDatum(stats->stadndistinct); + + /* For single column statistics we keep staextinfo to NULL */ + values[Anum_pg_statistic_history_staextinfo - 1] = 0; + nulls[Anum_pg_statistic_history_staextinfo - 1] = true; + + /* Process stakind1 -> stakind5 */ + i = Anum_pg_statistic_history_stakind1 - 1; + for (k = 0; k < STATISTIC_NUM_SLOTS; k++) { + values[i++] = Int16GetDatum(stats->stakind[k]); /* stakindN */ + } + + /* Process staop1 -> staop5 */ + i = Anum_pg_statistic_history_staop1 - 1; + for (k = 0; k < STATISTIC_NUM_SLOTS; k++) { + values[i++] = ObjectIdGetDatum(stats->staop[k]); /* staopN */ + } + + /* Process stanumbers1 -> stanumber5 */ + i = Anum_pg_statistic_history_stanumbers1 - 1; + for (k = 0; k < STATISTIC_NUM_SLOTS; k++) { + int nnum = stats->numnumbers[k]; + + if (nnum > 0) { + Datum* numdatums = (Datum*)palloc(nnum * sizeof(Datum)); + ArrayType* arry = NULL; + + for (n = 0; n < nnum; n++) + numdatums[n] = Float4GetDatum(stats->stanumbers[k][n]); + /* XXX knows more than it should about type float4: */ + arry = construct_array(numdatums, nnum, FLOAT4OID, sizeof(float4), FLOAT4PASSBYVAL, 'i'); + values[i++] = PointerGetDatum(arry); /* stanumbersN */ + } else { + nulls[i] = true; + values[i++] = (Datum)0; + } + } + + /* Process stavalues1 -> stavalues5 */ + i = Anum_pg_statistic_history_stavalues1 - 1; + for (k = 0; k < STATISTIC_NUM_SLOTS; k++) { + if (stats->numvalues[k] > 0) { + ArrayType* array = construct_array(stats->stavalues[k], + stats->numvalues[k], + stats->statypid[k], + stats->statyplen[k], + stats->statypbyval[k], + stats->statypalign[k]); + values[i++] = PointerGetDatum(array); /* stavaluesN */ + } else { + nulls[i] = true; + values[i++] = (Datum)0; + } + } + + PG_TRY(); + { + ScanKeyData skey[4]; + SysScanDesc scan; + bool exists = false; + + ScanKeyInit(&skey[0], Anum_pg_statistic_history_starelid, BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(relid)); + ScanKeyInit(&skey[1], Anum_pg_statistic_history_statype, BTEqualStrategyNumber, F_CHAREQ, + CharGetDatum(STATYPE_COLUMN)); + ScanKeyInit(&skey[2], Anum_pg_statistic_history_starelkind, BTEqualStrategyNumber, F_CHAREQ, + CharGetDatum(relkind)); + ScanKeyInit(&skey[3], Anum_pg_statistic_history_staattnum, BTEqualStrategyNumber, F_INT2EQ, + Int16GetDatum(attnum)); + + scan = systable_beginscan(pgstat, StatisticHistoryTabStatypeRelkindAttnumIndexId, true, SnapshotNow, 4, skey); + while (HeapTupleIsValid(tuple = systable_getnext(scan))) { + replaces[Anum_pg_statistic_history_stalocktype - 1] = false; + replaces[Anum_pg_statistic_history_reltuples - 1] = false; + replaces[Anum_pg_statistic_history_relpages - 1] = false; + exists = true; + stup = heap_modify_tuple(tuple, RelationGetDescr(pgstat), values, nulls, replaces); + simple_heap_update(pgstat, &stup->t_self, stup); + break; + } + + if (!exists) { + stup = heap_form_tuple(RelationGetDescr(pgstat), values, nulls); + (void)simple_heap_insert(pgstat, stup); + } + + CatalogUpdateIndexes(pgstat, stup); + systable_endscan(scan); + } + PG_CATCH(); + { + analyze_concurrency_process(relid, attnum, oldcontext, PG_FUNCNAME_MACRO); + } + PG_END_TRY(); + + heap_close(pgstat, RowExclusiveLock); + tableam_tops_free_tuple(stup); +} + /* * update_attstats() -- update attribute statistics for one relation * @@ -4341,6 +4485,10 @@ void update_attstats(Oid relid, char relkind, bool inh, int natts, VacAttrStats* /* do signle column stats update */ update_stats_catalog(pgstat, oldcontext, relid, relkind, inh, stats, natts, relpersistence); + /* do stats history update */ + if (t_thrd.proc->workingVersionNum >= STATISTIC_HISTORY_VERSION_NUMBER) { + update_stats_history_catalog(oldcontext, relid, relkind, inh, stats); + } } } diff --git a/src/gausskernel/optimizer/commands/vacuum.cpp b/src/gausskernel/optimizer/commands/vacuum.cpp index cfa69865bc7c312e5ea0da786b2e5dccef5ea1b1..c0d6b1556875a43a9ffff6326cc36541fcc7252b 100644 --- a/src/gausskernel/optimizer/commands/vacuum.cpp +++ b/src/gausskernel/optimizer/commands/vacuum.cpp @@ -39,6 +39,7 @@ #include "catalog/pg_database.h" #include "catalog/pg_namespace.h" #include "catalog/pg_object.h" +#include "catalog/pg_statistic_history.h" #include "catalog/pgxc_class.h" #include "catalog/storage.h" #include "catalog/storage_gtt.h" @@ -1280,6 +1281,34 @@ static void debug_print_rows_and_pages(Relation relation, Form_pg_class pgcform) pgcform->reltuples, pgcform->relpages)))); } +static void vac_update_class_stats_history(Oid relid, RelPageType num_pages, double num_tuples) +{ + ScanKeyData skey[2]; + SysScanDesc sysscan; + HeapTuple tuple; + Relation rel; + + ScanKeyInit(&skey[0], Anum_pg_statistic_history_starelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relid)); + ScanKeyInit(&skey[1], Anum_pg_statistic_history_statype, BTEqualStrategyNumber, F_CHAREQ, + CharGetDatum(STATYPE_RELATION)); + + rel = heap_open(StatisticHistoryRelationId, RowExclusiveLock); + sysscan = systable_beginscan(rel, StatisticHistoryTabStatypeIndexId, true, SnapshotNow, 2, skey); + while (HeapTupleIsValid(tuple = systable_getnext(sysscan))) { + HeapTuple copytup = heap_copytuple(tuple); + Form_pg_statistic_history statshistup = (Form_pg_statistic_history)GETSTRUCT(copytup); + statshistup->relpages = num_pages; + statshistup->reltuples = num_tuples; + simple_heap_update(rel, ©tup->t_self, copytup); + CatalogUpdateIndexes(rel, copytup); + heap_freetuple(copytup); + break; + } + + systable_endscan(sysscan); + heap_close(rel, RowExclusiveLock); +} + /* * vac_update_relstats() -- update statistics for one relation * @@ -1460,6 +1489,10 @@ void vac_update_relstats(Relation relation, Relation classRel, RelPageType num_p if (nctup) heap_freetuple(nctup); } + + if (t_thrd.proc->workingVersionNum >= STATISTIC_HISTORY_VERSION_NUMBER) { + vac_update_class_stats_history(RelationGetRelid(relation), num_pages, num_tuples); + } } /* @@ -3009,6 +3042,35 @@ bool bypass_lazy_vacuum_index( return bypass; } +static void vac_update_partition_stats_history(Oid relid, Oid partid, BlockNumber num_pages, double num_tuples) +{ + ScanKeyData skey[3]; + SysScanDesc sysscan; + HeapTuple tuple; + Relation rel; + + ScanKeyInit(&skey[0], Anum_pg_statistic_history_starelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relid)); + ScanKeyInit(&skey[1], Anum_pg_statistic_history_partid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(partid)); + ScanKeyInit(&skey[2], Anum_pg_statistic_history_statype, BTEqualStrategyNumber, F_CHAREQ, + CharGetDatum(STATYPE_PARTITION)); + + rel = heap_open(StatisticHistoryRelationId, RowExclusiveLock); + sysscan = systable_beginscan(rel, StatisticHistoryTabPartStatypeIndexId, true, SnapshotNow, 3, skey); + while (HeapTupleIsValid(tuple = systable_getnext(sysscan))) { + HeapTuple copytup = heap_copytuple(tuple); + Form_pg_statistic_history statshistup = (Form_pg_statistic_history)GETSTRUCT(copytup); + statshistup->relpages = num_pages; + statshistup->reltuples = num_tuples; + simple_heap_update(rel, ©tup->t_self, copytup); + CatalogUpdateIndexes(rel, copytup); + heap_freetuple(copytup); + break; + } + + systable_endscan(sysscan); + heap_close(rel, RowExclusiveLock); +} + void vac_update_partstats(Partition part, BlockNumber num_pages, double num_tuples, BlockNumber num_all_visible_pages, TransactionId frozenxid, MultiXactId minmulti) { @@ -3116,6 +3178,10 @@ void vac_update_partstats(Partition part, BlockNumber num_pages, double num_tupl heap_freetuple(nparttup); } heap_close(rd, RowExclusiveLock); + + if (t_thrd.proc->workingVersionNum >= STATISTIC_HISTORY_VERSION_NUMBER) { + vac_update_partition_stats_history(PartitionGetRelid(part), PartitionGetPartid(part), num_pages, num_tuples); + } } static void vac_open_global_indexs( diff --git a/src/gausskernel/process/postmaster/autovacuum.cpp b/src/gausskernel/process/postmaster/autovacuum.cpp index e357a44cd7cc18b502b67d10976b1c5dd4e0ead7..bd445b891eddf0b5836be1c5a0cc51be479d6388 100755 --- a/src/gausskernel/process/postmaster/autovacuum.cpp +++ b/src/gausskernel/process/postmaster/autovacuum.cpp @@ -87,6 +87,8 @@ #include "catalog/namespace.h" #include "catalog/pg_database.h" #include "catalog/pgxc_class.h" +#include "catalog/pg_statistic_history.h" +#include "catalog/pg_statistic_lock.h" #include "commands/dbcommands.h" #include "commands/vacuum.h" #include "commands/matview.h" @@ -1882,6 +1884,67 @@ static void fetch_global_autovac_info() DeleteApplicationNameFromPoolerParams(); } +static void clear_stats_history_timeout_records() +{ + ScanKeyData skey[1]; + SysScanDesc sysscan; + HeapTuple tuple; + TimestampTz retention; + TimestampTz current_timestamp = GetCurrentTimestamp(); + +#ifdef HAVE_INT64_TIMESTAMP + retention = u_sess->attr.attr_sql.gms_stats_history_retention * USECS_PER_DAY; +#else + retention = u_sess->attr.attr_sql.gms_stats_history_retention * SECS_PER_DAY; +#endif + + ScanKeyInit(&skey[0], Anum_pg_statistic_history_current_analyzetime, BTLessStrategyNumber, F_TIMESTAMP_LT, + TimestampTzGetDatum(current_timestamp - retention)); + + Relation rel = heap_open(StatisticHistoryRelationId, RowExclusiveLock); + sysscan = systable_beginscan(rel, StatisticHistoryCurrentAnalyzetimeIndexId, true, SnapshotNow, 1, skey); + while (HeapTupleIsValid(tuple = systable_getnext(sysscan))) { + simple_heap_delete(rel, &tuple->t_self); + } + + systable_endscan(sysscan); + heap_close(rel, RowExclusiveLock); +} + +static bool check_table_is_locked(autovac_table* tab) +{ + SysScanDesc sysscan; + ScanKeyData skey[2]; + HeapTuple tuple = NULL; + + ScanKeyInit(&skey[0], Anum_pg_statistic_lock_relid, BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(tab->at_relid)); + ScanKeyInit(&skey[1], Anum_pg_statistic_lock_stalocktype, BTEqualStrategyNumber, F_CHAREQ, + CharGetDatum(STALOCKTYPE_RELATION)); + + Relation rel = heap_open(StatisticLockRelationId, AccessShareLock); + sysscan = systable_beginscan(rel, StatisticLockRelTypeIndexId, true, SnapshotNow, 2, skey); + while (HeapTupleIsValid(tuple = systable_getnext(sysscan))) { + return true; + } + + systable_endscan(sysscan); + + ScanKeyInit(&skey[0], Anum_pg_statistic_lock_namespace, BTEqualStrategyNumber, F_TEXTEQ, + CStringGetTextDatum(tab->at_nspname)); + ScanKeyInit(&skey[1], Anum_pg_statistic_lock_stalocktype, BTEqualStrategyNumber, F_CHAREQ, + CharGetDatum(STALOCKTYPE_SCHEMA)); + + sysscan = systable_beginscan(rel, StatisticLockNamespacTypeIndexId, true, SnapshotNow, 2, skey); + while (HeapTupleIsValid(tuple = systable_getnext(sysscan))) { + return true; + } + + systable_endscan(sysscan); + heap_close(rel, AccessShareLock); + return false; +} + /* * Process a database table-by-table * @@ -2755,10 +2818,13 @@ static void do_autovacuum(void) enable_sig_alarm(u_sess->attr.attr_storage.autoanalyze_timeout * 1000, true); } - if (local_autovacuum || vacObj->is_internal_relation) - autovacuum_local_vac_analyze(tab, bstrategy); - else - autovacuum_do_vac_analyze(tab, bstrategy); + /* Skip autovacuum as the vacuumed table is locked */ + if (t_thrd.proc->workingVersionNum < STATISTIC_HISTORY_VERSION_NUMBER || !check_table_is_locked(tab)) { + if (local_autovacuum || vacObj->is_internal_relation) + autovacuum_local_vac_analyze(tab, bstrategy); + else + autovacuum_do_vac_analyze(tab, bstrategy); + } if (vacObj->flags & VACFLG_SUB_PARTITION) { // Get partitioned/subpartitioned table's oid @@ -2777,6 +2843,10 @@ static void do_autovacuum(void) /* Cancel any active statement timeout before committing */ disable_sig_alarm(true); + if (t_thrd.proc->workingVersionNum >= STATISTIC_HISTORY_VERSION_NUMBER) { + clear_stats_history_timeout_records(); + } + /* * Clear a possible query-cancel signal, to avoid a late reaction * to an automatically-sent signal because of vacuuming the diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 2f626430cceb4a1fcb96069978e1d2bc90456c52..ef2aa9fa3e7eb6de8ed9c85b29293a77a90dfbcb 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -62,6 +62,6 @@ #define NAILED_IN_CATALOG_NUM 8 -#define CATALOG_NUM 112 +#define CATALOG_NUM 114 #endif diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h index 6cb3ad8bf678e797c6c0273c2090a1dce030a2ca..cecd9e2595a363977aed750fd1a71fe0bf326f16 100644 --- a/src/include/catalog/indexing.h +++ b/src/include/catalog/indexing.h @@ -703,6 +703,24 @@ DECLARE_UNIQUE_INDEX(pg_event_trigger_oid_index, 3487, on pg_event_trigger using DECLARE_UNIQUE_INDEX(pg_proc_ext_proc_oid_index, 3488, on pg_proc_ext using btree(proc_oid oid_ops)); #define ProcExtProcOidIndexId 3488 +DECLARE_INDEX(pg_statistic_history_namespac_tab_index, 4887, on pg_statistic_history using btree(namespaceid oid_ops, starelid oid_ops)); +#define StatisticHistoryNamespacTabIndexId 4887 +DECLARE_INDEX(pg_statistic_history_current_analyzetime_index, 4894, on pg_statistic_history using btree(current_analyzetime timestamptz_ops)); +#define StatisticHistoryCurrentAnalyzetimeIndexId 4894 +DECLARE_INDEX(pg_statistic_history_tab_statype_index, 4902, on pg_statistic_history using btree(starelid oid_ops, statype char_ops)); +#define StatisticHistoryTabStatypeIndexId 4902 +DECLARE_INDEX(pg_statistic_history_tab_part_statype_index, 4903, on pg_statistic_history using btree(starelid oid_ops, partid oid_ops, statype char_ops)); +#define StatisticHistoryTabPartStatypeIndexId 4903 +DECLARE_INDEX(pg_statistic_history_tab_statype_relkind_attnum_index, 4904, on pg_statistic_history using btree(starelid oid_ops, statype char_ops, starelkind char_ops, staattnum int2_ops)); +#define StatisticHistoryTabStatypeRelkindAttnumIndexId 4904 + +DECLARE_INDEX(pg_statistic_lock_namespac_type_index, 4899, on pg_statistic_lock using btree(namespace text_ops, stalocktype char_ops)); +#define StatisticLockNamespacTypeIndexId 4899 +DECLARE_INDEX(pg_statistic_lock_rel_type_index, 4900, on pg_statistic_lock using btree(relid oid_ops, stalocktype char_ops)); +#define StatisticLockRelTypeIndexId 4900 +DECLARE_INDEX(pg_statistic_lock_rel_part_type_index, 4901, on pg_statistic_lock using btree(relid oid_ops, partname text_ops, stalocktype char_ops)); +#define StatisticLockRelPartTypeIndexId 4901 + /* last step of initialization script: build the indexes declared above */ BUILD_INDICES diff --git a/src/include/catalog/pg_statistic_history.h b/src/include/catalog/pg_statistic_history.h new file mode 100644 index 0000000000000000000000000000000000000000..06894162adf050c444a7a7a845a3d91eff0ef840 --- /dev/null +++ b/src/include/catalog/pg_statistic_history.h @@ -0,0 +1,149 @@ +/* ------------------------------------------------------------------------- + * + * pg_statistic_history.h + * definition of the system "statistic" relation (pg_statistic_history) + * along with the relation's initial contents. + * + * + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/catalog/pg_statistic_history.h + * + * NOTES + * the genbki.pl script reads this file and generates .bki + * information from the DATA() statements. + * + * ------------------------------------------------------------------------- + */ +#ifndef PG_STATISTIC_HISTORY_H +#define PG_STATISTIC_HISTORY_H + +#include "catalog/genbki.h" + +#define timestamptz Datum + +/* ---------------- + * pg_statistic_history definition. cpp turns this into + * typedef struct FormData_pg_statistic_history + * ---------------- + */ +#define StatisticHistoryRelationId 4885 +#define StatisticHistoryRelation_Rowtype_Id 4886 + +CATALOG(pg_statistic_history,4885) BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(4886) BKI_SCHEMA_MACRO +{ + /* These fields form the unique key for the entry: */ + Oid namespaceid; + Oid starelid; /* relation containing attribute */ + Oid partid; + char statype; /* 't': statistic of tables + * 'p': statistic of partitions + * 'c': statistic of attributes and indexes + * */ + timestamptz last_analyzetime; + timestamptz current_analyzetime; + char starelkind; + int2 staattnum; + bool stainherit; + float4 stanullfrac; + int4 stawidth; + float4 stadistinct; + float8 reltuples; + float8 relpages; + char stalocktype; + + int2 stakind1; + int2 stakind2; + int2 stakind3; + int2 stakind4; + int2 stakind5; + + Oid staop1; + Oid staop2; + Oid staop3; + Oid staop4; + Oid staop5; +#ifdef CATALOG_VARLEN /* variable-length fields start here */ + float4 stanumbers1[1]; + float4 stanumbers2[1]; + float4 stanumbers3[1]; + float4 stanumbers4[1]; + float4 stanumbers5[1]; + + /* + * Values in these arrays are values of the column's data type, or of some + * related type such as an array element type. We presently have to cheat + * quite a bit to allow polymorphic arrays of this kind, but perhaps + * someday it'll be a less bogus facility. + */ + anyarray stavalues1; + anyarray stavalues2; + anyarray stavalues3; + anyarray stavalues4; + anyarray stavalues5; + float4 stadndistinct; + text statextinfo; +#endif +} FormData_pg_statistic_history; + +#undef timestamptz + +/* ---------------- + * Form_pg_statistic corresponds to a pointer to a tuple with + * the format of pg_statistic_history relation. + * ---------------- + */ +typedef FormData_pg_statistic_history *Form_pg_statistic_history; + +/* ---------------- + * compiler constants for pg_statistic_history + * ---------------- + */ +#define Natts_pg_statistic_history 37 +#define Anum_pg_statistic_history_namespaceid 1 +#define Anum_pg_statistic_history_starelid 2 +#define Anum_pg_statistic_history_partid 3 +#define Anum_pg_statistic_history_statype 4 +#define Anum_pg_statistic_history_last_analyzetime 5 +#define Anum_pg_statistic_history_current_analyzetime 6 +#define Anum_pg_statistic_history_starelkind 7 +#define Anum_pg_statistic_history_staattnum 8 +#define Anum_pg_statistic_history_stainherit 9 +#define Anum_pg_statistic_history_stanullfrac 10 +#define Anum_pg_statistic_history_stawidth 11 +#define Anum_pg_statistic_history_stadistinct 12 +#define Anum_pg_statistic_history_reltuples 13 +#define Anum_pg_statistic_history_relpages 14 +#define Anum_pg_statistic_history_stalocktype 15 +#define Anum_pg_statistic_history_stakind1 16 +#define Anum_pg_statistic_history_stakind2 17 +#define Anum_pg_statistic_history_stakind3 18 +#define Anum_pg_statistic_history_stakind4 19 +#define Anum_pg_statistic_history_stakind5 20 +#define Anum_pg_statistic_history_staop1 21 +#define Anum_pg_statistic_history_staop2 22 +#define Anum_pg_statistic_history_staop3 23 +#define Anum_pg_statistic_history_staop4 24 +#define Anum_pg_statistic_history_staop5 25 +#define Anum_pg_statistic_history_stanumbers1 26 +#define Anum_pg_statistic_history_stanumbers2 27 +#define Anum_pg_statistic_history_stanumbers3 28 +#define Anum_pg_statistic_history_stanumbers4 29 +#define Anum_pg_statistic_history_stanumbers5 30 +#define Anum_pg_statistic_history_stavalues1 31 +#define Anum_pg_statistic_history_stavalues2 32 +#define Anum_pg_statistic_history_stavalues3 33 +#define Anum_pg_statistic_history_stavalues4 34 +#define Anum_pg_statistic_history_stavalues5 35 +#define Anum_pg_statistic_history_stadndistinct 36 +#define Anum_pg_statistic_history_staextinfo 37 + +#define STATYPE_RELATION 't' +#define STATYPE_PARTITION 'p' +#define STATYPE_COLUMN 'c' + +#define STALOCKTYPE_RELATION 't' +#define STALOCKTYPE_SCHEMA 's' +#define STALOCKTYPE_PARTITION 'p' + +#endif /* PG_STATISTIC_HISTORY_H */ \ No newline at end of file diff --git a/src/include/catalog/pg_statistic_lock.h b/src/include/catalog/pg_statistic_lock.h new file mode 100644 index 0000000000000000000000000000000000000000..28c16940288ad64635b6fb8ae8a842fe447495fb --- /dev/null +++ b/src/include/catalog/pg_statistic_lock.h @@ -0,0 +1,60 @@ +/* ------------------------------------------------------------------------- + * + * pg_statistic_lock.h + * definition of the system "statistic_lock" relation (pg_statistic_lock) + * along with the relation's initial contents. + * + * + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/catalog/pg_statistic_lock.h + * + * NOTES + * the genbki.pl script reads this file and generates .bki + * information from the DATA() statements. + * + * ------------------------------------------------------------------------- + */ +#ifndef PG_STATISTIC_LOCK_H +#define PG_STATISTIC_LOCK_H + +#include "catalog/genbki.h" + +/* ---------------- + * pg_statistic_lock definition. cpp turns this into + * typedef struct FormData_pg_statistic_lock + * ---------------- + */ +#define StatisticLockRelationId 4897 +#define StatisticLockRelation_Rowtype_Id 4898 + +CATALOG(pg_statistic_lock,4897) BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(4898) BKI_SCHEMA_MACRO +{ + /* These fields form the unique key for the entry: */ + Oid relid; + char stalocktype; +#ifdef CATALOG_VARLEN /* variable-length fields start here */ + text namespace; + text partname; +#endif + +} FormData_pg_statistic_lock; + +/* ---------------- + * Form_pg_statistic corresponds to a pointer to a tuple with + * the format of pg_statistic_lock relation. + * ---------------- + */ +typedef FormData_pg_statistic_lock *Form_pg_statistic_lock; + +/* ---------------- + * compiler constants for pg_statistic_lock + * ---------------- + */ +#define Natts_pg_statistic_lock 4 +#define Anum_pg_statistic_lock_relid 1 +#define Anum_pg_statistic_lock_stalocktype 2 +#define Anum_pg_statistic_lock_namespace 3 +#define Anum_pg_statistic_lock_partname 4 + +#endif /* PG_STATISTIC_LOCK_H */ \ No newline at end of file diff --git a/src/include/catalog/toasting.h b/src/include/catalog/toasting.h index 8369d6df2ccd6305a58e0f13b8a471e8ae14fd8b..74d4fef15097e1ce4771bc3358eb6ecf3821202b 100644 --- a/src/include/catalog/toasting.h +++ b/src/include/catalog/toasting.h @@ -61,6 +61,7 @@ DECLARE_TOAST(gs_package, 8002, 8003); DECLARE_TOAST(pg_object_type, 8633, 8636); DECLARE_TOAST(gs_global_chain, 5816, 5817); DECLARE_TOAST(gs_model_warehouse, 3995, 3996); +DECLARE_TOAST(pg_statistic_history, 4895, 4896); /* shared catalogs */ DECLARE_TOAST(pg_shdescription, 2846, 2847); diff --git a/src/include/catalog/upgrade_sql/rollback_catalog_maindb/rollback_catalog_maindb_93_026.sql b/src/include/catalog/upgrade_sql/rollback_catalog_maindb/rollback_catalog_maindb_93_026.sql new file mode 100644 index 0000000000000000000000000000000000000000..eb7b469d12643c163cf80409a4ea688edc0112b6 --- /dev/null +++ b/src/include/catalog/upgrade_sql/rollback_catalog_maindb/rollback_catalog_maindb_93_026.sql @@ -0,0 +1,13 @@ +--rollback TABLE +DROP INDEX IF EXISTS pg_statistic_history_namespac_tab_index; +DROP INDEX IF EXISTS pg_statistic_history_current_analyzetime_index; +DROP INDEX IF EXISTS pg_statistic_history_tab_statype_index; +DROP INDEX IF EXISTS pg_statistic_history_tab_part_statype_index; +DROP INDEX IF EXISTS pg_statistic_history_tab_statype_relkind_attnum_index; +DROP TYPE IF EXISTS pg_catalog.pg_statistic_history; +DROP TABLE IF EXISTS pg_catalog.pg_statistic_history; +DROP INDEX IF EXISTS pg_statistic_lock_namespac_type_index; +DROP INDEX IF EXISTS pg_statistic_lock_rel_type_index; +DROP INDEX IF EXISTS pg_statistic_lock_rel_part_type_index; +DROP TYPE IF EXISTS pg_catalog.pg_statistic_lock; +DROP TABLE IF EXISTS pg_catalog.pg_statistic_lock; \ No newline at end of file diff --git a/src/include/catalog/upgrade_sql/rollback_catalog_otherdb/rollback_catalog_otherdb_93_026.sql b/src/include/catalog/upgrade_sql/rollback_catalog_otherdb/rollback_catalog_otherdb_93_026.sql new file mode 100644 index 0000000000000000000000000000000000000000..eb7b469d12643c163cf80409a4ea688edc0112b6 --- /dev/null +++ b/src/include/catalog/upgrade_sql/rollback_catalog_otherdb/rollback_catalog_otherdb_93_026.sql @@ -0,0 +1,13 @@ +--rollback TABLE +DROP INDEX IF EXISTS pg_statistic_history_namespac_tab_index; +DROP INDEX IF EXISTS pg_statistic_history_current_analyzetime_index; +DROP INDEX IF EXISTS pg_statistic_history_tab_statype_index; +DROP INDEX IF EXISTS pg_statistic_history_tab_part_statype_index; +DROP INDEX IF EXISTS pg_statistic_history_tab_statype_relkind_attnum_index; +DROP TYPE IF EXISTS pg_catalog.pg_statistic_history; +DROP TABLE IF EXISTS pg_catalog.pg_statistic_history; +DROP INDEX IF EXISTS pg_statistic_lock_namespac_type_index; +DROP INDEX IF EXISTS pg_statistic_lock_rel_type_index; +DROP INDEX IF EXISTS pg_statistic_lock_rel_part_type_index; +DROP TYPE IF EXISTS pg_catalog.pg_statistic_lock; +DROP TABLE IF EXISTS pg_catalog.pg_statistic_lock; \ No newline at end of file diff --git a/src/include/catalog/upgrade_sql/upgrade_catalog_maindb/upgrade_catalog_maindb_93_026.sql b/src/include/catalog/upgrade_sql/upgrade_catalog_maindb/upgrade_catalog_maindb_93_026.sql new file mode 100644 index 0000000000000000000000000000000000000000..9a3890d1633fad6bda80e876bd707ba7c9beb8c6 --- /dev/null +++ b/src/include/catalog/upgrade_sql/upgrade_catalog_maindb/upgrade_catalog_maindb_93_026.sql @@ -0,0 +1,79 @@ +--upgrade TABLE +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_CATALOG, false, true, 4885, 4886, 0, 0; +CREATE TABLE pg_catalog.pg_statistic_history ( + namespaceid oid, + starelid oid, + partid oid, + statype "char", + last_analyzetime timestamp with time zone, + current_analyzetime timestamp with time zone not null, + starelkind "char", + staattnum smallint, + stainherit boolean, + stanullfrac real, + stawidth int, + stadistinct real, + reltuples double precision, + relpages double precision, + stalocktype "char", + stakind1 smallint, + stakind2 smallint, + stakind3 smallint, + stakind4 smallint, + stakind5 smallint, + staop1 oid, + staop2 oid, + staop3 oid, + staop4 oid, + staop5 oid, + stanumbers1 real[], + stanumbers2 real[], + stanumbers3 real[], + stanumbers4 real[], + stanumbers5 real[], + stavalues1 anyarray, + stavalues2 anyarray, + stavalues3 anyarray, + stavalues4 anyarray, + stavalues5 anyarray, + stadndistinct real, + staextinfo text +) WITH(oids=false) TABLESPACE pg_default; + +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_CATALOG, false, true, 0, 0, 0, 4887; +CREATE INDEX pg_catalog.pg_statistic_history_namespac_tab_index ON pg_statistic_history USING btree (namespaceid oid_ops, starelid oid_ops); + +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_CATALOG, false, true, 0, 0, 0, 4894; +CREATE INDEX pg_catalog.pg_statistic_history_current_analyzetime_index ON pg_statistic_history USING btree (current_analyzetime timestamptz_ops); + +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_CATALOG, false, true, 0, 0, 0, 4902; +CREATE INDEX pg_catalog.pg_statistic_history_tab_statype_index ON pg_statistic_history USING btree (starelid oid_ops, statype char_ops); + +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_CATALOG, false, true, 0, 0, 0, 4903; +CREATE INDEX pg_catalog.pg_statistic_history_tab_part_statype_index ON pg_statistic_history USING btree (starelid oid_ops, partid oid_ops, statype char_ops); + +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_CATALOG, false, true, 0, 0, 0, 4904; +CREATE INDEX pg_catalog.pg_statistic_history_tab_statype_relkind_attnum_index ON pg_statistic_history USING btree (starelid oid_ops, statype char_ops, starelkind char_ops, staattnum int2_ops); + +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_CATALOG, false, true, 0, 0, 0, 0; +GRANT SELECT ON pg_catalog.pg_statistic_history TO PUBLIC; + +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_CATALOG, false, true, 4897, 4898, 0, 0; +CREATE TABLE pg_catalog.pg_statistic_lock ( + namespace text, + relid oid, + partname text, + stalocktype "char" not null +) WITH(oids=false) TABLESPACE pg_default; + +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_CATALOG, false, true, 0, 0, 0, 4899; +CREATE INDEX pg_catalog.pg_statistic_lock_namespac_type_index ON pg_statistic_lock USING btree (namespace text_ops, stalocktype char_ops); + +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_CATALOG, false, true, 0, 0, 0, 4900; +CREATE UNIQUE INDEX pg_catalog.pg_statistic_lock_rel_type_index ON pg_statistic_lock USING btree (relid oid_ops, stalocktype char_ops); + +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_CATALOG, false, true, 0, 0, 0, 4901; +CREATE UNIQUE INDEX pg_catalog.pg_statistic_lock_rel_part_type_index ON pg_statistic_lock USING btree (relid oid_ops, partname text_ops, stalocktype char_ops); + +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_CATALOG, false, true, 0, 0, 0, 0; +GRANT SELECT ON pg_catalog.pg_statistic_lock TO PUBLIC; \ No newline at end of file diff --git a/src/include/catalog/upgrade_sql/upgrade_catalog_otherdb/upgrade_catalog_otherdb_93_026.sql b/src/include/catalog/upgrade_sql/upgrade_catalog_otherdb/upgrade_catalog_otherdb_93_026.sql new file mode 100644 index 0000000000000000000000000000000000000000..9a3890d1633fad6bda80e876bd707ba7c9beb8c6 --- /dev/null +++ b/src/include/catalog/upgrade_sql/upgrade_catalog_otherdb/upgrade_catalog_otherdb_93_026.sql @@ -0,0 +1,79 @@ +--upgrade TABLE +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_CATALOG, false, true, 4885, 4886, 0, 0; +CREATE TABLE pg_catalog.pg_statistic_history ( + namespaceid oid, + starelid oid, + partid oid, + statype "char", + last_analyzetime timestamp with time zone, + current_analyzetime timestamp with time zone not null, + starelkind "char", + staattnum smallint, + stainherit boolean, + stanullfrac real, + stawidth int, + stadistinct real, + reltuples double precision, + relpages double precision, + stalocktype "char", + stakind1 smallint, + stakind2 smallint, + stakind3 smallint, + stakind4 smallint, + stakind5 smallint, + staop1 oid, + staop2 oid, + staop3 oid, + staop4 oid, + staop5 oid, + stanumbers1 real[], + stanumbers2 real[], + stanumbers3 real[], + stanumbers4 real[], + stanumbers5 real[], + stavalues1 anyarray, + stavalues2 anyarray, + stavalues3 anyarray, + stavalues4 anyarray, + stavalues5 anyarray, + stadndistinct real, + staextinfo text +) WITH(oids=false) TABLESPACE pg_default; + +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_CATALOG, false, true, 0, 0, 0, 4887; +CREATE INDEX pg_catalog.pg_statistic_history_namespac_tab_index ON pg_statistic_history USING btree (namespaceid oid_ops, starelid oid_ops); + +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_CATALOG, false, true, 0, 0, 0, 4894; +CREATE INDEX pg_catalog.pg_statistic_history_current_analyzetime_index ON pg_statistic_history USING btree (current_analyzetime timestamptz_ops); + +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_CATALOG, false, true, 0, 0, 0, 4902; +CREATE INDEX pg_catalog.pg_statistic_history_tab_statype_index ON pg_statistic_history USING btree (starelid oid_ops, statype char_ops); + +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_CATALOG, false, true, 0, 0, 0, 4903; +CREATE INDEX pg_catalog.pg_statistic_history_tab_part_statype_index ON pg_statistic_history USING btree (starelid oid_ops, partid oid_ops, statype char_ops); + +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_CATALOG, false, true, 0, 0, 0, 4904; +CREATE INDEX pg_catalog.pg_statistic_history_tab_statype_relkind_attnum_index ON pg_statistic_history USING btree (starelid oid_ops, statype char_ops, starelkind char_ops, staattnum int2_ops); + +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_CATALOG, false, true, 0, 0, 0, 0; +GRANT SELECT ON pg_catalog.pg_statistic_history TO PUBLIC; + +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_CATALOG, false, true, 4897, 4898, 0, 0; +CREATE TABLE pg_catalog.pg_statistic_lock ( + namespace text, + relid oid, + partname text, + stalocktype "char" not null +) WITH(oids=false) TABLESPACE pg_default; + +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_CATALOG, false, true, 0, 0, 0, 4899; +CREATE INDEX pg_catalog.pg_statistic_lock_namespac_type_index ON pg_statistic_lock USING btree (namespace text_ops, stalocktype char_ops); + +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_CATALOG, false, true, 0, 0, 0, 4900; +CREATE UNIQUE INDEX pg_catalog.pg_statistic_lock_rel_type_index ON pg_statistic_lock USING btree (relid oid_ops, stalocktype char_ops); + +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_CATALOG, false, true, 0, 0, 0, 4901; +CREATE UNIQUE INDEX pg_catalog.pg_statistic_lock_rel_part_type_index ON pg_statistic_lock USING btree (relid oid_ops, partname text_ops, stalocktype char_ops); + +SET LOCAL inplace_upgrade_next_system_object_oids = IUO_CATALOG, false, true, 0, 0, 0, 0; +GRANT SELECT ON pg_catalog.pg_statistic_lock TO PUBLIC; \ No newline at end of file diff --git a/src/include/knl/knl_guc/knl_session_attr_sql.h b/src/include/knl/knl_guc/knl_session_attr_sql.h index 182fa7cf3109aeaebe0eaf82be8051638b9cf22f..b8f0acff78b39b4130ddedf249578054c807977e 100644 --- a/src/include/knl/knl_guc/knl_session_attr_sql.h +++ b/src/include/knl/knl_guc/knl_session_attr_sql.h @@ -263,6 +263,7 @@ typedef struct knl_session_attr_sql { #endif bool var_eq_const_selectivity; int vectorEngineStrategy; + int gms_stats_history_retention; #if (!defined(ENABLE_MULTIPLE_NODES)) && (!defined(ENABLE_PRIVATEGAUSS)) bool enable_custom_parser; bool dolphin; diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index a4fbc05a73f1f33dcd78d151da2dd00609e3cf4c..d843c440ea53714a2dd81e6d242ed07e4d31d550 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -38,6 +38,7 @@ /***************************************************************************** * Backend version and inplace upgrade staffs *****************************************************************************/ +extern const uint32 STATISTIC_HISTORY_VERSION_NUMBER; extern const uint32 FETCH_ENHANCE_VERSION_NUM; extern const uint32 PUBLICATION_DDL_AT_VERSION_NUM; extern const uint32 PIPELINED_FUNCTION_VERSION_NUM;