diff --git a/sql/functions/apply_constraints.sql b/sql/functions/apply_constraints.sql index 4f60749..4b96e7d 100644 --- a/sql/functions/apply_constraints.sql +++ b/sql/functions/apply_constraints.sql @@ -131,7 +131,7 @@ IF p_child_table IS NULL THEN ELSE v_optimize_counter := v_optimize_counter + 1; IF v_optimize_counter > v_optimize_constraint THEN - v_child_tablename = v_row_max_value.partition_tablename; + v_child_tablename := v_row_max_value.partition_tablename; EXIT; END IF; END IF; diff --git a/test/test-id-time-subpart-custom-start.sql b/test/test-id-time-subpart-custom-start.sql index cc6c0da..ef4ffef 100644 --- a/test/test-id-time-subpart-custom-start.sql +++ b/test/test-id-time-subpart-custom-start.sql @@ -1,7 +1,7 @@ --- ########## NATIVE ID PARENT / TIME SUBPARENT TESTS ########## +-- ########## ID PARENT / TIME SUBPARENT TESTS ########## -- Additional tests: no pg_jobmon -- Test using a pre-created template table and passing to create_parent. Should allow indexes to be made for initial children. - -- additional constraint column + -- additional constraint column (top lvl int) \set ON_ERROR_ROLLBACK 1 \set ON_ERROR_STOP true @@ -9,7 +9,7 @@ BEGIN; SELECT set_config('search_path','partman, public',false); -SELECT plan(323); +SELECT plan(321); CREATE SCHEMA partman_test; CREATE TABLE partman_test.fk_test_reference (col2 text unique not null); @@ -31,7 +31,7 @@ ALTER TABLE partman_test.template_id_taptest_table ADD PRIMARY KEY (col1); CREATE INDEX ON partman_test.id_taptest_table (col3); ALTER TABLE partman_test.id_taptest_table ADD FOREIGN KEY (col2) REFERENCES partman_test.fk_test_reference(col2); -SELECT create_parent('partman_test.id_taptest_table', 'col1', '10', p_constraint_cols =>'{"col3"}', p_jobmon => false, p_template_table => 'partman_test.template_id_taptest_table'); +SELECT create_parent('partman_test.id_taptest_table', 'col1', '10', p_constraint_cols =>'{"col2"}', p_jobmon => false, p_template_table => 'partman_test.template_id_taptest_table'); INSERT INTO partman_test.id_taptest_table (col1) VALUES (generate_series(1,9)); SELECT is_partitioned('partman_test', 'id_taptest_table', 'Check that id_taptest_table is natively partitioned'); @@ -295,6 +295,7 @@ SELECT is_empty('SELECT * FROM partman_test.id_taptest_table_p40', 'Check count INSERT INTO partman_test.id_taptest_table (col1, col3) VALUES (generate_series(10,20), CURRENT_TIMESTAMP+'1 day'::interval); SELECT run_maintenance(); + SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_default', 'Check that top parent default is empty'); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p0_default', 'Check that subparent default p0 is empty'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0', ARRAY[9], 'Check count from parent table partman_test.id_taptest_table_p0'); @@ -387,7 +388,6 @@ SELECT is_empty('SELECT * FROM partman_test.id_taptest_table_p60', 'Check count SELECT hasnt_table('partman_test', 'id_taptest_table_p70', 'Check id_taptest_table_p70 doesn''t exists yet'); --- Ensure time partitioning works for all sub partitions UPDATE part_config SET premake = 5 WHERE parent_table ~ 'partman_test.id_taptest_table_p'; SELECT run_maintenance(); @@ -448,6 +448,14 @@ SELECT hasnt_table('partman_test', 'id_taptest_table_p50_p'||to_char(CURRENT_TIM SELECT hasnt_table('partman_test', 'id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYYMMDD'), 'Check id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYYMMDD')||' does not exist'); +-- Ensure constraint exclusion for top level. Latest data is in _p5, so optimize_constraint of 4 would be before _p1 +INSERT INTO partman_test.id_taptest_table (col1, col3) VALUES (generate_series(30,50), CURRENT_TIMESTAMP+'1 day'::interval); +UPDATE partman.part_config SET optimize_constraint = 4 WHERE parent_table = 'partman_test.id_taptest_table'; + +SELECT run_maintenance(); +SELECT col_has_check('partman_test', 'id_taptest_table_p0', 'col2' + , 'Check for additional constraint on col2 on id_taptest_table_p0'); + -- Test dropping without retention set SELECT drop_partition_time ('partman_test.id_taptest_table_p0', '2 days', p_keep_table := false); SELECT has_table('partman_test', 'id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYYMMDD'), @@ -462,7 +470,7 @@ SELECT hasnt_table('partman_test', 'id_taptest_table_p0_p'||to_char(CURRENT_TIME 'Check id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP-'6 days'::interval, 'YYYYMMDD')||' does not exist'); -UPDATE part_config SET retention = '10', retention_keep_table = false WHERE parent_table = 'partman_test.id_taptest_table'; +UPDATE part_config SET retention = '30', retention_keep_table = false WHERE parent_table = 'partman_test.id_taptest_table'; UPDATE part_config SET retention = '2 days', retention_keep_table = false WHERE parent_table = 'partman_test.id_taptest_table_p0'; UPDATE part_config SET retention = '2 days', retention_keep_table = false WHERE parent_table = 'partman_test.id_taptest_table_p10'; UPDATE part_config SET retention = '2 days', retention_keep_table = false WHERE parent_table = 'partman_test.id_taptest_table_p20'; @@ -475,31 +483,19 @@ UPDATE part_config SET retention = '2 days', retention_keep_table = false WHERE SELECT run_maintenance(); +SELECT hasnt_table('partman_test', 'id_taptest_table_p0', + 'Check id_taptest_table_p0 does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP, 'YYYYMMDD'), 'Check id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP, 'YYYYMMDD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYYMMDD'), 'Check id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYYMMDD')||' does not exist'); -SELECT hasnt_table('partman_test', 'id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYYMMDD'), - 'Check id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYYMMDD')||' does not exist'); -SELECT hasnt_table('partman_test', 'id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYYMMDD'), - 'Check id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYYMMDD')||' does not exist'); -SELECT hasnt_table('partman_test', 'id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYYMMDD'), - 'Check id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYYMMDD')||' does not exist'); -SELECT hasnt_table('partman_test', 'id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYYMMDD'), - 'Check id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYYMMDD')||' does not exist'); -SELECT hasnt_table('partman_test', 'id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP-'1 days'::interval, 'YYYYMMDD'), - 'Check id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP-'1 days'::interval, 'YYYYMMDD')||' does not exist'); -SELECT hasnt_table('partman_test', 'id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYYMMDD'), - 'Check id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYYMMDD')||' does not exist'); +SELECT hasnt_table('partman_test', 'id_taptest_table_p10', + 'Check id_taptest_table_p10 does not exist'); +SELECT hasnt_table('partman_test', 'id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP, 'YYYYMMDD'), + 'Check id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP, 'YYYYMMDD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYYMMDD'), 'Check id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYYMMDD')||' does not exist'); -SELECT hasnt_table('partman_test', 'id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYYMMDD'), - 'Check id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYYMMDD')||' does not exist'); -SELECT hasnt_table('partman_test', 'id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP-'5 days'::interval, 'YYYYMMDD'), - 'Check id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP-'5 days'::interval, 'YYYYMMDD')||' does not exist'); -SELECT hasnt_table('partman_test', 'id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP-'6 days'::interval, 'YYYYMMDD'), - 'Check id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP-'6 days'::interval, 'YYYYMMDD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYYMMDD'), 'Check id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYYMMDD')||' does not exist'); @@ -546,7 +542,6 @@ SELECT hasnt_table('partman_test', 'id_taptest_table_p60_p'||to_char(CURRENT_TIM SELECT hasnt_table('partman_test', 'id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP-'6 days'::interval, 'YYYYMMDD'), 'Check id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP-'6 days'::interval, 'YYYYMMDD')||' does not exist'); -SELECT undo_partition('partman_test.id_taptest_table_p10', p_target_table := 'partman_test.undo_taptest', p_loop_count => 20, p_keep_table := false); SELECT has_table('partman_test', 'template_id_taptest_table', 'Check that template table was not removed yet'); SELECT undo_partition('partman_test.id_taptest_table_p20', p_target_table := 'partman_test.undo_taptest', p_loop_count => 20, p_keep_table := false); SELECT has_table('partman_test', 'template_id_taptest_table', 'Check that template table was not removed yet'); @@ -558,9 +553,15 @@ SELECT undo_partition('partman_test.id_taptest_table_p50', p_target_table := 'pa SELECT has_table('partman_test', 'template_id_taptest_table', 'Check that template table was not removed yet'); SELECT undo_partition('partman_test.id_taptest_table_p60', p_target_table := 'partman_test.undo_taptest', p_loop_count => 20, p_keep_table := false); SELECT has_table('partman_test', 'template_id_taptest_table', 'Check that template table was not removed yet'); +SELECT undo_partition('partman_test.id_taptest_table_p70', p_target_table := 'partman_test.undo_taptest', p_loop_count => 20, p_keep_table := false); +SELECT has_table('partman_test', 'template_id_taptest_table', 'Check that template table was not removed yet'); +SELECT undo_partition('partman_test.id_taptest_table_p80', p_target_table := 'partman_test.undo_taptest', p_loop_count => 20, p_keep_table := false); +SELECT has_table('partman_test', 'template_id_taptest_table', 'Check that template table was not removed yet'); +SELECT undo_partition('partman_test.id_taptest_table_p90', p_target_table := 'partman_test.undo_taptest', p_loop_count => 20, p_keep_table := false); +SELECT has_table('partman_test', 'template_id_taptest_table', 'Check that template table was not removed yet'); -SELECT results_eq('SELECT count(*)::int FROM partman_test.undo_taptest', ARRAY[11], 'Check count from target of undo_partition'); +SELECT results_eq('SELECT count(*)::int FROM partman_test.undo_taptest', ARRAY[22], 'Check count from target of undo_partition'); SELECT hasnt_table('partman_test', 'id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP, 'YYYYMMDD'), 'Check id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP, 'YYYYMMDD')||' does not exist'); @@ -698,7 +699,7 @@ SELECT is_empty('SELECT parent_table from part_config where parent_table = ''par SELECT hasnt_table('partman_test', 'template_id_taptest_table', 'Check that template table was removed'); -SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.undo_taptest', ARRAY[11], 'Check count from final unpartitioned target table'); +SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.undo_taptest', ARRAY[22], 'Check count from final unpartitioned target table'); SELECT * FROM finish(); ROLLBACK; diff --git a/test/test-time-epoch-id-subpart-native.sql b/test/test-time-epoch-id-subpart-native.sql index 7d6a990..53a2e8e 100644 --- a/test/test-time-epoch-id-subpart-native.sql +++ b/test/test-time-epoch-id-subpart-native.sql @@ -3,6 +3,7 @@ -- Set analyze to true for maintenance -- Premake template table and ensure pk is made on initial child tables -- TODO Add additional pk checks to all children + -- Allow NOT NULL for control (top level) \set ON_ERROR_ROLLBACK 1 \set ON_ERROR_STOP true @@ -16,14 +17,14 @@ CREATE SCHEMA partman_test; CREATE TABLE partman_test.time_taptest_table ( col1 int NOT NULL , col2 text - , col3 int NOT NULL DEFAULT extract('epoch' from CURRENT_TIMESTAMP)::int) + , col3 int DEFAULT extract('epoch' from CURRENT_TIMESTAMP)::int) PARTITION BY RANGE (col3); CREATE TABLE partman_test.undo_taptest (LIKE partman_test.time_taptest_table INCLUDING ALL); CREATE TABLE partman_test.template_time_taptest_table (LIKE partman_test.time_taptest_table); ALTER TABLE partman_test.template_time_taptest_table ADD PRIMARY KEY (col1); -SELECT create_parent('partman_test.time_taptest_table', 'col3', '1 day', p_epoch := 'seconds', p_template_table := 'partman_test.template_time_taptest_table' ); +SELECT create_parent('partman_test.time_taptest_table', 'col3', '1 day', p_epoch := 'seconds', p_template_table := 'partman_test.template_time_taptest_table', p_control_not_null := false ); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(1,10), extract('epoch' from CURRENT_TIMESTAMP)::int); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYYMMDD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYYMMDD')||' exists'); diff --git a/updates/pg_partman--5.1.0--5.2.0.sql b/updates/pg_partman--5.1.0--5.2.0.sql index b4605ec..33c7a7a 100644 --- a/updates/pg_partman--5.1.0--5.2.0.sql +++ b/updates/pg_partman--5.1.0--5.2.0.sql @@ -1,13 +1,32 @@ --- TODO - add test for PR#650 - not creating partitions that aren't being retained anyway --- - add test to ensure sub partitioning preserves default table settings and not null enforcement --- - add test for constraint exclusion with integer-based partitioning +-- SEE CHANGELOG.md for all notes and details on this update + +CREATE TEMP TABLE partman_preserve_privs_temp (statement text); + +INSERT INTO partman_preserve_privs_temp +SELECT 'GRANT EXECUTE ON FUNCTION @extschema@.create_parent(text, text, text, text, text, int, text, boolean, text, text[], text, boolean, text, boolean) TO '||array_to_string(array_agg('"'||grantee::text||'"'), ',')||';' +FROM information_schema.routine_privileges +WHERE routine_schema = '@extschema@' +AND routine_name = 'create_parent' +AND grantee != 'PUBLIC'; + +INSERT INTO partman_preserve_privs_temp +SELECT 'GRANT EXECUTE ON FUNCTION @extschema@.create_sub_parent(text, text, text, text, boolean, text, text[], int, text, text, boolean, text, boolean) TO '||array_to_string(array_agg('"'||grantee::text||'"'), ',')||';' +FROM information_schema.routine_privileges +WHERE routine_schema = '@extschema@' +AND routine_name = 'create_sub_parent' +AND grantee != 'PUBLIC'; + +INSERT INTO partman_preserve_privs_temp +SELECT 'GRANT EXECUTE ON FUNCTION @extschema@.check_subpart_sameconfig(text) TO '||array_to_string(array_agg('"'||grantee::text||'"'), ',')||';' +FROM information_schema.routine_privileges +WHERE routine_schema = '@extschema@' +AND routine_name = 'check_subpart_sameconfig' +AND grantee != 'PUBLIC'; --- TODO PRESERVE PRIVILEGES DROP FUNCTION @extschema@.create_parent(text, text, text, text, text, int, text, boolean, text, text[], text, boolean, text); DROP FUNCTION @extschema@.create_sub_parent(text, text, text, text, boolean, text, text[], int, text, text, boolean, text); DROP FUNCTION @extschema@.check_subpart_sameconfig(text); - ALTER TABLE @extschema@.part_config DROP COLUMN default_table; ALTER TABLE @extschema@.part_config_sub ADD COLUMN sub_control_not_null boolean DEFAULT true; @@ -3842,7 +3861,7 @@ IF p_child_table IS NULL THEN ELSE v_optimize_counter := v_optimize_counter + 1; IF v_optimize_counter > v_optimize_constraint THEN - v_child_tablename = v_row_max_value.partition_tablename; + v_child_tablename := v_row_max_value.partition_tablename; EXIT; END IF; END IF; @@ -4110,3 +4129,18 @@ EXECUTE format('ANALYZE %I.%I', v_parent_schema, v_parent_tablename); PERFORM pg_advisory_unlock(hashtext('pg_partman reapply_constraints')); END $$; + +-- Restore dropped object privileges +DO $$ +DECLARE +v_row record; +BEGIN + FOR v_row IN SELECT statement FROM partman_preserve_privs_temp LOOP + IF v_row.statement IS NOT NULL THEN + EXECUTE v_row.statement; + END IF; + END LOOP; +END +$$; + +DROP TABLE IF EXISTS partman_preserve_privs_temp;