diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fab577a..d1060220 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,9 @@ UPGRADE NOTES ------------- - It is recommended that you take a backup of the `part_config` and `part_config_sub` tables before upgrading just to ensure they can be restored in case there are any issues. These tables are recreated as part of the upgrade. - - If you see any errors about the following tables existing during an upgrade attempt, please review their content and ensure you do not need any of the backed up pre-5.x configuration data they contain. Drop them if not needed and try the upgrade again: `part_config_pre_500_data`, `part_config_sub_pre_500_data` + - If you see any errors about the following tables existing during an upgrade attempt, please review their content and ensure you do not need any of the backed up pre-5.x configuration data they contain. Drop them if not needed and try the upgrade again: `part_config_pre_500_data`, `part_config_sub_pre_500_data` - For CHANGELOG notes prior to version 5.0.0, please see [CHANGELOG-pre-5.0.0.txt](CHANGELOG-pre-5.0.0.txt). - - Many thanks to all the people that have help with testing and code review during 5.x development. In particular... + - Many thanks to all the people that have helped with testing and code review during 5.x development. In particular... - Leigh Downs and the team at Crunchy Data for extensive testing - vitaly-burovoy on Github for some amazing optimizations and code review - andyatkinson on Github for documentation review and pointing out my antiquated usage of the term "native" now that there's only one partitioning method supported @@ -13,10 +13,10 @@ UPGRADE NOTES BREAKING CHANGES ---------------- - - Removed trigger-based partitioning support. All partitioning is now done using built-in declarative partitioning. The partitioning `type` in pg_partman will now refer to the types of delcarative partitioning that are supported. As of 5.0.0, only `range` is supported, but others are in development. + - Removed trigger-based partitioning support. All partitioning is now done using built-in declarative partitioning. The partitioning `type` in pg_partman will now refer to the types of delcarative partitioning that are supported. As of 5.0.0, only `range` is supported, but others are in development (Github Issue #490). - See [migrate_to_declarative.md](doc/migrate_to_declarative.md) for assistance on migrating off trigger-based partitioning. - Many functions have had their parameters altered, renamed, rearranged or removed. These should be more consistent across the code-base now. Please review ALL calls to pg_partman functions to ensure that your parameter names and values have been updated to match the changes. - - The `part_config` and `part_config_sub` tables have also had some columns removed and rearranged to only contain supported features. + - The `part_config` and `part_config_sub` tables have had some columns removed and rearranged to only contain supported features. - Due to a majority of extension objects being dropped & recreated, privileges on the extension objects ARE NOT being preserved as they have been done with past extension updates. Ensure existing privileges are recorded before upgrading pg_partman and are regranted/revoked after the upgrade is complete. Check the following system catalogs for privilege information for pg_partman objects: `information_schema.routine_privileges` & `information_schema.table_privileges` - Some specialized time-based interval types have been deprecated. See [pg_partman_5.0.0_upgrade.md](doc/pg_partman_5.0.0_upgrade.md) for additional guidance on migrating unsupported partition methods to supported ones. - All time-based interval values must now be valid values for the interval data type. The previous weekly, hourly, daily, etc interval values are no longer supported. @@ -24,20 +24,27 @@ BREAKING CHANGES - Removed specialized weekly partitioning with ISO style week numbers (see [5.0.0 migration doc](doc/pg_partman_5.0.0_upgrade.md)). - Hourly partitioning now has seconds on the child partition suffix. Migration for this is not necessary, but just be aware that any new partition sets created with this interval may look different than existing ones from prior pg_partman versions. - The minimum required version of PostgreSQL is now 14 - - Required for calling procedures via background worker + - Required for calling procedures via background worker (Github PR #242). + - Dropped automatic publication refresh of a partition set that was part of a logical replication subscription. As of PostgreSQL 14, it is no longer possible to call ALTER SUBSCRIPTION ... REFRESH PUBLICATION within a transaction block which means it can no longer be called via the maintenance functions. For now, an independent call with this refresh statement will need to be done on the subscription side of partition sets that are part of logical replication (Github Issue #572). NEW FEATURES ------------ - Changed all usage of the term "native" to "declarative" to better match upstream PostgreSQL terminology for built-in partitioning. - Simplified all time-based partitioning suffixes to `YYYYMMDD` for intervals greater than or equal to 1 day and `YYYYMMDD_HH24MISS` for intervals less than 1 day. Removed extra underscores to allow longer base partition names. Existing partition suffixes will still be supported, but newly created partition sets will use the new naming patterns by default. It is recommended that migration to the new suffixes is done when possible to ensure future support of possible pg_partman changes. The [documentation](doc/pg_partman_5.0.0_upgrade.md) on migrating the old specialized weekly/quarterly partition sets to be supported in 5.0.0 can be used as guidance for migrating other child table names as well. - By default, data in the default partition is now ignored when calculating new child partitions to create. If a new child table's boundaries would include data that exists in the default, this will cause an error during maintenance and must be manually resolved by either removing that data from the default or partitioning it out to the proper child table using the partition_data function/procedure. - - A flag is available to take default data into consideration, but this should only be used in rare circumstances to correct maintenance issues and should not be left permanently enabled. - - Added option to make default table optional during partition creation (`p_default_table`). - - As of PostgreSQL 13, newly created child tables in a partition set that is part of a logical replication PUBLICATION are automatically added to that PUBLICATION. Therefore the "publications" array configuration in the pg_partman configuration tables was removed. Simply make sure the parent table is a part of the necessary publications and it will be automatically handled by core PostgreSQL from now on. - - Note the SUBSCRIPTION does not automatically get refreshed to account for new tables added to a published partition set. If pg_partman is also managing your partition set on the SUBSCRIPTION side, ensure the `subscription_refresh` flag in the configuration table is set to true so that maintenance will automatically add the new tables to the subscription. - - Added support for dropping indexes for partitions moved to another schema as part of retention - - Creating a template table is now optional when calling `create_parent()`. Set `p_template_table` to `false` to skip template table creation. Note this is not a boolean since this parameter is also meant to take a template table name, so the explicit string value `false` must be set. + - A flag is available to take default data into consideration, but this should only be used in rare circumstances to correct maintenance issues and should not be left permanently enabled for ideal partition maintenance. + - Added option to make default table optional during partition creation (`p_default_table`) (Github Issue #489). + - As of PostgreSQL 13, newly created child tables in a partition set that is part of a logical replication PUBLICATION are automatically added to that PUBLICATION. Therefore the "publications" array configuration in the pg_partman configuration tables was removed. Simply make sure the parent table is a part of the necessary publications and it will be automatically handled by core PostgreSQL from now on (Github Issue #520) + - Note the SUBSCRIPTION does not automatically get refreshed to account for new tables added to a published partition set. See note above in BREAKING CHANGES concerning subscription side refreshes. + - Added support for dropping indexes for partitions moved to another schema as part of retention (Github PR #449). + - Creating a template table is now optional when calling `create_parent()`. Set `p_template_table` to `false` to skip template table creation. Note this is not a boolean since this parameter is also meant to take a template table name, so the explicit string value `false` must be set (Github Issue #505). + - Lots of backend optimizations and code simplification BUG FIXES --------- - Edge case with `infinite_time_partitions` fixed. If set to true and data far ahead of "now" was inserted, no new child tables would be created based on the premake. + - Make errors from `show_partition_info()` clearer (Github Issue #542). + - Ensure the bgw_type value is properly set in the Background Worker structs (Github Issue #573). + + +For CHANGELOGs prior to version 5.0.0, see CHANGELOG-pre-5.0.0.txt diff --git a/CHANGELOG.txt b/CHANGELOG.txt deleted file mode 100644 index 260b56b4..00000000 --- a/CHANGELOG.txt +++ /dev/null @@ -1,952 +0,0 @@ -4.7.4 -NEW FEATURES -============ --- Compatible with PostgreSQL 16. - - -4.7.3 -BUG FIXES -========= --- Fix partition_gap_fill() to work with epoch partitioning (Github Issue #503). - - -4.7.2 -BUG FIXES -========= --- Fix incorrect positional variable usage in partition_data_time that lead to row locking timeout not working properly. Thanks to @nicwolff on Github for the fix. (PR #495, Issue #496) - - -4.7.1 -BUG FIXES -========= --- When part_config.retention_keep_table was set to false and part_config.retention_schema was not NULL, the retention_schema option being set was not properly overriding the retention_keep_table option. This was causing the child tables to not be detached from the partition set, even though they were properly moved to the retention_schema. This could also inadvertently cause undo_partition() to fail since it expects all child tables to exist in the same schema. Thank you to @gsrirangan on Github for reporting this issue. (Github Issue #481) - - -4.7.0 -NEW FEATURES -============ --- Compatible with PostgreSQL 15. --- New document providing guidance on adding missing procedures that may not have been installed when running PostgreSQL versions older than 11. --- Allow ignoring data in the default partition when creating child partitions. (Github Issue #462) --- Explicitly do not support partition tables that have periods in the schema or table names. --- New procedure run_analyze() to run an analyze on all tables managed by pg_partman. Allows disabling of the analyze done by run_maintenance() to allow regular partition maintenance to run more efficiently. Auto-vacuum does not currently kick in to analyze the parent table and can affect query performance if statistics for it are not kept up to date. - -BUG FIXES -========= --- Fixed the usage of the parameter 'p_date_trunc_interval' to create_parent() not properly being applied to future created sub-partition parents. - - -4.6.2 -BUG FIXES -========= - --- Fix bug introduced in verson 4.6.1 when pg_jobmon is installed and in use. Caused error to be raised around assignment of search_path variables. Thanks to @Dakotah312 on Github for reporting this issue (Github Issue #461). - - -4.6.1 -NEW FEATURES -============ --- Add support for nanosecond epoch partitioning - -BUG FIXES -========= --- Allow custom search paths to work better with pg_partman. Per discussion in Github Issue #350 related to custom data types. - --- Fix default partition not being added to logical replication publication when using template table for PostgreSQL versions before 14. It is recommended to use the built in publication inheritance for PG14+. - --- Fix updating pg_partman in some recent versions (4.4.0 -> current) when granting privileges to pg_partman objects using a role name that has special characters in the name. If older update scripts need to be fixed, please create an issue on Github. - --- Fixed drop_cascade_fk option not being honored when dropping time-based child tables during retention (only applies to non-native partitioning). Thanks to @lorenzo on github for the fix (Github Issue #442). - --- Fixed retention intervals for time-based partitioning causing unexpected results depending on the interval given. Thanks to @sfrost for the fix after reviewing Github Issue #446. - - -4.6.0 -NEW FEATURES -============ --- Support for PostgreSQL 14 added (Github Issue #392). - --- Added p_drop_cascade option to undo_partition()/undo_partition_proc() to allow undoing sub-partition sets from parent tables higher in the inheritance tree. Only applies when p_keep_tables is set to false. Note this causes all child tables below a sub-partition parent to be dropped when that parent is dropped. - --- Better support NOTIFY commands with the extension's built-in background worker. Thank you to @eisaev on Github for this fix. (GitHub Issue #384). - --- Deprecate pg_partman's specialized UPSERT support for native partitioning in PG11+. Raise error for users to use built-in INSERT ON CONFLICT feature instead. - --- Added new optional parameter to drop_partition_time() to tell partman to use a different reference timestamp from which to determine which partitions should be affected. Thanks to @wesselvdv on Github for this feature (Github PR#386). - -BUG FIXES -========= --- Better handle custom intervals that don't fall on typical datetime boundaries (monthly, daily, etc). Previously if you chose a custom interval that did not initially align on a typical boundary, pg_partman would always try and truncate the initial table's datetime start value to one of those typical boundaries. This could lead to child table constraints that were not expected. A new parameter to create_parent(), 'p_date_trunc_interval' has been added to allow you to tell pg_partman how to truncate the initial datetime value to better meet custom interval needs. This accepts any valid interval value that the built-in 'date_trunc()' function would accept. (Ex: 9 week interval starting on 2021-04-05 would need to be truncated by 'week' vs 'month'. Otherwise pg_partman would truncate by 'month' and the partitions would start on 2021-04-01 and result in unexpected child table boundaries). Many thanks to @wesselvdv on Github for reporting this issue and the many fixes and tests that went in to solving this (Github PR#371, Issue #369). - --- Fixed show_partition_info() to properly return boundary values no matter what custom interval may be used for the partition set. Update documentation to note that the show_partition_info() boundary results are INCLUSIVE for returned start values and EXCLUSIVE for returned end values (Github PR#372, Github Issue #369). - --- When dropping tables for retention, DROP...CASCADE is no longer the default. This avoids foreign key references TO the partition set getting dropped if any child table is dropped. Set the option "drop_cascade_fk" in part_config to allow the old behavior if desired. This cascade is only allowed with non-native, trigger-based partitioning and is not supported with sub-partitioning at this time (Github Issue #365). - --- Fixed edge case in sub-partitioning where an additional subpartition child table could be created that is just outside the max boundary limit of its parent table boundaries. - --- Better handling of large interval values in ID partitioning (bigint). Thanks to @calebj on Github for reporting the issue and providing a fix (Github Issue #388). - - -4.5.1 -NEW FEATURES -============ --- Allow relation options set on the template table to be inherited on the child table. As of PG13 and earlier, relation options set on the parent are not being set on the child tables. Unknown if PG14 will handle this yet or not (Github PR #348). - -BUG FIXES -========= --- CVE-2021-33204 - Fixed security issue that could allow arbitrary code execution using SECURITY DEFINER functions. Set explicit search_path to avoid this. Thanks to Github user @tapioaiven of Aiven Ltd for reporting the issue. https://nvd.nist.gov/vuln/detail/CVE-2021-33204 - --- Fixed several bugs in sub-partitioning when using a mixture of epoch and regular integer partitioning in the same partition set (Github Issue #357). - - - -4.5.0 -NEW FEATURES -============ --- Officially supported minimal version is now PostgreSQL 9.6. PostgreSQL 9.6 support will be ending the next pg_partman release after PostgreSQL 14 is released. - --- Removed p_debug from functions changed in this update. Slowly migrating all functions away from parameter to using debug logging level instead. - -BUG FIXES -========= --- Fixed issue when upgrading from 4.4.0 to 4.4.1 on PG10 and below. Upgrade path now goes directly from 4.4.0 to 4.5.0 and skips 4.4.1. All changes in 4.4.1 have been included in 4.5.0 where necessary. (Github Issue #342) - --- Added the parameter `p_ignored_columns` to partition_data_time(), partition_data_id(), partition_data_proc(), undo_partition() and undo_partition_proc(). The parameter is an array that can list one or more columns to ignore their values when moving the data. This allows for moving data out of the default/parent table when generated columns are in use. (Github Issue #336) - --- Fixed the p_start_partition parameter working properly with id->timestamp sub-partitioning. (Github Issue #341) - - -4.4.1 -NEW FEATURES -============ --- Added documentation for how to use pg_partman with native partitioning. - -BUG FIXES -========= --- Fix long present typos mostly in comments, error messages, & docs. (Github PR #315 & #319) --- Update description of the pg_partman_bgw.dbname GUC to state that it is not actually optional. --- Fix syntax error in reapply_indexes.py --- Fixed wrong variable usage in undo_partition_proc() exception. - - -4.4.0 -NEW FEATURES -============ - --- No longer requires superuser in order to grant privileges directly to child tables (setting inherit_privileges to true or running apply_privileges() directly). Thanks to Stephen Frost for suggesting this fix. This now makes pg_partman no longer require superuser for any operation other than initial installation. - -BUG FIXES -========= - --- Added new config option to allow maintenance to refresh logical replication subscriptions. This is to fix a shortcoming where logical replication is used to replicate partition sets between databases, but not having a way to automatically account for new tables being added on the publisher side. Set the subscription name via the part_config.subscription_refresh column by just setting the name of the subscription. For sub-partitioning, part_config_sub.sub_subscription_refresh is also available. (Github Issue #307) - --- Fix partition_data_time() function (called by partition_data_proc()) to work with epoch time partitioning properly when moving data out of the default partition with native partitioning. Thanks to @sheeju on Github for reporting the issue and providing fix. (Github PR #309) - --- Fixed handling of mixed case/non-standard character object names during maintenance runs with native partitioning on PG11 or greater. Thanks to @sheeju on Github for reporting the issue and providing fix. (Github PR #309) - --- Fixed columns in sub_part_config that were not being kept consistent between configurations for sub-partition tables that were themselves further sub-partitioned. You may possibly have some configurations that are inconsistent due to these columns being missed during previous maintenance runs. The following fix for the consistency check should find this your next maintenance run and throw an error. - --- Fixed consistency check to ensure all parent tables that are part of the same sub-partition set have the same configuration in the sub_part_config table. If you encounter an error about inconsistent data in sub-partition configs after updating to this version, please carefully read the error message that is produced on how to fix it. - --- Fixed broken error message from maintenance runs (record "v_row" has no field "sub_parent") when the above consistency check tried to produce an error message to indicate that the sub-partition configurations had an issue. - - - -4.3.1 -BUG FIXES -========= --- Add support for inheriting GENERATED columns from the parent table to the child tables. Thanks to @Dishwasha on Github for reporting the issue! (Github PR #302) - --- Fix handling of data existing in the default partition with time-based partitioning when that data is further ahead of any data that exists in non-default children. New child tables may not have been created as expected. Thanks to @adrianlzt on Github for reporting the issue! (Github Issue #303) - - -4.3.0 -NEW FEATURES -============ - --- New documentation for how to migrate a trigger-based partition set managed by pg_partman to a native set - doc/migrate_to_native.md (Github Issue #282) - --- Added new table matrix to the pg_partman.md documentation for which properties are handled via the special pg_partman template table depending on your version of PG. Please review this matrix carefully when updating the major version of PG to see which template properties may need to be applied to the real parent for inheritance to keep working as expected. - --- Tablespace inheritance for PG12+ is now handled via the real parent table of the partition set, no longer via the pg_partman template table. Note that tablespaces for primary/unique keys that are not part of the partition key must still be handled via the template where those indexes are defined. For PG11 and earlier, tablespace inheritance must still be handled via the template table. If you are upgrading from PG11 or earlier to PG12+, ensure the tablespace is properly set on the parent table for inheritance to work as expected. (Github Issue #274) - --- Added new function dump_partitioned_table_definition() to provide a means of replaying partition setup statements in another database or during a dump/restore. Thanks to James Coleman (jcoleman) for providing this new feature! (Github Issue #260, PR #291) - -BUG FIXES -========= - --- Fixed maintenance runs with retention, or direct calls to drop_partition*() functions, failing if there were foreign keys pointing to the child table that was to be dropped. (Github Issue #294) - --- Fixed partition_data*() functions failing if they are run in the same session multiple times. Temporary tables were not being cleaned up properly. (Github Issue #295) - --- Changed child_end_time value of internal show_partition_info() function to show the non-inclusive end time boundary value. Previously it would subtract one second to try and show the actual values being stored in the partition, but this was causing issues trying to build boundary values when trying to migrate to native partitioning. This was previously an undocumented function, so its API was not guaranteed to be stable. It is now documented so any further changes will be fully documented. - - - -4.2.2 --- Fix bug in template table index inheritance that prevented any additional indexes from being copied after finding an index definition that exists on both the template and parent tables. Github Pull Request #278. - -4.2.1 --- Fix bug in partition_data_time causing never-ending loop while calculating partition range for natively partitioned tables. Github issue #273 - - -4.2.0 --- Preliminary compatability with PostgreSQL 12. - --- IMPORTANT SUPPORT CHANGE: As of PostgreSQL 12's stable release, version 9.4 of PostgreSQL will no longer officially be supported by pg_partman. It will likely continue to function without any issue, but any bugs tied specifically to that version and lower will no longer be fixed and new features that are not compatible with 9.4 may be introduced. - --- IMPORTANT CHANGE: The UNLOGGED status of a partition set in native partitioning has changed to be managed via the template table, no longer the parent table. - -- Currently setting a natively partitioned parent table as UNLOGGED/LOGGED is not a property that can be changed with an ALTER TABLE. So if the state is later changed to LOGGED (or vice versa), this property will not change for future child tables. Current child tables were never handled via this manner before and always had to be fixed manually. - -- If any of your native partition sets are UNLOGGED, be sure to set this property on the template table before upgrading to this version. Otherwise new child tables may be created as LOGGED. - -- pg_partman will maintain this behavior until core PostgreSQL determines a definative way that UNLOGGED changes will be handled. - -- See reported bug at https://www.postgresql.org/message-id/flat/15954-b61523bed4b110c4%40postgresql.org - --- As of PostgreSQL 12, pg_partman will no longer support inheriting OIDs since the special column behavior has been removed. See the release notes for PG12 for more information on this change. The feature has not been deprecated from pg_partman for older versions of PostgreSQL, but all tests involving the feature have been removed and any bugs involving it will likely not be fixed unless it is a compatability issue with 12+. It is recommended to remove any dependencies on special OID behavior that you previously had in your partition sets at this time. - -- Fixed bug that prevented pg_partman from creating child tables in PG12+ due to useage of pg_class.relhasoids column. (Github Issue #256) - --- A new configuration option, "constraint_valid", has been added to the part_config(_sub) table to control whether the additonal constraints that pg_partman can manage (see "Constraint Exclusion" section in pg_partman.md) are added in a NOT VALID state. By default the constraints are added as VALID as they were before. Note that if this config is changed to "false", constraint exclusion will not work until the constraints are validated. - --- Fixed bug where primary key/unique indexes applied to pre-existing template tables were not being applied to the DEFAULT partition created in PG11+ native partitioning. Thanks to @dtseiler on Github for testing and reporting this. (Github Issue #266) - --- Fixed bug that was causing error in the retention management of time-based partitioning sets. If retention would attempt to remove the last child table, maintenance would throw an error about a missing table instead of just a warning about the last table attempting to be dropped. Thanks to @LaVoCG on Github for the examples to reproduce this scenario. (Github Issues #227 & #237) - --- Changed default option to false on PG11+ for whether the maintenance background worker will run an ANALYZE whenever a child table is created. If this is desired, please set the option pg_partman_bgw.analyze to true in your postgresql.conf. (Github Issue #262) - --- Properly throw an error with create_parent() when the data type of the partition column is numeric. Previously would not report any errors but would also not add the proper data to the part_config table so future maintenance would not work. Currently testing to see if numeric partitioning can be properly supported in pg_partman. (Github Issue #265) - - - -4.1.0 --- For PostgreSQL 10+ and native partitioning, privilege inheritance is now optional and turned off by default. This now allows pg_partman to run fully as a non-superuser when using native partitioning. (Github Issue #231) - -- This means that all interaction with these partition sets must be done through the parent table. - -- A new column in the part_config table, inherit_privileges, is available if you require direct access to the child tables. Note that this will then require superuser at some level (superuser ownership of pg_partman functions for SECURITY DEFINER or running maintenance as a superuser). Just set this option to true and run the reapply_privileges() function to set all child privileges to match the parent. From then on, all new child tables will inherit the parent's privileges. - -- Note that for non-native partitioning on all versions, privileges will continue to be inherited to avoid backward compatability issues and will therefore require a superuser as the above bullet notes. - -- All existing partition sets have had their configuration values set to true so they will continue to act as they did before this update. - -- Updated GRANTS that are required for a non-superuser are in the INSTALLATION section of the readme. - --- The partition_data*() functions now work with the new DEFAULT table feature of PG11. Running these functions will now move data out of the default partition and into the proper child tables, creating them if necessary (Github Issue #230). - -- Note that normal maintenance can fail if data goes into the default table since data that exists in the default cannot have a corresponding child created without taking extra steps. Those extra steps are now handled by partition_data*() and, once that is run, maintenance should be able to proceed as normal. - --- Renamed check_parent() function to check_default(). This function now checks both the parent table in trigger-based partition sets as well as the default table introduced in PostgreSQL 11. - --- The reapply_indexes.py script now only works on trigger-based partition sets or native partition sets in PG10. This script will likely never work with native partitioning in PG11+ without being extremely prone to unexpected behavior, so it is currently not supported at all. Be aware that non-unique index inheritance is built in for PG11+ and that should be used if possible (Github Issue #232). - --- Fixed partition_data_proc() not working for epoch time partitioned sets (Github Pull Request #239). - --- Use SplitIdentifierString() in BGW for more recent versions of PostgreSQL (10.5+) (Github Pull Request #241) - --- Fixed show_partition_info() to account for quarterly/3month partitioning that may not use the "q" naming convention (Github Issue #252, PR #251). - --- Set the drop indexes retention feature to only be usable with trigger-based partitioning or for native, only on PG10 and lower. Natively inherited indexes cannot be dropped from children. - - - -4.0.0 --- Initial release to provide support PostgreSQL 11. All PG11 functionality is subject to change before its final stable release. - --- IMPORTANT POSTGRESQL 11 UPGRADE STEPS - -- All foreign keys, any non-unique indexes, and any unique indexes that include the partition key must be created on the actual parent table and removed from the template table for those property inheritances to continue working. Foreign key inheritance from the template table no longer works at all. - -- Any unique indexes or primary keys that do not include the partition column must still use the template table in order to be inherited to child tables. - -- Indexes defined on the parent will take precidence over those defined on the template table. Duplicate indexes should not happen. If they do, please report this as a bug. - --- Note that if this version is installed on PG10 and older, the procedures will not be created. A migration script to update pg_partman to include those will be made available once PG11 stable has been released. - --- Major change in privilege/security of this extension. SECURITY DEFINER is no longer in use to cause most of pg_partman's functions to run as the owner of the called function. This is mostly due to the requirements to support PROCEDUREs in PG11, but it's also generally a better idea for security in the long run. - -- It is recommended to create a dedicated role for pg_partman maintenance runs and give it the privileges outlined in the setup section of the top level README. - -- The role running maintenance must now have permissions to create tables in the target schema. If you start seeing permission errors after upgrading, this is most likely what needs to be fixed. - -- The role running maintenance must now always be the owner of the tables it is managing. This shouldn't be too drastic of a change since previously it was making the maintenace function owner the owner of the partitioned tables. However, if you've changed partition table ownership after initial creation, you may run into issues until you set the pg_partman role as the owner. - -- If you are using logical replication, the partman maintenance role must be the owner of the publication in use. - -- As a bonus, the ALTER DEFAULT PRIVILEGES feature should now be much easier to use since you have a dedicated role for maintaining partitions. Check the documentation for this feature so that you can set any read/write privileges you want new partition sets to have at the time they are initially created. - -- Two functions retain their SECURITY DEFINER status: - -- check_name_length() - Used inside the trigger function and I don't want to break existing installations. It's an IMMUTABLE function so the security implications aren't that great. - -- apply_privileges() - Requires access to pg_authid & also potentially changes object ownership. - -- If you're using pg_jobmon, privileges for that extension are managed independently of pg_partman. See that extension's documentation for privileges it requires. - --- For PostgreSQL 11+, started conversion of some python scripts into PROCEDUREs that are now installed within the database. Scripts are still available for pre-11 versions, but will be phased out as soon as PG10 is no longer under support. - -- Note that the user that calls these procedures must have permissions to write to the partition set and create/drop partitions in the set. - -- partition_data.py script is now the partition_data_proc() PROCEDURE. - -- undo_partition.py script is now the undo_partition_proc() PROCEDURE. - -- reapply_constraints.py is now the reapply_constraints_proc() PROCEDURE. - -- reapply_foreign_keys.py not converted since it's not needed for PG11+. Foreign keys can be applied to the parent and will automatically be inherited. - -- reapply_indexes.py was not converted yet because concurrent index creation is not yet possible inside a PROCEDURE. - -- Unable to turn autovacuum off/on reliably in PROCEDURE as was previously done with python scripts. If autovac is an issue when (un)partitioning large tables, recommend manually disabling it. - --- Consolidated undo_partition_time(), undo_partition_id(), and undo_partition_native() functions into a single undo_partition() function. --- The old undo_partition() function that just copied data from child tables to the parent has been removed. The only utility this really had was a possible way to support undoing partition sets not maintained by pg_partman. With native partitioning now in place, its continued usability is now questionable and it was also causing confusion since people were using it and not the previous ones specialized for pg_partman. Can incorporate back in if someone can provide a further beneficial use case. - --- For PG11+, a DEFAULT partition is automatically created as part of the partition set to handle any data that does not have a matching child table. - -- It's just given a _default suffix on the existing parent table name. - -- Support within pg_partman for moving data out of the default partition is not included in 4.0.0, but will be included in a future release. - -- New parameter to show_partitions() can include the DEFAULT in its output, but it's not output unless specifically requested since it's not commonly needed in internal code where it's mostly used. - --- For native partitioning, added option to give a "source" table to the partition_data*() functions that is different from the parent table of the partition set. This assists with partitioning an existing table to a natively partitioned table. - --- For PostgreSQL 11+, run_maintenance_proc() has been added and should cause much less contention when running against many large partition sets in a single call. - -- For PG11+, this is the preferred maintenance method to run over the old run_maintenance() function. The old one will continue to work for the next few major releases, but please update all pg_partman maintenence calls in your environment to use the new procedure. - --- Simplified --type argument for undo_partition.py script for PG versions 10 and below. Similar to create_parent(), values are now 'partman' or 'native'. - --- Maintenance that creates new partitions on PG11+ and is using native partitioning will no longer analyze the partition sets by default. This should greatly cut down on maintenance run time and contention issues. If you're noticing any odd query plans, especially if you're using additionally configured constraints, you may need to schedule some manual analyzes or you can set the p_analyze parameter in either the function or procedure call to "true". - --- Fixed bug in run_maintenance() with subpartitioning where it would throw an error if retention dropped one of the parent tables undergoing maintenance. - --- Fixed template table inheritance for PG10 not working on minor version PostgreSQL 10.0 (but you really shouldn't be running this version anymore). - --- Fixed compilation errors for background worker against PostgreSQL 11. - - - -3.2.1 --- Fix bug with inheriting indexes from template tables with native partitioning. Was throwing syntax error related to format() function. Thanks to iwarford on github for reporting the issue (Github Issue #223). - - -3.2.0 --- Added option to include newly created child tables in a subscription for use in logical replication. PG10+ Only. (Github PR #218). - -- Signature of create_parent() function has changed, so ensure calls are accounting for new argument. - -- Does not currently work for subpartition tables if they are natively partitioned. --- Fix so that unique indexes that exist on the the template table are created on child tables. PG10+ native partitioning only. (GitHub PR #220, Issue #225). --- Fix so that indexes inherited from template table are put into the same tablespace as defined on the template table. PG10+ native partitioning only. (Github Issue #223). - - -3.1.3 --- Allow tablespace inheritance from the template table in PostgreSQL 10+. Note that PG10 currently completely ignores the TABLESPACE flag to the CREATE TABLE command on a native partition parent table. So a natively partitioned parent can never live in a different tablespace other than the default. Tablespaces must be declared for each child table and pg_partman can now do that using the template table the same way it does for indexes and foreign keys (Github Issue #212). --- Obey the inherit_fk config option for natively partitioned tables. Previously foreign keys would always be inherited even if this was set to false. --- Do not allow empty string value for retention_schema in configuration tables. If this update causes an error related to this, please ensure your retention schema is actually set or leave the column value NULL (Github #214). --- Moved the pg_partman github repository to its own dedicated organization at http://github.com/pgpartman. Please update all links that previously referred to the keithf4 personal repository. - - -3.1.2 --- Fixed reapply_indexes.py to work properly with native partitioning. Previously it would recreate all indexes on the template table. If you've run this script on a natively partitioned set already, please review the indexes that exist on your template table and ensure only the ones you need exist. You will likely have to re-run the script again to get the child indexes set properly and you will need to set the the --recreate_all option. If your primary key has changed, also set the --primary option. Thanks to Jawshua on Github for finding the bug and providing a fix. (Github Pull Request #206). - --- Added note to documentation (pg_partman.md) about support for the new IDENTITY feature in PG10. - -3.1.1 --- Fixed custom time intervals to work properly with native partitioning. (Github Issue #200) --- Fixed template table owner to be the same as the partitioned table owner when the template table is not manually set during native partition creation. Note this will not change any existing template table ownerships, it only affects newly created native partitions sets. (Github Issue #203) - - -3.1.0 --- Add support for index & foreign key inheritance in native partitioning for PostgreSQL 10. This is done using a template table that is automatically created in the same schema the pg_partman was installed to. The template table is based off the given parent and any indexes or foreign keys that you would like to be created on child tables can be created on this table. Note that the initial child tables created when create_parent() is called will not have these indexes or FKs unless you run reapply_indexes.py and/or reapply_foreign_keys.py after creating them on the template table. You can also have the initial child tables contain indexes/FKs if you premake the template table and provide it as a parameter to create_parent(). - -- Updated reapply_indexes.py and reapply_foreign_keys.py scripts to handle template table feature for native partitioning - -- IMPORTANT NOTE: This is hopefully only a temporary option until there is built in support for applying these table properties natively. Once native support for these features is added, this template table will be phased out quickly. - --- Retention policy will no longer allow the final child table of a partition set to be dropped. pg_partman always requires at least one child table to work properly. If retention attempts to remove the final child table in a partition set, a WARNING is issued but the child table is not dropped. (Github Issue #189) - --- Fixed bug in partition trigger function creation for time-based partitioning where the target tables defined in the trigger code itself may not match the child tables that exist. This could occur only if the create_function_time() function was called independently of normal maintenance and the child tables were behind "now" as of when the function was ran. The target tables in the function are now based on the data that exists in the table as long as infinite_time_partitions is not set. If that is set, the tables defined in the trigger are always based on the current time as of when the function itself is run (same behavior as before). This function is rarely called independently and this is not an issue that would occur with normal maintenance runs since the partition trigger function is never rewritten unless a new child table is actually created. (Github Issue #176) - --- The show_partition_name() function now returns the child table schema as a separate field. - - -3.0.2 --- Allow maintenance background worker to restart itself if it crashes. Previously if the main background worker process exited for any reason, the only way to restart it was to restart the whole postgresql instance. Now it will try to restart itself every 10 minutes. A warning will be issued into the logs if this event occurs. (Github Issue #180). - -- NOTE: You will have to restart the PostgreSQL cluster to load the new library with the auto-restart option. --- Remove internal version checking function and use simpler comparison method using integer based version number (Pull Request #183). --- Apply fixes from running plpgsql_check (https://github.com/okbob/plpgsql_check/) - - -3.0.0 & 3.0.1 --- IMPORTANT NOTE: The initial version of pg_partman 3 had to be split into two updates due to changing both the data in the config table as well as adding constraints on that data. Depending on the data contained in the config table, doing this in a single-transaction update may not work (you may see an error about pending trigger events). If this is the case, please update to version 3.0.1 in a separate transaction from your update to 3.0.0. Example: - - /* - BEGIN; - ALTER EXTENSION pg_partman UPDATE TO '3.0.0'; - COMMIT; - - BEGIN; - ALTER EXTENSION pg_partman UPDATE TO '3.0.1'; - COMMIT; - */ - --- Update 3.0.1 MUST be installed for version 3.x of pg_partman to work properly. As long as these updates are run within a few seconds of each other, there should be no issues. - --- In preparation for native partitioning support, the "partition_type" parameter to create_parent() and create_sub_parent() has been simplified. - -- The two main, valid values are now: "partman" or "native". "partman" is the same pre-3.0 partitioning method that has always existed. "native" uses the new native partitioning method that comes with PostgreSQL 10. See below for important information about native partitioning. - -- Whether a table is partitioned by time or id/serial is now automatically determined by the data type of the partition column. - -- smallint, integer, & bigint (int2, int4, int8 respectively) are automatically id/serial partitioning (unless the epoch flag is set) - -- timestamptz, timestamp, & date are automatically time - -- If the preset time intervals in the documentation are used (daily, weekly, etc), then the more efficient, static trigger function will be used. If any other time interval value is given, then the slower, but more flexible "time-custom" trigger will be used (See "Custom Time Interval Considerations" section of documentation). - -- "time-custom" is also still a valid value, but not necessary unless you want to be explicit in your partitioning method. - --- Added preliminary support for native partitioning methods that will be introduced in PostgreSQL 10. Note this feature is still in very early testing, and could change before the official 10.0 release. But since it's a significant new feature, I want to stay ahead of it and allow real-world testing as early as possible for those interested in it and also make sure it works with pg_partman's own trigger-based partitioning methods as well. (Github Issue #171) - -- Use partition_type "native" in the create_parent() or create_sub_parent() functions. See pg_partman.md for more info - -- Native Sub-partitioning is a DESTRUCTIVE processes and cannot be run on a table that has any data without losing that data. This is because a table has to be declared natively partitioned at creation (no ALTER statement exists) and the current child tables that must become parents have to be dropped and recreated as such. - -- A new parameter flag for create_sub_parent() is included that must be set to "yes" to help ensure this is not accidentally done. --- Features NOT (yet) available in native: - -- Indexes are not currently possible to create on a partitioned parent table. This means there is currently no automatic index support for native partitioning in pg_partman. Waiting to see what final implementation will be like before adding code to handle this. - -- Data that does not match any children causes an error and is not preserved in the parent. - -- Foreign keys from partition set to other tables (FKs from other tables to partition sets not supported at all for any partition methods). - --- Removed automatic partition creation via trigger at 50% for id/serial partitioning. Was making trigger function generation code harder to maintain and is just a bad practice in general to be making child partitions on the fly with data insertion. This is also not possible with native partitioning, so rather phase out the feature now. Now must use automatic partitioning and schedule frequently enough to keep up with ingestion rate or manually call run_maintenance() via some other means to maintain new partition creation for id-based partition sets. - -- The next maintenance run of any id-partitioned table will have its function remove the 50% code-block. Unable to do it as part of the extension update since that would then make the trigger function part of the extension. - -- If you encounter any issues caused by this update and the removal of the 50% code, call create_function_id() to recreate the trigger. This function has been added to pg_partman.md for reference. - --- Changed config column & function arguments from "use_run_maintenance" to "automatic_maintenance". This makes the option clearer since manual maintenance can still use the run_maintenance() function. - -- Valid values are currently: "on", "off" - -- A value of "on" means that a call of run_maintenance() with no specific table parameter given will cause maintenance to automatically run for that partition set. - -- A value of "off" means that maintenance will not run in the above situation, but if you give a specific table parameter to run_maintenance(), maintenance can still be run manually for it. - -- Note that all partition sets in part_config have set this value to "on" as part of the 3.0.0 upgrade. Since the semantics of the column value have changed, I went with the safe option of turning maintenance for all partitions sets on vs trying to figure out what the intention of previous settings were. Review your configuration after upgrading and the new meanings for this column's value to ensure things are set up properly for your database. This is most relevant if you had this turned off to avoid automatic serial partition creation at 50%. Now that all serial partitioning requires run_maintenance(), this meaning is no longer relevant. - --- The undo_partition plpgsql functions now return both the number of partitions undone as well as the number of rows copied/moved. Result is returned as a record set, so these functions can be queried as a table to have columns/rows returned. The value of undone partitions will be -1 when an issue is encountered while running the function. - --- Improved performance if infinite_time_partitions flag is set to true. No longer obtains max value of partition set needlessly. (Github Pull Request #169) - --- Added create_partition_time(), create_partition_id(), create_function_time(), create_function_id() to documenation. Had thought they were only really useful internally, but turns out they can be pretty useful in general to fill in missing children gaps and manually fix partition trigger function. (Github Issue #161). - --- Fixed bug with upgrading from version 1.x to 2.x of pg_partman if your version of PostgreSQL had been less than 9.2 at the time the 1.6.0 version of pg_partman came out. The custom_time_partitions table was only created if PostgreSQL was >= than 9.2 since it relies on the range data type. If you then later upgraded to 9.2 or greater, the custom time table was never created and can cause issue with upgrading as well as if a custom time partition set is ever created. Since v2.x of pg_partman only works with PostgreSQL 9.4 and later, forcing this table creation as of v2.x should no longer be an issue (Github Issue #159). - --- Fixed bug in sub-partitioning that did not check for sub-partitions being equal to or larger than the top parent's interval. Also did not properly disallow weekly sub-partitions. If any of your current partition sets have weekly sub-partitions, this won't break them, but you should be aware that time boundaries will not line up properly in some cases due to the nature of ISO weeks. It is recommended that you repartition in a different pattern. - --- Fixed bug when undoing a subpartition set that did not remove the entry in the part_config_sub table. (Github Issue #168). - -- If the parent table given as an argument to the undo function had an entry in part_config_sub, it is now always removed. - -- By default, if you undo a child table that is also partitioned, it will not stop additional sibling children of the parent partition set from being subpartitioned unless that parent is also undone. - -- To handle the above situation where you may not be removing the parent but don't want any additional subpartitioned children, a new function has been added: stop_sub_partition(). - --- Fixed bug in undo_partition.py that would stop undoing the partition set if it hit a child table with zero rows. Script now returns the number of partitions undone instead of the rowcount moved. --- Also fixed bug in undo_partition.py that would mask any python errors and let script run as if nothing bad happened. - --- Fixed bug in sub-partitioning when a parent was partitioned by time and its children were partitioned by ID. In certain situations, all ID sub-sets were not getting their parent table created. - --- Removed unused p_analyze parameter from create_function_id(). - --- Began moving function description comments into the functions themselves so they're visible in the database as well. - - -2.6.4 --- Note, if you installed update 2.6.3 prior to the release of 2.6.4, this update will not change anything, but is still required to be installed to allow the extension version update chain to work. --- While upgrading to version 2.6.3 or later, if you encounter the error: "cannot ALTER TABLE "part_config_sub" because it has pending trigger events" (or for the part_config table), then you must install that update by itself first and then install the update to 2.6.4 separately in a different transaction. Changing the values of a column in the same transaction as altering it to add/remove a constraint can cause the pending triggers error. The only way around that is to do the steps as separate transactions, which this update separates out from the previous one that caused the issue. Unfortunately, all extension updates run in a single transaction unless you upgrade manually to a specific version to control the transaction state between versions. (Github Issue #167). -See the updates/pg_partman--2.6.3--2.6.4.sql file for more specific instructions on how to deal with this issue. - - -2.6.3 --- IMPORTANT NOTE: Installing the new C library requires a restart of the database cluster. Even if you are not running 9.6, it is recommended to restart after this update to ensure the latest library file is loaded into the database if you have compiled the C library. --- Fixed pg_partman background worker to work with PostgreSQL 9.6. If configured for multiple databases in a single cluster, the background worker processes will no longer start up and run in parallel. Unfortunately, changes in background worker code have made that harder to do at this time. Note it never ran in parallel on a single database before, it would only do that for multiple databases. (Github Issue #141). --- Allow option for millisecond epoch partitioning. (Github Issue #150, Also requested back in #75) - -- The "epoch" parameter to any function and the column in the part_config table has changed from boolean to text and only accepts the values: "seconds", "milliseconds", "none". - -- The default is "none". - -- Any current epoch partition sets will automatically have this value set to "seconds". - - -2.6.2 --- Refactor of time functions in relation to epoch partitioning to improve maintainability of the code. Much thanks to Michael Rasmussen (Trekoid)! (Github Pull Request #143) --- Some code cleanup for python scripts. Much thanks to Rodolphe QuiƩdeville (rodo)! (Github Pull Request #148) - - -2.6.1 --- WARNING: The pg_partman background worker is currently incompatible with PostgreSQL 9.6. It runs fine in 9.4 and 9.5. I'm still working on trying to figure out what the issue is. If you must upgrade to 9.6 before this issue is fixed, you can disable the background worker by removing the entry for it in shared_preload_libraries and restarting postgres. Instead schedule the run_maintenance() plpgsql function to run via another scheduler method (cron). - --- NOTE: A restart of the database is required to put the background worker code changes in place once the new library file is installed. - --- Add p_analyze parameter to partition_data_id() and partition_data_time() functions to allow moving data out of the parent to be faster if an analyze is not required after each child table is created. If set to false, highly recommended that an analyze on the entire partitions set is run after moving data. (Github Pull Request #137) - -- Also allows better control of automatic analyzing of new partitions for serial partition sets by allowing the p_analyze parameter to run_maintenance() to be passed further down the function call hierarchy. - -- Updated partition_data.py to pass this parameter as FALSE when calling these functions. The script does a VACUUM ANALYZE when it completes all batches anyway, so an analyze per child table is not necessary. --- Improve performance during privilege inheritance when creating new partitions or when running reapply_privileges() function. Most noticable on databases with many objects (Github Issue #139). --- Fixed buffer overflow in background worker code if database name was too long (Github Pull Request #140). --- Fixed bug in background worker code that could cause improper initialization of dynamic bgw processes (Github Pull Request #140). --- Account for non-lowercase schemename for pg_partman's schema in background worker (Github PullRequest #140). - - -2.6.0 --- New function to aid in column removal: drop_partition_column(). - -- Depending on when a column was added (before or after setting up inheritance for partitioning), dropping it on the parent may or may not drop the column from all children. - -- Using this function will ensure the column is always dropped. - -- Uses IF EXISTS clause to cleanly work even if column is missing from children already. Will give off warnings if column doesn't exist, but not return errors. - -- This function can work for any inheritance set to drop columns, not just those maintained by pg_partman. - -- Issue mentioned in blog post by Andrew Dunstan and also borrowed code from there for this function - http://adpgtech.blogspot.com/2015/07/dropping-columns-on-partitioned-tables.html --- Added new configuration option to control if the partitioning trigger returns NULL or NEW. Default is to return NULL. See documentation for further use of this option (Github Issue #128 & Pull Request #127). - -- New parameter to create_parent() & create_sub_parent() so be sure to account for that in existing function calls --- Account for all new config columns when checking subpartition configuration consistency. If you suddenly start getting errors about subpartition configuration mismatches, ensure all sibling subpartition parents are configured exactly the same. See error returned by this check for more details on how to fix this. --- Added --version option to vacuum_maintenance.py - - -2.5.1 --- Fix check for ensuring upsert feature can only be used in 9.5 in create_parent() & create_sub_parent(). Was causing erroneous failures with versions prior to 9.5. Thanks to Death_Syn & ilmari on #postgresql freenode for reporting the issue. - - -2.5.0 --- Added very limited support for INSERT ... ON CONFLICT (upsert) in the partitioning trigger. For situations where only new data is being inserted, this can provide significant performance improvements. However, major limitations are that the constraint violations that would trigger the ON CONFLICT clause only occur on a per child table basis. This is a known limitation for inheritance in general. Constraints DO NOT apply across all tables in an inheritance set (Ex. Primary keys are only enforced for each individual child table and not for all tables in the partition set. Data duplication is possible). The included python script "check_unique_constraint.py" can help mitigate duplication, but cannot prevent it. Of a larger concern is an ON CONFLICT DO UPDATE clause which may not fire and cause wildly inconsistent data if not accounted for. These limitations will likely never be overcome in this extension until global indexes or constraints for inheritance sets are supported in PostgreSQL. It is recommended you test this feature out extensively before implementing in production and monitor it carefully. Many thanks to MikaelUlvesjo for contributing work on this issue (Github Issue #105 & Pull Request #122). --- Added check to part_config_sub to ensure premake > 0. Makes it consistent with part_config table. --- Allow internal check_version() function to work with test releases of postgres. If it's an alpha, beta or rc release it ignores the current version, so you're on your own if things fail due to version feature mismatches. - - -2.4.1 --- Fixed partition_data_id() function to allow intervals larger than a 32-bit integer (bigint) (Github Pull Request #125). --- Fixed vacuum_maintenance.py to work with timestamptz now that all extension time data types were fixed in v2.4.0 --- Fixed pgtap failure in postgres 9.5 for id subpartitioning test (Github Issue #82). - - -2.4.0 --- IMPORTANT NOTICE: As of this version (2.4.0) support for the 1.x series has been dropped. No further fixes (bugs, features, etc) will be backported and 1.8.8 will be the last version in the 1.x series. If you encounter a bug, the only supported method of fixing it will be to upgrade to the 2.x series where it may already have been fixed or you can submit an Issue on Github. --- Change data type of time-based variables in many functions from "timestamp without time zone (timestamp)" to "timestamp with timezone (timestamptz)". Previously there was a mix of both being used and was causing inconsistencies on timezone edge cases. This was also affecting the data type used when creating the constraints on child tables (they may have had improper timezone info). This should allow constraints on child tables to work more consistently whether the partition column is either timestamp or timestamptz (Github Issue #118). Note this will not recreate the constraints on existing child tables, but if you weren't having any issues before, it shouldn't cause any problems. If you need to have the constraints fixed, you can either do it manually for all existing child tables or unpartition and repartition the set again. - -- If you're concerned about this causing issues, you can set the "trigger_exception_handling" configuration option to true which causes any errors encountered when inserting data to child tables to put the data into the parent. Note this will override any other exception handling that uses these tables, but should ensure no data goes missing for normal writes. --- Allow constraint exclusion to work better with epoch partitioning. Previously the only constraint was based around the time value of the epoch column and constraint exclusion would not work if the integer value was queried. This adds an additional constraint to the child tables so that exclusion should work for both time & integer based queries when epoch partitioning is used.(Github Issue #108). --- Fixed bug where if any time-custom partitions were set up, their configuration could possibly be used for any time based partition set (Github Pull Request #120). - - -2.3.4 --- IMPORTANT NOTICE: The next minor version (2.4.0) will officially drop support for the 1.x series. No further fixes (bugs, features, etc) will be backported and 1.8.8 will likely be the last version in the 1.x series. If you encounter a bug, the only supported method of fixing it will be to upgrade to the 2.x series where it may already have been fixed or you can submit an Issue on Github. - --- Properly handle SECURITY DEFINER functions in a safe manner by resetting search_path as recommended in the PG docs (http://www.postgresql.org/docs/current/static/sql-createfunction.html#SQL-CREATEFUNCTION-SECURITY) (Github Pull Request #108). --- Made the exception handling in the partitioning trigger introduced in v2.3.3 optional. This was causing unexpected behavior whenever an exception was being handled by some other external process when it used a partitioned table. The trigger exception handling would take over and not allow the external process to handle it. New configuration option "trigger_exception_handling" has been added to the part_config (and part_config_sub) table to enable/disable this feature. It has been set to false by default to avoid unexpected default behavior which means any partition sets that had a trigger created by v2.3.3 will have the exception handling removed the next time it is updated. If you'd like to keep this feature, set the config table column to true. --- Added parameter p_exact_count to check_parent() to allow faster checks for data in parent table. If set to false, will only look for a single row existing in the parent instead of trying to get an exact count which could take a long time if a lot of data is in the parent (Github Pull Request #114). --- Further optimization to catalog lookups to improve performance on larger databases (Github Issue #107). - - -2.3.3 --- Fixed bug in partition_data_time() that was causing it to fail with custom time partition sets. This bug was introduced in v2.3.2. --- New constraint on partman config tables does not allow constraint_cols column to contain the control column. If you get this constraint volation during extension update, please review your part_config (and part_config_sub) table for this condition. You also likely have additional, unnecessary constraints on your child partitions that should be reviewed and removed. (Github Issue #100). --- Further optimizations for databases with many tables and large partition sets. Thanks to https://github.com/Crack for the contributions here (Github Issue #107). --- If an error is encountered when the partition trigger is run, pg_partman's trigger function now catches the exception and writes the row to the parent instead of just throwing an error. Reduces chance of losing data when encountering issues. Gives a WARNING in the log when this happens along with the original error that caused the issue. Thanks to David Turon for this suggestion (https://github.com/dturon/pg_partman/blob/lbox-trigger-support/sql/functions/create_function_id.sql#L192). - -- Note this feature will not go into affect immediately upon extension update. It requires recreating the trigger function, which occurs when a new child table is created. You can force this by running create_function_id(parent_table) or create_function_time(parent_table). - - -2.3.2 --- Fixed issue where additional constraints would not get applied on all older partitions if more than one new partition was made for a single partition set in a single maintenance run. You can fix this by using the reapply_constraints.py script to remove then reapply all additional constraints on older tables. Or if you know the specific children that are missing a constraint, you can run apply_constraints() and pass it the child table argument. - --- Improved performance of create_parent() when p_start_partition is set. Was previously doing a full seq scan of the parent table which could keep it locked longer than intended (Github Pull Reqest #99). - --- New function that returns partition data about a given child name: show_partition_info(). - -- Returns start/end values that are contained in a given child table. - -- Can pass an optional interval value to see start/end values that are different than the currently configured interval. - --- Added new options to vacuum_maintenance.py script - -- New --all option tells it to run against all tables managed by pg_partman. - -- New --type option sets whether to run against time or id based partition sets managed by pg_partman. - -- New --interval option tells the script to only run against child tables older than the given interval. See the script's --help for more info on how this works. - -- All these options only work on partition sets managed by pg_partman. If none of these options are set, script still works on non-pg_partman inheritance sets. - --- Disallow weekly partitioning to be a subpartition of anything else. Alignment of ISO weeks does not always match up with larger intervals and can cause constraint conflicts. --- Also disallow subpartition interval to be equal to or greater than parent. --- Set part_config_sub & custom_time_partition config tables to dump their contents with pg_dump to ensure all config data is saved. - - -2.3.1 --- Fixed typo in standalone create_parent.sql file that caused a brand new installation of v2.3.0 to fail (Github Issue #94). --- Added --noparent option to new vacuum_maintenance.py script to allow parent table to be excluded from the maintenance run. --- Added vacuum_maintenance.py script to documentation. - - -2.3.0 --- New migration documentation (doc/migration.md) to provide assistance changing an existing partition set to be used by pg_partman. Note that pg_partman does not support having child table names that do not comply with its naming convention. - --- Added new configuration option to configuration table for time-based partitioning: infinite_time_partitions (Github Pull Request 84) - -- By default, if no new data is being inserted, no new partitions are created. This keeps pg_partman from creating new, empty tables infinitely when data insertion has stopped. - -- Setting this value to true in part_config or part_config_sub will tell pg_partman to continue making partitions forever with time-based partitioning, even if there's no new data - -- Note that serial/id partition is not affected by this and will only make new partitions if new data is being inserted. - --- Implemented the clustering work done in fork by https://github.com/dturon/pg_partman - -- If a parent table is set to cluster on a specific index, that clustering property will be inherited to all children. - -- This does NOT actually cluster the table data, it only inherits the cluster property so that if/when CLUSTER is run on the partition set, it will use a default. - -- Note that FILLFACTOR is not inherited at this time, so adjust your re-cluster schedule accordingly. - --- Optimized catalog lookups throughout many functions to improve performance on databases with many tables (Github Issue 87). - -- For custom time partition sets, their trigger function has had this optimization applied (normal time & id did not have this issue). The next time a child partition is created, the new function will be put in place. If you need it immediately, run: create_function_time('parent_table'). - --- Changed the "optimize_constraints" config option to calculate the "old" partition based on what the current partition is instead of the data contained in the partition set. For time series with the new infinite option above set, this means the "current" partition is always moving forward despite no new data. This returns it to the behaviour it had before v2.2.0. The reapply_constraints.py script had not been updated properly to account for the new config option and was not calculating the "old" tables properly at all (it was looking for tables much further in the past than intended) (Github Issue #89). - --- New python script to provide additional VACUUM maintenance to help avoid needless vacuums and transaction id wraparound issues: vacuum_maintenance.py - -- The script checks the age(relfrozenid) value of all tables in a partition set (parent & children) and runs a VACUUM on them if they are higher than vacuum_freeze_min_age. - -- Passing the --freeze option will run a VACUUM FREEZE on all tables in a set that match the above criteria. - -- Passing the --full option does the same as above, but runs VACUUM FULL instead. Note this will lock all tables it is vacuuming, possibly even the parent, so review the --dryrun list before running it. - -- An explanation for the necessity of using this script is far beyond what can be written in a CHANGELOG or the script help itself. Google is your friend. Basically if you have old, static child tables that will never be dropped, it is highly recommend you run this occasionally with the --freeze option to avoid future database maintenance issues. - --- Pulled in bugfix for using the intarray contrib extension in conjunction with constraint management (Github Pull Request #88). --- Pulled in bugfix for using bigint instead of int type for variable in apply_constraints() (Github Pull Request #90). --- Fixed bug that may have caused serial trigger function to not contain all optimized child table conditions. No write failures would have occurred, just slower than expected performance. - --- Fixed bug in partition_data.py & undo_partition.py that would cause either to throw an error and then not reset autovacuum if script was interrupted (Ctrl+C) (Github Issue #92). - -- They also now properly handle SIGTERM to reset autovac as well. - -- Note that disabling/enabling autovac now requires an extra available connection to the database. - - -2.2.3 --- Fixed bug in partition_data_time() that would cause an infinite loop when moving data would require creating a new partition newer than the newest one. This would only occur when using a custom time interval. Infinite loop would also occur when using the partition_data.py python script (Github Issue #83). --- Properly handle the special PUBLIC role when granting/revoking privileges on child tables (Github Issue #66). --- Fixed python exception in reapply_indexes.py if the parent table has no indexes (Github Issue #86). --- Fixed all undo_partition functions to remove the child table in the same batch session when its rowcount reaches zero. Previously an extra batch may have been required to remove all child tables and remove config information after the last row was moved to the parent. --- Fixed undo_partition functions to be more strict on the triggers they drop and ensure they only drop the trigger on the target parent table. --- Fixed undo_partition() to properly remove entries from the custom_time_partitions config table if necessary. Also greatly simplified code in this function. --- Consolidated privilege management into new apply_privileges() internal function. --- Improved performance when applying parent privileges to child tables. Most noticable on larger partition sets with many grants. (Github Issue #78) --- Added a sort to the Makefile when creating the sql extension file. Allows more predictable output between builds (Github Push Request #16 from Mimeo extension). - - -2.2.2 --- Fixed infinite loop in reapply_indexes.py if the same index name would have been used more than once. Thanks to bougyman for tha bug report. --- Fixed indexes being applied to the parent table instead of the children in reapply_indexes.py (Github Push Request #73). - - -2.2.1 --- Fixed search path bug with run_maintenance() introduced in v2.2.0. Was causing errors if the pg_partman schema was not in the search path of the role calling it. (Github Issue #71) - - -2.2.0 --- Added new optimization & constraint configuration options to part_config & part_config_sub (optimize_trigger, optimize_constraint) (Github Issue #62) - -- The premake config value now only controls what its name suggests (the number of future partitions to premake) - -- For existing partition sets, the values for these columns will be set equal to the current premake value - -- Default value for optimize_trigger is same as default for premake (4) - -- Default value for optimize_constraint is 30 (30 days, 30 months, etc) - -- Not added as parameters to create_parent() or create_sub_parent() - --These can be changed later and normal maintenance will take care of updating the current partition set to match. - --- Time epoch partitioning is now possible (Github Issue #44) - -- Control column can be an integer type but trigger, partition constraints and partition names will be based on a time interval. - -- New boolean parameter to the create_parent()/create_sub_parent() functions. Ensure all of your current calls to this function account for it! - --- Fixed bug introduced in v2.1.0 that caused run_maintenance() to not catch up if partition creation fell behind. (Github Issue #67) - -- The fix for this issue also introduces new (improved) behavior for time-based partition maintenance. - -- Previously, the premade tables were determined by the current time at the time maintenance ran. - -- It is now instead based off of the maximum value in the partition set. - -- This is essentially the same behavior that serial partitioning has always had. - -- This means that if no new data is being inserted, once the premake value is met, no new partitions will be made. Previously they would be created no matter what, indefinitely. - -- Resumption of data insertion after a large gap in data and maintenance runs may cause data to go into the parent until run_maintenance() is called again. - -- New data should then go to new children, but data will then have to be cleaned out of the parent. - -- This also means partitioning should work more predictably if data with a future timestamp is inserted. Previously this was not accounted for and could cause maintenance to work inconsistently. - -- KNOWN BUGS: If you insert "future" data in a time-based, sub-partitioned set, maintenance may skip child tables in some partition sets depending how that data relates to the subpartition interval. - -- Working on a fix for this, but didn't want to hold this release up anymore. To work around this, - -- Increase the premake and/or optimize_trigger values appropriately to account for your normal data insertion window. - -- Monitor for data going into parent tables and fix that as documented. - --- Fixed bug in show_partitions() when used against a quarterly partitioned set. Wouldn't return all tables in expected order. --- Fixed bugs in reapply_constraints.py instroduced in v2.1.0 that prevented it from running. Increased minimum version requirement of pg_partman to 2.2.0. --- Made creation of additional column constraints more efficient (if used) and less spammy in the run_maintenance() jobmon logs. --- Added debug option to run_maintenance(). - --- Fixed bug introduced in 2.1.0 that wasn't setting the retention config columns in part_config properly for multi-level subpartitions - -- You may encounter an error during maintenance or during the upgrade related to inconsistent data in the part_config_sub table. There was a constraint in place before that tried to prevent this happening, but it was not doing it properly. There is now a new function in place that is part of routine maintenance that checks for this consistency. This checks to ensure that all sub-partition parents in part_config_sub that are themselves part of a sub-partition set have the same configuration values. You can run the function "check_subpart_sameconfig('parent_table')" for all sub-partitioned table sets to see which ones have inconsistent data. The function should only return a single row. If more than one row is returned then just update the config entries that are mismatched and this should clear the error. If you're still having problems, please open an Issue on Github with the error you receive and the full contents of your part_config & part_config_sub tables. --- If you encounter the error during the upgrade, run the query found in the 2.1.0 -> 2.2.0 upgrade file comments at the top using the parent table returned in the error for <<>> and setting <<>> to the schema you installed pg_partman to. Only a single row should be returned. If not then fix the mismatched data. - - -2.1.0 --- Object names (tables, roles, etc) with special characters & mixed case are now supported. --- Fixed bug in apply_foreign_keys() that was causing it to fail if there were multiple FKs to the same foreign column. Also fixed a bug that allowed the child name parameter to not be passed and didn't handle it if it wasn't. Thanks to Andrew Dunstan for the bug fix and tremendously simplified code. Backpatched to 1.8.8. (Github Issue #64). --- Ensure trigger function is recreated if a partition is dropped as part of retention. This makes sure the tables explicitily listed actually exist. Backpatched to 1.8.8 (Github Issue #62). --- Fixed bug in check_unique_constraint.py to properly inspect all data from an entire inheritance tree. If you were using this before to check for duplicates across children in an inheritance set, it is highly recommended you run it again as it may not have caught all dupes. --- New function 'show_partition_name()' can tell you the child table name that a given value would exist in given a parent table that pg_partman manages. The name will always be returned whether the child table exists or not. Another boolean column is returned that tells you whether the child table actually does exist. It also returns the raw value (timestamp or integer) for the suffix of the returned partition name (used internally, but could be useful elsewhere too). Thanks to Corey Huinker for idea & assistance. --- show_partitions() function now returns the schema and table name as two separate fields. --- New column in part_config to denote when a sub-partition set has had its final child partition made (sub_partition_set_full). Allows run_maintenance() to skip over it and run more efficiently when managing many sub-partition sets. --- Updated all python scripts to handle mixed-case & special characters. --- Bumped all minimum pg_partman version requirements for python scripts to 2.0.0. May still work on the older versions, but not officially supported or guaranteed anymore. --- New --nonpartman option to the reapply_indexes.py and reapply_foreign_keys.py scripts to allow them to work better with partition sets not managed by pg_partman and to allow those that are to work more efficiently. --- Greatly reduced number of individual pg_jobmon jobs generated. Steps such as creating partition functions, applying foreign keys, and applying additional constraints have been combined into the job log entries for creating partitions or general maintenance when applicable. --- Internal function check_name_length() no longer takes a schemaname argument, nor returns the schema as part of the modified object name. --- Simplified internal code for handling time suffix name generation --- Much more extensive pgTAP testing suites to handle more edge cases - --- NOTE: If you are running version 1.8.7 and need to update to 1.8.8 do the following steps: - -- Download the latest version as normal. - -- Copy the file "updates/pg_partman--1.8.7--1.8.8.sql" to the folder where your extension SQL files are kept (depends on your OS or where you manually installed postgres). - -- If you're running from less than 1.8.7, copy whichever prior update files you need to get from your version to 1.8.8. They are all kept in the updates folder. - -- While logged into postgres run: ALTER EXTENSION pg_partman UPDATE TO '1.8.8'; - -- The version number at the end of the above command is important. Otherwise it may try and update you to the latest 2.x version if you copied more update files then necessary or ran "make install". - - -2.0.0 --- IMPORTANT NOTE: This version, and all future versions of pg_partman >= 2.0.0, are only compatible with PostgreSQL 9.4 and greater. - -- I do not have any current plans for development on the v1.x.x branch anymore. Reported bugs will be fixed, but all development will only be on the 2.x.x from here on out. Bug fixes will also only be done while versions of PostgreSQL older than 9.4 are themselves officially supported. If a bug only exists in an unsupported version of PostgreSQL, it WILL NOT be fixed. And once PostgreSQL 9.3 goes EOL, the 1.8 series will be deprecated entirely. - -- No new features, API changes, configuration options or major internal structure changes will be accepted for the 1.x.x series. These will be locked to the 1.8.x series. No future 1.x.x versions after 1.8.7 will be able to be installed directly. You will have to install 1.8.7 (tagged version releases are available on github) and then install the updates found in the "updates" folder of later versions to get to the latest 1.x.x version. I unfortunately don't have time to be maintaining two independent branches with the way extension versions work now in PostgreSQL. Even fixing bugs in the 1.8.x series will be troublesome since I then have to redo every 1.8.x -> 2.x.x update file every time to ensure all fixes are applied no matter the upgrade path. This will unfortunately break managing the 1.x.x series via PGXN. I'm sorry but I don't see any other way around this with the way extension versions are managed right now. I highly recommend updating your database to 9.4+ if you wish to easily maintain this extension going forward. There's tons of other great new features and performance improvements, so it's definitely worth the effort! - --- A background worker process (BGW) has been added to pg_partman for general partition maintenance. - -- A separate scheduler is no longer required in most cases if you compile pg_partman with the background worker option. - -- New postgresql.conf configuration options are listed in the documentation. You must at minimum give the database names you want the BGW to run for. The rest have defaults if not set. Only a reload is required to change values once the database is started with the BGW running. - -- The BGW can only be started at cluster start. This may change later, but it is done this way for initial simplicity and with future design ideas in mind. - --- There are no longer distinct "static" and "dynamic" partitioning modes. The features of each mode have been combined into a single trigger format. - -- All triggers now have the static INSERT statements for the premake window first followed by a fallback, dynamic option to insert to the child table if it exists. - -- Whenever the next child partition is created for a partition set, its function will automatically be updated to the new version. If you'd like to update all partition sets immediately, see the code following these release notes in the update file. I cannot make this automatically part of the update because doing so would cause all the new trigger functions that are created to be part of the extension. After updating, all old "static" sets will still work as they did before, but will have an additional fallback case to handle inserts to child tables outside the current premake value if the child table exists. All old "dynamic" sets will now have the "static" conditions added before the dynamic portion and you should see a performance improvement for any data inserted within the premake window. As before, any data that is inserted that has no associated child table will go to the parent. - -- The "time-custom" partition type still exists and has not changed for those that need an interval other than the predefined ones. It still uses a lookup table and sacrifices performance for flexibility. - -- Eliminated nearly half of the existing pgtap tests due to this change (but added some new ones). - -- See my blog post for the explanation of what "static" and "dynamic" meant. http://www.keithf4.com/postgresql-partition-manager - --- If retention system is turned on, jobmon no longer logs entries if no retention work was actually done. Would previously just log that zero tables were dropped. If anything is dropped/uninherited, it will be logged as expected. --- Changed column "type" in part_config to "partition_type". "type" is a reserved word, but not currently strictly enforced (doesn't require double-quoting). This avoids any possible future issues. Also changed sub_type in part_config_sub to "sub_partition_type" for consistency --- Changed column "part_interval" in part_config & part_config_sub to "partition_interval" to be more consistent with above renamed column. --- Now uses new, more extensive GET STACKED DIAGNOSTIC feature added in 9.2 to provide more detailed errors when an exception is encountered. Previously when functions called other functions and a custom exception block was used, only the latest function called would report the error. Now a more full stack trace is available to see the original function that caused the error. --- Thanks to https://github.com/IMSoP for the extensive documentation formatting improvements. - - -1.8.7 --- This update has no core code changes. It is being released to give users that ran into a situation where the trigger functions for serial partition sets called a function that was renamed. In v1.8.0 create_id_partition() was renamed to create_partition_id(). All core code was updated to use this new function, but any existing trigger function from previous versions may have the old function name in the code that creates new partitions when the current one reaches 50% of the max. --- The code block contained in "updates/pg_partman-1.8.6--1.8.7.sql" will go through and recreate all the trigger functions on all partition sets managed by pg_partman. This should fix the issue described above. While only serial partition sets should have been affected, the code below does it for both time & id to just ensure everything is fixed. --- This code MUST must be run manually because if the trigger functions are recreated as part of an extension update, then those trigger functions become members of the pg_partman extension itself. Just copy-n-paste it into psql and things should be good to go. --- Thanks to Jan Lentfer for finding this bug during v2.0.0 testing. - - -1.8.6 --- Fixed bug with run_maintenance() and subpartitioning. If you had any subpartitioned sets that went back a significant amount of time, or had a significant number of subpartitions, calls to run_maintenance() would seem to hang. It would finish eventually, but could take quite a long time and if you have pg_jobmon installed, would also cause many entries mentioning old partition sets. This bug was introduced with the fixed included in v1.8.5. - - -1.8.5 --- If run_maintenance() had not been called for a while outside of a partition set's interval, running it again was not creating the necessary child partitions to catch up. Recovery from this scenario was possible if the latest partition was manually created. This update fixes the problem and if you have a partition set that was not catching up before, running it with this version installed should fix things with no further manual intervention. This bug was introduced with version 1.8.0. (Github Issue #56). - - -1.8.4 --- When inheriting foreign keys to children, also account for the following additional options: - -- MATCH FULL/PARTIAL/SIMPLE - -- ON UPDATE/DELETE NO ACTION/RESTRICT/CASCADE/SET NULL/SET DEFAULT - -- DEFERRABLE / NOT DEFERRABLE - -- INITIALLY IMMEDIATE / INITIALLY DEFERRED , when it is DEFERRABLE, MATCH, & CASCADE actions. --- Note that none of the above properties were being inherited to child tables before. If you need to reapply foreign keys on children to enforce these options, see the reapply_foreign_keys.py python script or apply_foreign_keys() plpgsql function. The script is the preferred method to avoid contentions. --- reapply_foreign_keys.py claimed it could work on partition sets not managed by pg_partman, but that wasn't true. Removed dependency on show_partitions() function, so now that is true. - - -1.8.3 --- Fix both the retention system and the undo partitioning functions/scripts not cleaning up the custom_time_partitions table when using a custom time interval (Github Issue #49). --- When using sub-partitioning, the call to create_parent() that is within create_partition_id() & create_partition_time() was passing 2 of the parameters through incorrectly. p_use_run_maintenance was being fed to p_inherit_fk and vice versa, so the inherit_fk and use_run_maintenance columns in part_config & part_config_sub may be reversed. Since these are both boolean parameters, no error was being raised. If you've used sub-partitioning and used anything other than the default values for either of these configuration options, please double-check the part_config & part_config_sub tables to ensure the proper values are there. If you did not set them specifically, the default values were set for both and things should be fine. - -- If p_use_run_maintenance was set wrong, you likely noticed that new partitions were not getting created for new sub-partition sets. You'll still have to fix any existing config settings, but future ones shoould be fine now. - -- If p_inherit_fk was set wrong, child tables were likely not inheriting FKs or they were inheriting them when you didn't want them to. Again, fix this for existing partition sets by correcting the config table and all future sub-partitions should now be set properly. If you need to generate FKs on child tables that were missing them, you can use the reapply_foreign_keys.py script. - - -1.8.2 --- Fixed a bug in sub-partitioning that would cause child tables outside of the time boundaries of the parent partitions to be created when using time->time sub-partitioning. A user encountered the error when doing weekly->daily subpartitioning, but it was possible it could have happened in other interval combinations I had not tested as well (Github Issue #47). --- Updated reapply_indexes.py script to, by default, only add new indexes and drop ones that don't exist on the parent. Previously it would drop all indexes on all children and recreate them to match the parent. Now it only does the minimal amount of work to make the children match the parent. An additional option (--recreate_all/-R) was added to allow the old behavior of redoing all indexes from scratch if desired (Github Issue #41) --- Changed the minimal interval that serial partitioning can be done to 10. Ran into issues with an interval of 2 and partitioning anything this low is unrealistic and provides no benefit. 10 seems like a reasonable minimal to have at this point to avoid any future issues. This does not affect any existing partition sets, only newly created ones (Github Issue #39). --- Serial partition maintenance when using run_maintenance() is now much more efficient. Should run significantly faster for very large partition sets. --- Fixed bug in subpartition creation when using "time-custom" partitioning type. May have created subpartition child tables that were outside the time boundaries of the parent partitions. --- Fixed bug in additional constraint management when using "time-custom" partitioning type. May not have always added additional constraints on old child tables. --- Added constraint on part_config_sub to ensure valid partition types. - - -1.8.1 --- The p_analyze parameter to the apply_constraint() function is now FALSE by default instead of TRUE. This makes it so that by default, an analyze is only run by the create_partition_id/time() functions upon new partition creation. The parameter was left in apply_contraints() in case someone needs to call it directly and ensure an analyze is run so statistics are updated. (Github Issue #45) - -- Note: If using reapply_constraints.py, an ANALYZE is always done at the end of the script. It was like that before this update. --- Changed the manner in which new partition creation is logged in pg_jobmon. Previously, each individual child table creation was logged as its own, separate job entry in pg_jobmon. Now, if multiple child partitions are created for a single partition set, all those child tables are logged as steps for a single job log entry. This now allows the analyze step (if it is done) to be logged as well in pg_jobmon and allows for easier diagnosis if this if holding up partition maintenance. - - -1.8.0 --- PG Partman now supports sub-partitioning. This allows automatic configuration to turn the child tables of an existing partition set into parent tables of their own partition sets. (Github Issue #26) - -- New function "create_sub_parent()" works exactly like "create_parent()", even taking similar parameters. Instead, the parent_table you're giving it as a parameter is telling it which parent's child tables to partition and how to partition them. - -- This can be chained down as many levels as desired. Just recall the 63 character limit on object names since this will be adding a new partition suffix every level down. The final suffix is always guarenteed to be added on in full, but the parent suffix name may get truncated off. - -- Due to logical complexity (and possible contention issues at larger data sizes), when using subpartitioning, all parent tables at all partition levels are set to use run_maintenance() by default. This includes serial partitioning which normally by default can use a trigger based method to create future partitions. You can still set it to false so you can force maintenance to run at specific times (see new run_maintenance() feature below), but you MUST force it to run at some point otherwise new partitions will never be made. - -- Note that there will ALWAYS be at least one child partition created, even for subpartition parents that are outside the current trigger range. Data outside the currently covered trigger range will still be inserted the the relevant parent. - -- Note that for retention policies, whatever retention period is set on the highest level will be honored and ALL child tables will be dropped, cascading all the way down to the bottom. Use this option even more carefully! - --- New parent table name parameter to run_maintenance(). If set, skips all other tables for that maintenance run and only does the one given. (Github Issue #32) - -- This is an optional parameter, so should not affect any existing use of the function. When not given, maintenance is run for all partition sets set to use it in the part_config table. - -- The already existing configuration option in part_config (use_run_maintenance) can be used to tell run_maintenance() to skip any partition sets for which you do not want it to run when no table name parameter is given. You can then schedule partition maintenance for specific tables to run at specific times using the new argument to run_maintenance(). Note that if a parent table argument is explicitely given to run_maintenance, it will always run the maintenance for it no matter what the configuration table has set. - -- Note that when a table argument is given to run_maintenance(), retention settings will only be run for that one specific table given (if configured). - -- Be aware that the "use_run_maintenance" configuration option is always set to true for time-based partitioning & subpartition sets and set false for serial based partitioning (when not subpartitioned) when calling create_parent() or create_sub_parent(). Adjust this configuration setting accordingly so run_maintenance() does what you require after you create your partition sets. - -- The trigger constraint on the **part_config** table that would not allow "use_run_maintenance" to be set to false for time based partitioning has been removed. - --- New analyze parameter to run_maintenance(). - -- Defaults to true so that if any partition set has a new child table created, an analyze is run on that whole partition set. This is to ensure constraint exclusion works properly. - -- Large partition sets were causing run_maintenance() to take a long time to run since the analyze would hold it up. This could cause some contention. - -- Setting p_analyze to false will cause the analyze to not run for ALL partition sets that are eligible for new partition creation or retention management at the time it is called. - -- If you set this to false, it is advised that you have some other means to ensure a regular analyze is being run on your partition sets. - -- NOTE this parameter is set as the second argument since it's likely to be more commonly used, so make sure to check any current run_maintenence() calls to account for this (previously p_jobmon was the second parameter). - --- Analyze is no longer automatically run on the parent table after create_parent() is run. Since create_parent() takes an exclusive lock on the parent table during setup, tables that already had a lot of existing data where being locked for the length of the analyze run, which could be quite long. When data is partitioned out later, analyze is automatically run. Also, whenever new partitions are created in the future, an analyze will be run as well (if the p_analyze argument to run_maintenance() is true which it is by default). Both those cases should take care of updating the planner statistics when it begins to matter. Run an analyze on the parent table after setup if you want to be sure. --- Fixed bug in show_partitions() that caused an error when the values in the control column of a serial partition set were larger than the max int value. This would also cause errors when partitioning existing data with values that high since the partitioning functions use show_partitions() internally. (Reported by S. Kristensen) --- create_parent() and new create_sub_parent() now return a boolean value to determine whether they succeeded. --- For all pythons scripts, changed the --connection default to "host=" instead of "host=localhost". This makes the default connection to the database use the local socket instead of TCP. Makes it act more predictibly like all other postgres executables (psql, pg_dump, etc). Please check any that you many have scheduled to run to ensure they are still working properly. --- Added a --version argument to all python scripts. This tells you the minimum version of pg_partman this script is meant to work with. --- Made sure all scripts in bin folder are added to Makefile for installation. --- Make sure autovacuum is reset if SIGINT (Ctrl+C) is fired when using partition_data.py or undo_partition.py. --- Added howto.md file to doc folder with some more extensive examples. --- last_partition column in part_config table no longer in use. Dropped it. --- Renamed internal functions create_id_partition(), create_id_function(), create_time_partition() & create_time_function() to create_partition_id(), create_function_id(), create_partition_time() & create_function_time() respectively. This gives all functions a consistent naming pattern. - - -1.7.2 --- Fixed bug in apply_foreign_keys() where new partition creation would fail when the partition set's schema is in the current search_path. Most commonly happened when partition sets with foreign keys were in public, but any schema in current search_path would cause this to manifest. Reported by Isaƭas SƔnchez via my blog. (Github Issue #27) --- Foreign key inheritance is now optional since more complex FK relationships may not work ideally with pg_partman's default method. New configuration option in part_config table and parameter to create_parent(). - - -1.7.1 --- Foreign keys placed on the parent table are now inherited to child tables. - -- Any new partitions created after this update is installed will have the FKs applied to children. - -- Existing child partitions will not have FKs applied. See the reapply_foreign_keys.py python script to reapply FKs to all child tables. - -- The new apply_foreign_keys() function & reapply_foreign_keys.py script can be applied to any table inheritance set, not just the ones managed by pg_partman. - -- See blog post for some more information about partitioning & foreign keys - http://www.keithf4.com/table-partitioning-and-foreign-keys --- Unlogged table property on parent table is now automatically inherited to child tables. Note this only applies to newly created child partitions after this update is installed. --- Added lockwait options to the undo partition functions (plpgsql & python) --- Added the same autovacuum feature & option to undo_partition.py that partition_data.py has. --- Made python scripts compatible with python 3 --- PgTAP tests for unlogged tables and FK inheritance - - -1.7.0 --- New configuration option to allow serial partitioning to use run_maintenance() instead of creating next partition via trigger. - -- Use "p_use_run_maintenance" argument to create_parent() to set this during partition creation. - -- part_config table has new boolean column "use_run_maintenance" - -- Serial/ID based partitioning defaults to FALSE. This means serial partitioning uses the parent partition trigger function to make new child partitions when the current one reaches 50% of its configured capacity (the same way it used to work). If set to TRUE, then you must schedule run_maintenance() to run often enough to keep up with your insertion rate. Otherwise rows will get inserted to the parent table. - -- Time based partitioning defaults to TRUE and config values for using run_maintenance cannot be set to false. All time-based partitioning still requires run_maintenance() for creating new child tables. - -- Existing partition sets have their config table values set to the defaults above. - -- If you'd like to change an existing serial partition set to use run_maintenance instead of the trigger, update the "use_run_maintenance" column in part_config to set it to TRUE for that parent table. You must then run the "create_id_function()" function giving it a parameter of the schema qualified parent table of the set. This will remove the code in the trigger that automatically makes new child tables. - -- Ex: SELECT partman.create_id_function('parent_schema.parent_table'); --- reapply_indexes.py can now handle too long or duplicate index names. Please see docs for how this is handled since it can change index naming patterns (Github Issue #21). --- Fixed partition_data_id() & partition_data_time() to properly return the number of rows moved when the parent table is empty before the batch limit is reached (Github Issue #22). --- Fixed creation of new child partition tables not working when parent tables had OIDs turned on. (Github Issue #20) --- Fixed check_unique_constraint.py to avoid index scans and check underlying table data. Option added to try and allow index scans if desired. --- Fixed reapply_constraints.py & reapply_indexes.py to properly run jobs in parallel. --- Ensure an analyze is run on parent table of a set after any child table is created so that constraint exclusion works properly for all child tables. --- Ensure an analyze is run on a child table whenever additional column constraints are automatically added. Also analyze partition set if reapply_constraints.py is run. --- Added pgtap tests that ensure the partitioning functions are returning the proper number of rows. --- Added pgtap tests for new features in reapply_index.py - - -1.6.1 --- The python partitioning script now turns off autovacuum on the entire partition set while it is running. This should help reduce load since it will prevent the autovacuum daemon from kicking off while data is being migrated. When the script is done running, the default value for autovacuum is restored to all tables in the partition set. Also, VACUUM ANALYZE is run on the parent table when all data has finished moving as well. There is an option to disable the turning off of autovacuum if the ALTER TABLE statements are causing more contention and issues than the autovacuum. There is no option for turning off autovacuum when using the plpgsql partitioning functions (inability to COMMIT within function loop would cause too much contention). --- The order that data is migrated from the parent to the children can now be determined via an option to the partition_data_id/time() functions or the python script. The default is the way it originally moved data (ascending order). Thanks for bougyman from #postgresql on freenode for this idea. --- Removed plpgsql function "check_unique_column()" and created python script "check_unique_constraint.py". This runs far more efficiently and causes less contention within the database while checking if a unique constraint is consistent across all child tables. Also now supports checking multi-column constraints. See doc file for more info on script options. --- Fixed syntax error in create_parent(), create_id_function() exception blocks. Reported by bougyman. --- Added pgtap tests for additional constraints feature. - - -1.6.0 --- A new partitioning type has been added to allow setting almost any desired time interval (time-custom). The smallest interval supported is 1 second and the upper limit is bounded by the minimum and maximum timestamp values that PostgreSQL supports (http://www.postgresql.org/docs/current/static/datatype-datetime.html). This feature uses the range data type for internal configuration management, so it is only supported in PostgreSQL 9.2+. - -- The custom time interval is less efficient than both time-static and time-dynamic since it must use a lookup table. If your needed partitioning interval can fit in one of the pre-made intervals given in the documentation, it is highly recommended to use one of those for better performance. time-static is still the best method when performance of inserts is important. See the documentation for more details on this new partitioning type. --- New parameter to create_parent() that sets what the first partition in the set will be (p_start_partition). - -- Must be a valid timestamp (for time-based) or positive integer (for id-based) value. Be aware, though, the actual paramater data type is text. - -- For time-based partitioning, all partitions starting with the given timestamp up to CURRENT_TIMESTAMP (plus premake) will be created. - -- For id-based partitioning, only the partition starting at the given value (plus premake) will be made. --- pg_jobmon is now truly optional. Additonal configuration option for each individual partition set to turn it off and on. run_maintenance() now has an optional parameter to turn it off when being run. If you tried to partition pg_jobmon tables before, it would cause a permanent lockwait. Turn pg_jobmon off for those tables to avoid this. --- Fixed partition_data_time() & partition_data_id() functions to recreate the parent trigger function when static partitioning is used. Without this, partitioning more recent data that may have gotten into the parent table could possibly leave the function without conditions for the new partitions. run_maintenance() would eventually fix this for time partitioning, but id partitioning could be left in a broken state forever. (Github issue #16) --- Fixed bug in partition_data_time() & partition_data_id() to reset the lock wait counter properly between loops. Bug reported & fixed by bougyman from #postgresql on Freenode. --- pg_partman only supports id intervals greater than 1. May see if I can get an interval of 1 working later, but changed create_parent() to check for this and not allow it since it won't work properly at this time. New partitions were not automatically created if interval was set to 1. (Github issue #15) --- Clarify in docs that the id interval value passed to create_parent() must actually be in text type format. --- Changed drop & undo partition functions to use transaction based advistory locks. --- Removed need for internally used function create_next_time_partition() and therefore dropped the function. --- Simplified the create_time_partition() & create_id_partition() parameter lists. - - -1.5.1 --- Fix create_parent() to actually insert the contraint_cols value passed into the function to the config table when using time based partitioning. Thanks to Jeff Amiel for reporting the issue. - - -1.5.0 --- New functions that can manage additional constraints on child tables older than premake value based on what their min/max values are. This allows constraint exclusion to become usable on columns other than the partitioning column. However, this is only useful if older tables are no longer editing the columns that will have constraints placed on them, otherwise you risk constraint violations. See blog post for a more thorough explanation and examples: http://www.keithf4.com/managing-constraint-exclusion-in-table-partitioning --- New python script for using above functions to drop/apply constraints on an entire partition set without causing excessive lock issues. --- show_partitions() function is now guarenteed to return a list of partitions in the order that makes sense for the partition type/interval for the set. Additional option to specify ascending or descending order (defaults to ascending). --- Check for valid parameter values in partition creation function (github issue #14 from Josh Berkus) --- Added drop index concurrently option (--drop_concurrently) to reapply_indexes.py script. Only works for 9.2+ --- Changed run_maintenance() to use advisory transaction lock instead of session level lock. --- Fixed missing library import in python scripts. --- Organized docmentation of functions. - - -1.4.5 --- Fixed bug in reapply_indexes.py script that could cause all new indexes to be added to the parent instead of the children. This was happening if the parent table's schema was in the search_path of the role that the script uses to connect to the database. --- Removed any unneeded library imports in all python scripts. --- Moved python scripts from "extras" folder to "bin" folder. Now that they're actually getting installed as part of "make install" they're not really extras anymore. - - -1.4.4 --- Bug fix: Typos in partition_time_data/id() functions. Only ran into this if a lockwait was hit while trying to partition data. - - -1.4.3 --- Fix "make install" to work in PostgreSQL 9.3.x without throwing an error. --- "make install" now installs the python script files to /bin. They are now also executable and have the proper #! line at the top. --- Updated the rest of the python scripts to use argparse library for options (thanks to Josh Berkus for the assistance on this). - -- Some of the command line options have changed for the scripts. See the --help for each script to ensure you are using the correct parameters. - - -1.4.2 --- Added lockwait functionality to background data partitioning, including partition_data_id, partition_data_time, and partition_data.py (Thanks to Josh Berkus for this feature). - - -1.4.1 --- Assign child partitions to the tablespace of the parent. This will only apply to newly created partitions after this update is installed. To fix existing partitions, you will have to manually alter the child tables. Thanks to https://github.com/joelhoffman for the fix. - - -1.4.0 --- Updated creation of child partition, function & trigger names to take into account the max object length an object can have to guarentee the partition suffix. Involved extensive rewrite of many core functions. --- WARNING: If your table names were already long enough to be causing name truncation (over 63 characters), you may get duplicate child tables, functions & triggers created. Please check your object name lengths on your partition sets before installing this update to see if you may be affected by this edge case and its subsequent fix. --- New python script (reapply-indexes.py) to re-apply indexes to child tables when they have changed on the parent. See docs for more info. --- New function to check the uniqueness of a column in a partition set (check_unique_column()). Helps to overcome the inability of a unique constraint to be applied efficiently across all partitions in a set. Does not prevent a unique violation, but provides a method to monitor for it happening. --- More pgTAP tests to ensure name trunucation process is working. --- Changed pgTAP tests to assume pgTAP is installed in public schema to try and avoid issues when it isn't. - - -1.3.0 --- New configuration option for retention system that allows child tables that are eligible for removal to instead be moved to another schema. Set the "retention_schema" option in the configuration table to move the table to the designated schema instead of dropping it. This overrides the retention_keep_table & retention_keep_index options. --- New python script, dump_partition.py, that will dump any tables found in a given schema using pg_dump, create a SHA-512 hash of the dumped file and then drop the table from the database. --- The combination of the retention_schema option and the dump_partition.py script give a way to reliably dump out tables for archiving when they are no longer needed in the database. Idea for this feature adapted from conversation at PGDay NYC 2013 (lost the card of the individual I was talking with :( ). --- New function show_partitions() that gives a list of child tables in a partition set. Adapted from fork by https://github.com/ebaptistella --- Previously the functions that created the new partitions were using only the "INCLUDING DEFAULTS INCLUDING INDEXES" options when using the CREATE TABLE ... (LIKE ...) syntax. This caused some contraints on the parent to be missed in child tables. Changed to include all available options as of PostgreSQL 9.1: INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING STORAGE INCLUDING COMMENTS. Change will apply to all newly created child tables in all partition sets managed by pg_partman. You'll have to go back and manually fix any already existing child tables that may be missing constraints. Issue reported by Nick Ebbitt. --- Added TAP tests for drop partition functions. --- Fixed some tap tests to more accurately test for table (non)existance --- Clarified the drop_partition_id() function's retention parameter meaning. - - -1.2.0 --- Bug fix: Make child table lookups more intelligent to be able to deal with schemas being in the current search_path. Functions this affects are: drop_time_partition(), drop_id_partition(), reapply_privileges(), undo_partition(), undo_partition_id(), undo_partition_time(). Before table names may not have matched properly when looping through all tables to drop or reset privileges. Thanks to https://github.com/terrorobe for reporting this issue. --- Bug fix: reapply_privileges() had unconditional calls to pg_jobmon functions and would fail if it wasn't installed. --- Added new parameter to drop partition functions to manually set an interval you'd like to drop. Makes it easier to cleanup a bunch of old partitions you don't need anymore without having to go through the whole retention policy setup if that's not needed. --- Renamed drop_time_partition() to drop_partition_time() and drop_id_partition() to drop_partition_id() to be more consistent with the other function names. Please check function ownership & privileges before and after update to ensure they are reset properly. - - -1.1.0 --- New python scripts in extras folder to allow partition creation and undoing using smaller commit batches, as is suggested in the documentation for the partition_data_* and undo_partition_* functions. This helps avoid transaction locks when there is a large amount of data to move around. There are also options to commit more slowly and ease the load on very busy systems. --- Changed the ordering of batch arguments in partition_data_id() & partition_data_time(). This makes their order the same as the undo functions and is a more sensical order (I think anyway). --- Made partition functions quieter. No more notices and just returns number of rows moved. --- Changed the undo partition functions to remove partitions in the order they were originally created. They were doing it alphabetically before, which could cause an odd order for serial based partitioning (p100 would be before p2). Creation order may not remove them in ascending order of the data at first, which would be ideal, but it makes more sense than alphabetically. --- Bug fix: undo_partition() could return 0 prematurely if some of the partitions were empty. Will now automatically uninherit/drop any empty partitions and continue on if there are still child tables, not counting them against p_batch_count if given. - - -1.0.0 --- New functions to undo partitioning. These all either move or copy data from the child tables and put it into the parent. All have an option to allow you either uninherit the child tables (default) or drop them when all their data has been put into the parent. - -- undo_partition_time() & undo_partition_id are functions that move the data from the child partitions to the parent tables. Data is deleted from the child table and inserted to the parent. These functions allow smaller interval batches to be given as a parameter and are better able to handle larger partitioning sets. - -- undo_partition() can work on an any parent/child table set in PostgreSQL, not just partition sets created by pg_partman. Just pass it the name of the parent table. This method only copies the data out of the child tables instead of deleting it, allowing you to keep all the partitioned data if desired. Because of this it can only process an entire partition at a time and cannot handle batches smaller than the partition interval. --- Changed create_prev_id_partition() to partition_data_id() & create_prev_time_partition() to partition_data_time(). This clarifies what these actually do since they don't always create a partition nor is it always necessarily "previous" data. --- Changed how the above functions work to move data from parent into partitions. You can now feed them a smaller interval value for the rows that you'd like moved instead of it always moving exactly one entire partition of data. This allows smaller batch sizes when you've got a lot of data even in just one partition. That interval is now the second parameter. A third parameter can tell it how many of those interval batches you'd like to move in a single run of the function. Both of these parameters are optional. If not given, the interval defaults to the partition interval and the batch count is one (so it works exactly like it used to with no parameters but the parent table given). --- Partition premake system is now able to catch up if it falls behind for some reason. Also makes it so that if the premake value is increased, within the next few runs it will have that many partitions premade automatically. --- Bug fix: create_time_partition() & create_time_function() now handle the "timestamp with time zone" data type much better. Was getting some mismatches in the trigger rules and table constraints when timestamptz was in use on server not running in UTC/GMT time. Would cause constraint violations during data insert at certain time boundaries. If you ran into this issue, there are two ways to fix it: 1) Manually recreate the constraints for the most recent partitions and any future partitions already created. You may have to move some data around as well. 2) Use the new undo functions to move all the data back into the parent table and then repartition again using the partition_data_* functions. This will fix the issue for all partitions. --- Bug fix: Determining how many partitions to premake in run_maintenance() is now more accurate. Previous date math would occasionally premake 1 extra partition depending on the time differences. This can still occur with weekly partitioning due to differing month lengths (especially February) and daylight savings. Doesn't hurt anything and will self-correct. --- Much more complete pgTAP test suite. - - -0.4.2 --- The static partitioning trigger function can now handle partitions based on the configured premake value. For example, the default premake value is 4 so it can now handle data for the current partition, 4 previous partitions and 4 future partitions. Changing the premake value will cause the trigger function to be changed appropriately the next time a partition is automatically created. Except for initial setup, at no time does the automated partitioning system create old partitions (see the create_prev_* functions if you need to do this). If you change the premake value and there is no previous partition for it to put data in, it will go to the parent table. --- create_parent() now accounts for the new static partitioning rules. For time-static, it will create the current partition as well as previous and future partitions equal to the configured premake number (default premake being 4, you will end up with 9 partitions). For id-static, it will only create previous partitions if the resulting rules handle id values greater than zero. So if you're starting from zero you will only have future partitions created, and no previous. --- Constraint now ensures that premake value is greater than zero. --- create_parent() now ensures interval value for serial partitioning is greater than zero. --- Much more extensive pgTAP tests. - - -0.4.1 --- Changed the privilege management system to apply the current parent's privileges only to new child tables at the time they're created. No longer re-applies privileges to existing child tables. When partition sets grew large, this was causing serious performance problems and was too expensive an operation to run every time a child was created. --- Dropped apply_grants() function. New child table privileges are now managed by the partition creation functions themselves. --- Created reapply_privileges() function to reset the privileges of all child tables in a given partition set. Uses the given parent's privileges at the time the function is run. All new grants will be set and all that don't exist will be revoked. Ownership will be updated if it has changed. --- First round of pgTAP tests. - - -0.4.0 --- No separate configuration required for setting privileges on child tables anymore. Grants config table has been dropped. Please apply the grants you need to the parent table and they will be set for all children using that. Note that unlike before, privilges that don't exist on the parent will now be revoked from all child tables. --- create_parent() now enforces that a given parent table be schema qualified. Ensures that a custom search_path doesn't affect the wrong table by accident. --- Removed enum custom type and replace with check function. --- Applying of grants is now logged in pg_jobmon so if there's any issues with that step, it's clear where it failed. - - -0.3.2 --- Allow multiple grant commands for the same partition set in case different roles need different grants. Removed primary key constraint from part_grants table and updated apply_grants function --- create_parent() function now ensures that the control column has a not null constraint. --- Make select-only functions STABLE - - -0.3.1 --- Added check to dynamic id & time trigger functions to see if target table exists. If it doesn't, insert to parent instead of throwing error. Better than losing data! check_parent() function can monitor for this happening and create_prev_* functions can easily fix it. Thought of having it auto-create the needed partition, but if something is going wrong, that could end up creating a lot of unwanted partitions and be harder to clean up. - - -0.3.0 --- Added grants configuration table to propagate permissions to newly created child partitions. Will also apply those permissions to the parent table and all existing child tables whenever a new partition is created. Permissions are only granted, never revoked. See docs for more info. - - -0.2.0 --- New functions to manage dropping old partitions. Does not actually need to be called directly unless necessary. Use run_maintenance() function. --- Added ability to run_maintenance() function to manage dropping old tables in addition to managing time-based partitioning. --- Removed raise notice in run_maintenance and make sure old search path is reset after function finishes running. --- Lot of documentation updates - - -0.1.2 --- Added support for quarterly time partitioning (trickier than it first appeared) --- Fixed bug in run_maintenance() that would give an invalid cast to integer error. --- Fixed some calls to pg_jobmon that were outside the checks to see if it's actually installed --- Properly reset search path back to original before partman functions were run if pg_jobmon is being used --- Changed the default premake to 4 instead of 3. This will cause pg_jobmon's default monitoring for 3 consecutive failing jobs to trigger an before the last premade partition is used up. --- Added optional jobmon logging to run_maintenance() so that if it fails, pg_jobmon can notify that maintenance didn't work. - - -0.1.1 --- Only re-create partition functions if a new partition is made. diff --git a/META.json b/META.json index c06f29a4..d98bb38b 100644 --- a/META.json +++ b/META.json @@ -1,8 +1,8 @@ { "name": "pg_partman", "abstract": "Extension to manage partitioned tables by time or ID", - "version": "4.7.4", - "maintainer": [ + "version": "5.0.0", + "maintainer": [ "Keith Fiske " ], "license": "postgresql", @@ -11,7 +11,7 @@ "prereqs": { "runtime": { "requires": { - "PostgreSQL": "9.6.0" + "PostgreSQL": "14.0" }, "recommends": { "pg_jobmon": "1.4.1" @@ -20,9 +20,9 @@ }, "provides": { "pg_partman": { - "file": "sql/pg_partman--4.7.4.sql", + "file": "sql/pg_partman--5.0.0.sql", "docfile": "doc/pg_partman.md", - "version": "4.7.4", + "version": "5.0.0", "abstract": "Extension to manage partitioned tables by time or ID" } }, @@ -35,7 +35,7 @@ "web": "https://github.com/pgpartman/pg_partman", "type": "git" } - }, + }, "meta-spec": { "version": "1.0.0", "url": "http://pgxn.org/meta/spec.txt" diff --git a/pg_partman.control b/pg_partman.control index ad871877..4dd5c4a4 100644 --- a/pg_partman.control +++ b/pg_partman.control @@ -1,3 +1,3 @@ -default_version = '4.7.4' +default_version = '5.0.0' comment = 'Extension to manage partitioned tables by time or ID' relocatable = false diff --git a/sql/functions/apply_constraints.sql b/sql/functions/apply_constraints.sql index 7d0363d1..aa6ccdbf 100644 --- a/sql/functions/apply_constraints.sql +++ b/sql/functions/apply_constraints.sql @@ -1,4 +1,10 @@ -CREATE FUNCTION @extschema@.apply_constraints(p_parent_table text, p_child_table text DEFAULT NULL, p_analyze boolean DEFAULT FALSE, p_job_id bigint DEFAULT NULL) RETURNS void +CREATE FUNCTION @extschema@.apply_constraints( + p_parent_table text + , p_child_table text DEFAULT NULL + , p_analyze boolean DEFAULT FALSE + , p_job_id bigint DEFAULT NULL +) + RETURNS void LANGUAGE plpgsql AS $$ DECLARE @@ -11,7 +17,6 @@ v_child_exists text; v_child_tablename text; v_col text; v_constraint_cols text[]; -v_constraint_col_type text; v_constraint_name text; v_constraint_valid boolean; v_constraint_values record; @@ -24,10 +29,8 @@ v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_last_partition text; -v_last_partition_id bigint; +v_last_partition_id bigint; v_last_partition_timestamp timestamptz; -v_max_id bigint; -v_max_timestamp timestamptz; v_new_search_path text; v_old_search_path text; v_optimize_constraint int; @@ -39,8 +42,6 @@ v_partition_suffix text; v_premake int; v_sql text; v_step_id bigint; -v_suffix_position int; -v_type text; BEGIN /* @@ -48,7 +49,6 @@ BEGIN */ SELECT parent_table - , partition_type , control , premake , partition_interval @@ -59,7 +59,6 @@ SELECT parent_table , jobmon , constraint_valid INTO v_parent_table - , v_type , v_control , v_premake , v_partition_interval @@ -79,9 +78,9 @@ IF v_constraint_cols IS NULL THEN RETURN; END IF; -SELECT schemaname, tablename -INTO v_parent_schema, v_parent_tablename -FROM pg_catalog.pg_tables +SELECT schemaname, tablename +INTO v_parent_schema, v_parent_tablename +FROM pg_catalog.pg_tables WHERE schemaname = split_part(v_parent_table, '.', 1)::name AND tablename = split_part(v_parent_table, '.', 2)::name; @@ -122,7 +121,7 @@ IF p_child_table IS NULL THEN v_partition_suffix := to_char(v_last_partition_timestamp - (v_partition_interval::interval * (v_optimize_constraint + v_premake + 1) ), v_datetime_string); ELSIF v_control_type = 'id' THEN SELECT child_start_id INTO v_last_partition_id FROM @extschema@.show_partition_info(v_parent_schema||'.'||v_last_partition, v_partition_interval, v_parent_table); - v_partition_suffix := (v_last_partition_id - (v_partition_interval::bigint * (v_optimize_constraint + v_premake + 1) ))::text; + v_partition_suffix := (v_last_partition_id - (v_partition_interval::bigint * (v_optimize_constraint + v_premake + 1) ))::text; END IF; RAISE DEBUG 'apply_constraint: v_parent_tablename: %, v_last_partition: %, v_last_partition_timestamp: %, v_partition_suffix: %' @@ -136,7 +135,7 @@ IF p_child_table IS NULL THEN ELSE v_child_tablename = split_part(p_child_table, '.', 2); END IF; - + IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Applying additional constraints: Checking if target child table exists'); END IF; @@ -165,11 +164,11 @@ LOOP FROM pg_catalog.pg_constraint con JOIN pg_class c ON c.oid = con.conrelid JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid - JOIN pg_catalog.pg_attribute a ON con.conrelid = a.attrelid + JOIN pg_catalog.pg_attribute a ON con.conrelid = a.attrelid WHERE c.relname = v_child_tablename::name AND n.nspname = v_parent_schema::name AND con.conname LIKE 'partmanconstr_%' - AND con.contype = 'c' + AND con.contype = 'c' AND a.attname = v_col::name AND ARRAY[a.attnum] OPERATOR(pg_catalog.<@) con.conkey AND a.attisdropped = false; @@ -186,7 +185,7 @@ LOOP CONTINUE; END IF; - -- Ensure column name gets put on end of constraint name to help avoid naming conflicts + -- Ensure column name gets put on end of constraint name to help avoid naming conflicts v_constraint_name := @extschema@.check_name_length('partmanconstr_'||v_child_tablename, p_suffix := '_'||v_col); EXECUTE format('SELECT min(%I)::text AS min, max(%I)::text AS max FROM %I.%I', v_col, v_col, v_parent_schema, v_child_tablename) INTO v_constraint_values; @@ -260,4 +259,3 @@ DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; - diff --git a/sql/functions/calculate_time_partition_info.sql b/sql/functions/calculate_time_partition_info.sql new file mode 100644 index 00000000..ad637145 --- /dev/null +++ b/sql/functions/calculate_time_partition_info.sql @@ -0,0 +1,51 @@ +CREATE FUNCTION @extschema@.calculate_time_partition_info( + p_time_interval interval + , p_start_time timestamptz + , p_date_trunc_interval text DEFAULT NULL + , OUT base_timestamp timestamptz + , OUT datetime_string text +) + RETURNS record + LANGUAGE plpgsql STABLE + AS $$ +BEGIN + +-- Built-in datetime string suffixes are YYYYMMDD, YYYYMMDD_HH24MISS +datetime_string := 'YYYYMMDD'; +IF p_time_interval < '1 day' THEN + datetime_string := datetime_string || '_HH24MISS'; +END IF; + +IF p_date_trunc_interval IS NOT NULL THEN + base_timestamp := date_trunc(p_date_trunc_interval, p_start_time); + +ELSE + IF p_time_interval >= '1 year' THEN + base_timestamp := date_trunc('year', p_start_time); + IF p_time_interval >= '10 years' THEN + base_timestamp := date_trunc('decade', p_start_time); + IF p_time_interval >= '100 years' THEN + base_timestamp := date_trunc('century', p_start_time); + IF p_time_interval >= '1000 years' THEN + base_timestamp := date_trunc('millennium', p_start_time); + END IF; -- 1000 + END IF; -- 100 + END IF; -- 10 + END IF; -- 1 + + IF p_time_interval < '1 year' THEN + base_timestamp := date_trunc('month', p_start_time); + IF p_time_interval < '1 month' THEN + base_timestamp := date_trunc('day', p_start_time); + IF p_time_interval < '1 day' THEN + base_timestamp := date_trunc('hour', p_start_time); + IF p_time_interval < '1 minute' THEN + base_timestamp := date_trunc('minute', p_start_time); + END IF; -- minute + END IF; -- day + END IF; -- month + END IF; -- year + +END IF; +END +$$; diff --git a/sql/functions/check_default.sql b/sql/functions/check_default.sql index c4ba280a..e04b954d 100644 --- a/sql/functions/check_default.sql +++ b/sql/functions/check_default.sql @@ -1,4 +1,5 @@ -CREATE FUNCTION @extschema@.check_default(p_exact_count boolean DEFAULT true) RETURNS SETOF @extschema@.check_default_table +CREATE FUNCTION @extschema@.check_default(p_exact_count boolean DEFAULT true) + RETURNS SETOF @extschema@.check_default_table LANGUAGE plpgsql STABLE SET search_path = @extschema@,pg_temp AS $$ @@ -15,66 +16,46 @@ v_trouble @extschema@.check_default_table%rowtype; BEGIN /* - * Function to monitor for data getting inserted into parent/default tables + * Function to monitor for data getting inserted into default table */ -FOR v_row IN - SELECT parent_table, partition_type FROM @extschema@.part_config +FOR v_row IN + SELECT parent_table FROM @extschema@.part_config LOOP SELECT schemaname, tablename INTO v_parent_schemaname, v_parent_tablename - FROM pg_catalog.pg_tables + FROM pg_catalog.pg_tables WHERE schemaname = split_part(v_row.parent_table, '.', 1)::name AND tablename = split_part(v_row.parent_table, '.', 2)::name; - IF v_row.partition_type = 'partman' THEN - -- trigger based checks parent table + v_sql := format('SELECT n.nspname::text, c.relname::text FROM + pg_catalog.pg_inherits h + JOIN pg_catalog.pg_class c ON c.oid = h.inhrelid + JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid + WHERE h.inhparent = ''%I.%I''::regclass + AND pg_get_expr(relpartbound, c.oid) = ''DEFAULT''' + , v_parent_schemaname + , v_parent_tablename); + + EXECUTE v_sql INTO v_default_schemaname, v_default_tablename; + + IF v_default_schemaname IS NOT NULL AND v_default_tablename IS NOT NULL THEN + IF p_exact_count THEN - v_sql := format('SELECT count(1) AS n FROM ONLY %I.%I', v_parent_schemaname, v_parent_tablename); + v_sql := format('SELECT count(1) AS n FROM ONLY %I.%I', v_default_schemaname, v_default_tablename); ELSE - v_sql := format('SELECT count(1) AS n FROM (SELECT 1 FROM ONLY %I.%I LIMIT 1) x', v_parent_schemaname, v_parent_tablename); + v_sql := format('SELECT count(1) AS n FROM (SELECT 1 FROM ONLY %I.%I LIMIT 1) x', v_default_schemaname, v_default_tablename); END IF; + EXECUTE v_sql INTO v_count; - IF v_count > 0 THEN - v_trouble.default_table := v_parent_schemaname ||'.'|| v_parent_tablename; + IF v_count > 0 THEN + v_trouble.default_table := v_default_schemaname ||'.'|| v_default_tablename; v_trouble.count := v_count; RETURN NEXT v_trouble; END IF; - ELSIF v_row.partition_type = 'native' AND current_setting('server_version_num')::int >= 110000 THEN - -- native PG11+ checks default if it exists - - v_sql := format('SELECT n.nspname::text, c.relname::text FROM - pg_catalog.pg_inherits h - JOIN pg_catalog.pg_class c ON c.oid = h.inhrelid - JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid - WHERE h.inhparent = ''%I.%I''::regclass - AND pg_get_expr(relpartbound, c.oid) = ''DEFAULT''' - , v_parent_schemaname - , v_parent_tablename); - - EXECUTE v_sql INTO v_default_schemaname, v_default_tablename; - - IF v_default_schemaname IS NOT NULL AND v_default_tablename IS NOT NULL THEN - - IF p_exact_count THEN - v_sql := format('SELECT count(1) AS n FROM ONLY %I.%I', v_default_schemaname, v_default_tablename); - ELSE - v_sql := format('SELECT count(1) AS n FROM (SELECT 1 FROM ONLY %I.%I LIMIT 1) x', v_default_schemaname, v_default_tablename); - END IF; - - EXECUTE v_sql INTO v_count; - - IF v_count > 0 THEN - v_trouble.default_table := v_default_schemaname ||'.'|| v_default_tablename; - v_trouble.count := v_count; - RETURN NEXT v_trouble; - END IF; - - END IF; - - END IF; + END IF; v_count := 0; @@ -84,4 +65,3 @@ RETURN; END $$; - diff --git a/sql/functions/check_name_length.sql b/sql/functions/check_name_length.sql index 2e3d456b..73276d8c 100644 --- a/sql/functions/check_name_length.sql +++ b/sql/functions/check_name_length.sql @@ -1,10 +1,15 @@ -CREATE FUNCTION @extschema@.check_name_length (p_object_name text, p_suffix text DEFAULT NULL, p_table_partition boolean DEFAULT FALSE) RETURNS text +CREATE FUNCTION @extschema@.check_name_length ( + p_object_name text + , p_suffix text DEFAULT NULL + , p_table_partition boolean DEFAULT FALSE +) + RETURNS text LANGUAGE plpgsql IMMUTABLE SECURITY DEFINER SET search_path TO pg_catalog, pg_temp AS $$ DECLARE - v_new_length int; v_new_name text; + v_suffix text; BEGIN /* * Truncate the name of the given object if it is greater than the postgres default max (63 characters). @@ -13,28 +18,28 @@ BEGIN * Retains SECURITY DEFINER since it is called by trigger functions and did not want to break installations prior to 4.0.0 */ -IF p_table_partition IS TRUE AND (p_suffix IS NULL) THEN +IF p_table_partition IS TRUE AND (NULLIF(p_suffix, '') IS NULL) THEN RAISE EXCEPTION 'Table partition name requires a suffix value'; END IF; -IF p_table_partition THEN -- 61 characters to account for _p in partition name - IF char_length(p_object_name) + char_length(p_suffix) >= 61 THEN - v_new_length := 61 - char_length(p_suffix); - v_new_name := substring(p_object_name from 1 for v_new_length) || '_p' || p_suffix; - ELSE - v_new_name := p_object_name||'_p'||p_suffix; - END IF; -ELSE - IF char_length(p_object_name) + char_length(COALESCE(p_suffix, '')) >= 63 THEN - v_new_length := 63 - char_length(COALESCE(p_suffix, '')); - v_new_name := substring(p_object_name from 1 for v_new_length) || COALESCE(p_suffix, ''); - ELSE - v_new_name := p_object_name||COALESCE(p_suffix, ''); - END IF; + +v_suffix := format('%s%s', CASE WHEN p_table_partition THEN '_p' END, p_suffix); +-- Use optimistic behavior: in almost all cases `v_new_name` will be less than allowed maximum. +-- Do "heavy" work only in rare cases. +v_new_name := p_object_name || v_suffix; + +-- Postgres' relation name limit is in bytes, not characters; also it can be compiled with bigger allowed length. +-- Use its internals to detect where to cut new object name. +IF v_new_name::name != v_new_name THEN + -- Here we need to detect how many chars (not bytes) we need to get from the `p_object_name`. + -- Use suffix as prefix and get the rest of `p_object_name`. + v_new_name := (v_suffix || p_object_name)::name; + -- `substr` starts from 1, that is why we need to add 1 below. + -- Edge case: `v_suffix` is empty, length is 0, but need to start from 1. + v_new_name := substr(v_new_name, length(v_suffix) + 1) || v_suffix; END IF; RETURN v_new_name; END $$; - diff --git a/sql/functions/check_subpart_sameconfig.sql b/sql/functions/check_subpart_sameconfig.sql index 50c4622b..e9a59095 100644 --- a/sql/functions/check_subpart_sameconfig.sql +++ b/sql/functions/check_subpart_sameconfig.sql @@ -1,42 +1,39 @@ -CREATE FUNCTION @extschema@.check_subpart_sameconfig(p_parent_table text) - RETURNS TABLE (sub_partition_type text - , sub_control text +CREATE FUNCTION @extschema@.check_subpart_sameconfig(p_parent_table text) + RETURNS TABLE ( + sub_control text , sub_partition_interval text - , sub_constraint_cols text[] + , sub_partition_type text , sub_premake int - , sub_optimize_trigger int - , sub_optimize_constraint int - , sub_epoch text - , sub_inherit_fk boolean + , sub_automatic_maintenance text + , sub_template_table text , sub_retention text , sub_retention_schema text - , sub_retention_keep_table boolean , sub_retention_keep_index boolean + , sub_retention_keep_table boolean + , sub_epoch text + , sub_constraint_cols text[] + , sub_optimize_constraint int , sub_infinite_time_partitions boolean - , sub_automatic_maintenance text , sub_jobmon boolean - , sub_trigger_exception_handling boolean - , sub_upsert text - , sub_trigger_return_null boolean - , sub_template_table text , sub_inherit_privileges boolean , sub_constraint_valid boolean - , sub_subscription_refresh text , sub_date_trunc_interval text - , sub_ignore_default_data boolean) + , sub_ignore_default_data boolean + , sub_default_table boolean + ) LANGUAGE sql STABLE SET search_path = @extschema@,pg_temp AS $$ /* - * Check for consistent data in part_config_sub table. Was unable to get this working properly as either a constraint or trigger. + * Check for consistent data in part_config_sub table. Was unable to get this working properly as either a constraint or trigger. * Would either delay raising an error until the next write (which I cannot predict) or disallow future edits to update a sub-partition set's configuration. - * This is called by run_maintainance() and at least provides a consistent way to check that I know will run. + * This is called by run_maintainance() and at least provides a consistent way to check that I know will run. * If anyone can get a working constraint/trigger, please help! */ WITH parent_info AS ( SELECT c1.oid - FROM pg_catalog.pg_class c1 + FROM pg_catalog.pg_class c1 JOIN pg_catalog.pg_namespace n1 ON c1.relnamespace = n1.oid WHERE n1.nspname = split_part(p_parent_table, '.', 1)::name AND c1.relname = split_part(p_parent_table, '.', 2)::name @@ -49,34 +46,30 @@ AS $$ JOIN parent_info pi ON h.inhparent = pi.oid ) -- Column order here must match the RETURNS TABLE definition - -- This column list must be kept consistent between: + -- This column list must be kept consistent between: -- create_parent, check_subpart_sameconfig, create_partition_id, create_partition_time, dump_partitioned_table_definition, and table definition - SELECT DISTINCT a.sub_partition_type - , a.sub_control + -- Also check return table list from this function + SELECT DISTINCT + a.sub_control , a.sub_partition_interval - , a.sub_constraint_cols + , a.sub_partition_type , a.sub_premake - , a.sub_optimize_trigger - , a.sub_optimize_constraint - , a.sub_epoch - , a.sub_inherit_fk + , a.sub_automatic_maintenance + , a.sub_template_table , a.sub_retention , a.sub_retention_schema - , a.sub_retention_keep_table , a.sub_retention_keep_index + , a.sub_retention_keep_table + , a.sub_epoch + , a.sub_constraint_cols + , a.sub_optimize_constraint , a.sub_infinite_time_partitions - , a.sub_automatic_maintenance , a.sub_jobmon - , a.sub_trigger_exception_handling - , a.sub_upsert - , a.sub_trigger_return_null - , a.sub_template_table , a.sub_inherit_privileges , a.sub_constraint_valid - , a.sub_subscription_refresh , a.sub_date_trunc_interval , a.sub_ignore_default_data + , a.sub_default_table FROM @extschema@.part_config_sub a JOIN child_tables b on a.sub_parent = b.tablename; $$; - diff --git a/sql/functions/create_parent.sql b/sql/functions/create_parent.sql index f2bdf82c..b6979b0c 100644 --- a/sql/functions/create_parent.sql +++ b/sql/functions/create_parent.sql @@ -1,22 +1,20 @@ CREATE FUNCTION @extschema@.create_parent( p_parent_table text , p_control text - , p_type text , p_interval text - , p_constraint_cols text[] DEFAULT NULL + , p_type text DEFAULT 'range' + , p_epoch text DEFAULT 'none' , p_premake int DEFAULT 4 - , p_automatic_maintenance text DEFAULT 'on' , p_start_partition text DEFAULT NULL - , p_inherit_fk boolean DEFAULT true - , p_epoch text DEFAULT 'none' - , p_upsert text DEFAULT '' - , p_publications text[] DEFAULT NULL - , p_trigger_return_null boolean DEFAULT true + , p_default_table boolean DEFAULT true + , p_automatic_maintenance text DEFAULT 'on' + , p_constraint_cols text[] DEFAULT NULL , p_template_table text DEFAULT NULL , p_jobmon boolean DEFAULT true - , p_date_trunc_interval text DEFAULT NULL) -RETURNS boolean - LANGUAGE plpgsql + , p_date_trunc_interval text DEFAULT NULL +) + RETURNS boolean + LANGUAGE plpgsql AS $$ DECLARE @@ -37,12 +35,11 @@ v_higher_parent_epoch text; v_higher_parent_schema text := split_part(p_parent_table, '.', 1); v_higher_parent_table text := split_part(p_parent_table, '.', 2); v_id_interval bigint; -v_inherit_privileges boolean := false; +v_inherit_privileges boolean := false; -- This is false by default so initial partition set creation doesn't require superuser. v_job_id bigint; v_jobmon_schema text; v_last_partition_created boolean; v_max bigint; -v_native_sub_control text; v_notnull boolean; v_new_search_path text; v_old_search_path text; @@ -51,26 +48,22 @@ v_parent_partition_id bigint; v_parent_partition_timestamp timestamptz; v_parent_schema text; v_parent_tablename text; -v_parent_tablespace text; v_part_col text; v_part_type text; v_partition_time timestamptz; v_partition_time_array timestamptz[]; v_partition_id_array bigint[]; v_partstrat char; -v_publication_exists text; v_row record; v_sql text; v_start_time timestamptz; v_starting_partition_id bigint; v_step_id bigint; v_step_overflow_id bigint; -v_sub_parent text; v_success boolean := false; v_template_schema text; v_template_tablename text; v_time_interval interval; -v_top_datetime_string text; v_top_parent_schema text := split_part(p_parent_table, '.', 1); v_top_parent_table text := split_part(p_parent_table, '.', 2); v_unlogged char; @@ -86,34 +79,36 @@ ELSIF array_length(string_to_array(p_parent_table, '.'), 1) > 2 THEN RAISE EXCEPTION 'pg_partman does not support objects with periods in their names'; END IF; -IF p_upsert <> '' THEN - IF current_setting('server_version_num')::int < 90500 THEN - RAISE EXCEPTION 'INSERT ... ON CONFLICT (UPSERT) feature is only supported in PostgreSQL 9.5 and later'; - END IF; - IF p_type = 'native' AND current_setting('server_version_num')::int >= 110000 THEN - RAISE EXCEPTION 'The pg_partman upsert feature is not supported with native partitioning in PG11+. Use the built-in support for INSERT ON CONFLICT with native partitioning instead.'; - END IF; +IF p_interval = 'yearly' + OR p_interval = 'quarterly' + OR p_interval = 'monthly' + OR p_interval = 'weekly' + OR p_interval = 'daily' + OR p_interval = 'hourly' + OR p_interval = 'half-hour' + OR p_interval = 'quarter-hour' +THEN + RAISE EXCEPTION 'Special partition interval values from old pg_partman versions (%) are no longer supported. Please use a supported interval time value from core PostgreSQL (https://www.postgresql.org/docs/current/datatype-datetime.html#DATATYPE-INTERVAL-INPUT)', p_interval; END IF; -SELECT n.nspname, c.relname, t.spcname, c.relpersistence -INTO v_parent_schema, v_parent_tablename, v_parent_tablespace, v_unlogged +SELECT n.nspname, c.relname, c.relpersistence +INTO v_parent_schema, v_parent_tablename, v_unlogged FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid -LEFT OUTER JOIN pg_catalog.pg_tablespace t ON c.reltablespace = t.oid WHERE n.nspname = split_part(p_parent_table, '.', 1)::name AND c.relname = split_part(p_parent_table, '.', 2)::name; IF v_parent_tablename IS NULL THEN RAISE EXCEPTION 'Unable to find given parent table in system catalogs. Please create parent table first: %', p_parent_table; END IF; - -SELECT attnotnull INTO v_notnull + +SELECT attnotnull INTO v_notnull FROM pg_catalog.pg_attribute a JOIN pg_catalog.pg_class c ON a.attrelid = c.oid JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid WHERE c.relname = v_parent_tablename::name AND n.nspname = v_parent_schema::name AND a.attname = p_control::name; - IF p_type <> 'native' AND (v_notnull = false OR v_notnull IS NULL) THEN + IF (v_notnull = false OR v_notnull IS NULL) THEN RAISE EXCEPTION 'Control column given (%) for parent table (%) does not exist or must be set to NOT NULL', p_control, p_parent_table; END IF; @@ -125,7 +120,7 @@ IF v_control_type IS NULL THEN END IF; IF (p_epoch <> 'none' AND v_control_type <> 'id') THEN - RAISE EXCEPTION 'p_epoch can only be used with an integer based control column and does not work for native partitioning'; + RAISE EXCEPTION 'p_epoch can only be used with an integer based control column'; END IF; @@ -133,114 +128,73 @@ IF NOT @extschema@.check_partition_type(p_type) THEN RAISE EXCEPTION '% is not a valid partitioning type for pg_partman', p_type; END IF; -IF p_type = 'native' THEN - - IF current_setting('server_version_num')::int < 100000 THEN - RAISE EXCEPTION 'Native partitioning only available in PostgreSQL versions 10.0+'; - END IF; - -- Check if given parent table has been already set up as a partitioned table and is ranged - SELECT p.partstrat, partattrs INTO v_partstrat, v_partattrs - FROM pg_catalog.pg_partitioned_table p - JOIN pg_catalog.pg_class c ON p.partrelid = c.oid - JOIN pg_namespace n ON c.relnamespace = n.oid - WHERE n.nspname = v_parent_schema::name - AND c.relname = v_parent_tablename::name; - - IF v_partstrat <> 'r' OR v_partstrat IS NULL THEN - RAISE EXCEPTION 'When using native partitioning, you must have created the given parent table as ranged (not list) partitioned already. Ex: CREATE TABLE ... PARTITION BY RANGE ...)'; - END IF; - - IF array_length(v_partattrs, 1) > 1 THEN - RAISE NOTICE 'pg_partman only supports single column native partitioning at this time. Found % columns in given parent definition.', array_length(v_partattrs, 1); - END IF; - - SELECT a.attname, t.typname - INTO v_part_col, v_part_type - FROM pg_attribute a - JOIN pg_class c ON a.attrelid = c.oid - JOIN pg_namespace n ON c.relnamespace = n.oid - JOIN pg_type t ON a.atttypid = t.oid - WHERE n.nspname = v_parent_schema::name - AND c.relname = v_parent_tablename::name - AND attnum IN (SELECT unnest(partattrs) FROM pg_partitioned_table p WHERE a.attrelid = p.partrelid); - - IF p_control <> v_part_col OR v_control_exact_type <> v_part_type THEN - RAISE EXCEPTION 'Control column and type given in arguments (%, %) does not match the control column and type of the given native partition set (%, %)', p_control, v_control_exact_type, v_part_col, v_part_type; - END IF; - - -- Check that control column is a usable type for pg_partman. - IF v_control_type NOT IN ('time', 'id') THEN - RAISE EXCEPTION 'Only date/time or integer types are allowed for the control column with native partitioning.'; - END IF; - - -- Table to handle properties not natively inherited yet (indexes, fks, etc) - IF p_template_table IS NULL THEN - v_template_schema := '@extschema@'; - v_template_tablename := @extschema@.check_name_length('template_'||v_parent_schema||'_'||v_parent_tablename); - EXECUTE format('CREATE TABLE IF NOT EXISTS %I.%I (LIKE %I.%I)', '@extschema@', v_template_tablename, v_parent_schema, v_parent_tablename); - - SELECT pg_get_userbyid(c.relowner) INTO v_parent_owner - FROM pg_catalog.pg_class c - JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid - WHERE n.nspname = v_parent_schema::name - AND c.relname = v_parent_tablename::name; - - EXECUTE format('ALTER TABLE %I.%I OWNER TO %I' - , '@extschema@' - , v_template_tablename - , v_parent_owner); - ELSE - SELECT n.nspname, c.relname INTO v_template_schema, v_template_tablename - FROM pg_catalog.pg_class c - JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid - WHERE n.nspname = split_part(p_template_table, '.', 1)::name - AND c.relname = split_part(p_template_table, '.', 2)::name; - IF v_template_tablename IS NULL THEN - RAISE EXCEPTION 'Unable to find given template table in system catalogs (%). Please create template table first or leave parameter NULL to have a default one created for you.', p_parent_table; - END IF; - END IF; - -ELSE -- if not native +IF current_setting('server_version_num')::int < 140000 THEN + RAISE EXCEPTION 'pg_partman requires PostgreSQL 14 or greater'; +END IF; +-- Check if given parent table has been already set up as a partitioned table and is ranged +SELECT p.partstrat, partattrs INTO v_partstrat, v_partattrs +FROM pg_catalog.pg_partitioned_table p +JOIN pg_catalog.pg_class c ON p.partrelid = c.oid +JOIN pg_namespace n ON c.relnamespace = n.oid +WHERE n.nspname = v_parent_schema::name +AND c.relname = v_parent_tablename::name; + +IF v_partstrat <> 'r' OR v_partstrat IS NULL THEN + RAISE EXCEPTION 'You must have created the given parent table as ranged (not list) partitioned already. Ex: CREATE TABLE ... PARTITION BY RANGE ...)'; +END IF; - IF current_setting('server_version_num')::int >= 100000 THEN - SELECT p.partstrat INTO v_partstrat - FROM pg_catalog.pg_partitioned_table p - JOIN pg_catalog.pg_class c ON p.partrelid = c.oid - JOIN pg_namespace n ON c.relnamespace = n.oid - WHERE n.nspname = v_parent_schema::name - AND c.relname = v_parent_tablename::name; - END IF; +IF array_length(v_partattrs, 1) > 1 THEN + RAISE NOTICE 'pg_partman only supports single column partitioning at this time. Found % columns in given parent definition.', array_length(v_partattrs, 1); +END IF; - IF v_partstrat IS NOT NULL THEN - RAISE EXCEPTION 'Given parent table has been set up with native partitioning therefore cannot be used with pg_partman''s other partitioning types. Either recreate table non-native or set the type argument to ''native'''; - END IF; +SELECT a.attname, t.typname +INTO v_part_col, v_part_type +FROM pg_attribute a +JOIN pg_class c ON a.attrelid = c.oid +JOIN pg_namespace n ON c.relnamespace = n.oid +JOIN pg_type t ON a.atttypid = t.oid +WHERE n.nspname = v_parent_schema::name +AND c.relname = v_parent_tablename::name +AND attnum IN (SELECT unnest(partattrs) FROM pg_partitioned_table p WHERE a.attrelid = p.partrelid); + +IF p_control <> v_part_col OR v_control_exact_type <> v_part_type THEN + RAISE EXCEPTION 'Control column and type given in arguments (%, %) does not match the control column and type of the given partition set (%, %)', p_control, v_control_exact_type, v_part_col, v_part_type; +END IF; -END IF; -- end if "native" check +-- Check that control column is a usable type for pg_partman. +IF v_control_type NOT IN ('time', 'id') THEN + RAISE EXCEPTION 'Only date/time or integer types are allowed for the control column.'; +END IF; +-- Table to handle properties not managed by core PostgreSQL yet +IF p_template_table IS NULL THEN + v_template_schema := '@extschema@'; + v_template_tablename := @extschema@.check_name_length('template_'||v_parent_schema||'_'||v_parent_tablename); + EXECUTE format('CREATE TABLE IF NOT EXISTS %I.%I (LIKE %I.%I)', v_template_schema, v_template_tablename, v_parent_schema, v_parent_tablename); -IF p_publications IS NOT NULL THEN - IF current_setting('server_version_num')::int < 100000 THEN - RAISE EXCEPTION 'p_publications argument not null but CREATE PUBLICATION is only available in PostgreSQL versions 10.0+'; - END IF; - IF p_publications = '{}' THEN - RAISE EXCEPTION 'p_publications cannot be an empty set'; - END IF; - FOR v_row IN - SELECT unnest(p_publications) AS pubname - LOOP - SELECT pubname INTO v_publication_exists FROM pg_catalog.pg_publication where pubname = v_row.pubname::name; - IF v_publication_exists IS NULL THEN - RAISE EXCEPTION 'Given publication name (%) does not exist in system catalog. Ensure it is created first.', v_row.pubname; - END IF; - END LOOP; -END IF; + SELECT pg_get_userbyid(c.relowner) INTO v_parent_owner + FROM pg_catalog.pg_class c + JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid + WHERE n.nspname = v_parent_schema::name + AND c.relname = v_parent_tablename::name; --- Only inherit parent ownership/privileges on non-native sets by default --- This is false by default so initial partition set creation doesn't require superuser. -IF p_type = 'native' THEN - v_inherit_privileges = false; + EXECUTE format('ALTER TABLE %s.%I OWNER TO %I' + , '@extschema@' + , v_template_tablename + , v_parent_owner); +ELSIF lower(p_template_table) IN ('false', 'f') THEN + v_template_schema := NULL; + v_template_tablename := NULL; + RAISE DEBUG 'create_parent(): parent_table: %, skipped template table creation', p_parent_table; ELSE - v_inherit_privileges = true; + SELECT n.nspname, c.relname INTO v_template_schema, v_template_tablename + FROM pg_catalog.pg_class c + JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid + WHERE n.nspname = split_part(p_template_table, '.', 1)::name + AND c.relname = split_part(p_template_table, '.', 2)::name; + IF v_template_tablename IS NULL THEN + RAISE EXCEPTION 'Unable to find given template table in system catalogs (%). Please create template table first or leave parameter NULL to have a default one created for you.', p_parent_table; + END IF; END IF; SELECT current_setting('search_path') INTO v_old_search_path; @@ -266,7 +220,7 @@ END IF; -- If this parent table has siblings that are also partitioned (subpartitions), ensure this parent gets added to part_config_sub table so future maintenance will subpartition it -- Just doing in a loop to avoid having to assign a bunch of variables (should only run once, if at all; constraint should enforce only one value.) -FOR v_row IN +FOR v_row IN WITH parent_table AS ( SELECT h.inhparent AS parent_oid FROM pg_catalog.pg_inherits h @@ -275,37 +229,33 @@ FOR v_row IN WHERE c.relname = v_parent_tablename::name AND n.nspname = v_parent_schema::name ), sibling_children AS ( - SELECT i.inhrelid::regclass::text AS tablename + SELECT i.inhrelid::regclass::text AS tablename FROM pg_inherits i JOIN parent_table p ON i.inhparent = p.parent_oid ) - -- This column list must be kept consistent between: + -- This column list must be kept consistent between: -- create_parent, check_subpart_sameconfig, create_partition_id, create_partition_time, dump_partitioned_table_definition and table definition - SELECT DISTINCT sub_partition_type - , sub_control - , sub_partition_interval - , sub_constraint_cols - , sub_premake - , sub_inherit_fk - , sub_retention - , sub_retention_schema - , sub_retention_keep_table - , sub_retention_keep_index - , sub_automatic_maintenance - , sub_epoch - , sub_optimize_trigger - , sub_optimize_constraint - , sub_infinite_time_partitions - , sub_jobmon - , sub_trigger_exception_handling - , sub_upsert - , sub_trigger_return_null - , sub_template_table - , sub_inherit_privileges - , sub_constraint_valid - , sub_subscription_refresh - , sub_date_trunc_interval - , sub_ignore_default_data + SELECT DISTINCT + a.sub_control + , a.sub_partition_interval + , a.sub_partition_type + , a.sub_premake + , a.sub_automatic_maintenance + , a.sub_template_table + , a.sub_retention + , a.sub_retention_schema + , a.sub_retention_keep_index + , a.sub_retention_keep_table + , a.sub_epoch + , a.sub_constraint_cols + , a.sub_optimize_constraint + , a.sub_infinite_time_partitions + , a.sub_jobmon + , a.sub_inherit_privileges + , a.sub_constraint_valid + , a.sub_date_trunc_interval + , a.sub_ignore_default_data + , a.sub_default_table FROM @extschema@.part_config_sub a JOIN sibling_children b on a.sub_parent = b.tablename LIMIT 1 LOOP @@ -316,24 +266,18 @@ LOOP , sub_partition_interval , sub_constraint_cols , sub_premake - , sub_inherit_fk , sub_retention , sub_retention_schema , sub_retention_keep_table , sub_retention_keep_index , sub_automatic_maintenance , sub_epoch - , sub_optimize_trigger , sub_optimize_constraint , sub_infinite_time_partitions , sub_jobmon - , sub_trigger_exception_handling - , sub_upsert - , sub_trigger_return_null , sub_template_table , sub_inherit_privileges , sub_constraint_valid - , sub_subscription_refresh , sub_date_trunc_interval , sub_ignore_default_data) VALUES ( @@ -343,122 +287,41 @@ LOOP , v_row.sub_partition_interval , v_row.sub_constraint_cols , v_row.sub_premake - , v_row.sub_inherit_fk , v_row.sub_retention , v_row.sub_retention_schema - , v_row.sub_retention_keep_table , v_row.sub_retention_keep_index + , v_row.sub_retention_keep_table , v_row.sub_automatic_maintenance , v_row.sub_epoch - , v_row.sub_optimize_trigger , v_row.sub_optimize_constraint , v_row.sub_infinite_time_partitions , v_row.sub_jobmon - , v_row.sub_trigger_exception_handling - , v_row.sub_upsert - , v_row.sub_trigger_return_null , v_row.sub_template_table , v_row.sub_inherit_privileges , v_row.sub_constraint_valid - , v_row.sub_subscription_refresh , v_row.sub_date_trunc_interval , v_row.sub_ignore_default_data); - -- Set this equal to sibling configs so that newly created child table + -- Set this equal to sibling configs so that newly created child table -- privileges are set properly below during initial setup. - -- This setting is special because it applies immediately to the new child + -- This setting is special because it applies immediately to the new child -- tables of a given parent, not just during maintenance like most other settings. v_inherit_privileges = v_row.sub_inherit_privileges; END LOOP; IF v_control_type = 'time' OR (v_control_type = 'id' AND p_epoch <> 'none') THEN - CASE - WHEN p_interval = 'yearly' THEN - v_time_interval := '1 year'; - WHEN p_interval = 'quarterly' THEN - v_time_interval := '3 months'; - WHEN p_interval = 'monthly' THEN - v_time_interval := '1 month'; - WHEN p_interval = 'weekly' THEN - v_time_interval := '1 week'; - WHEN p_interval = 'daily' THEN - v_time_interval := '1 day'; - WHEN p_interval = 'hourly' THEN - v_time_interval := '1 hour'; - WHEN p_interval = 'half-hour' THEN - v_time_interval := '30 mins'; - WHEN p_interval = 'quarter-hour' THEN - v_time_interval := '15 mins'; - ELSE - IF p_type <> 'native' THEN - -- Reset for use as part_config type value below - p_type = 'time-custom'; - END IF; - v_time_interval := p_interval::interval; - IF v_time_interval < '1 second'::interval THEN - RAISE EXCEPTION 'Partitioning interval must be 1 second or greater'; - END IF; - END CASE; + v_time_interval := p_interval::interval; + IF v_time_interval < '1 second'::interval THEN + RAISE EXCEPTION 'Partitioning interval must be 1 second or greater'; + END IF; -- First partition is either the min premake or p_start_partition v_start_time := COALESCE(p_start_partition::timestamptz, CURRENT_TIMESTAMP - (v_time_interval * p_premake)); - - v_datetime_string := 'YYYY'; - IF p_date_trunc_interval IS NOT NULL THEN - - v_base_timestamp := date_trunc(p_date_trunc_interval, v_start_time); - - IF v_time_interval >= '1 day' THEN - v_datetime_string := v_datetime_string || '_MM_DD'; - ELSE - v_datetime_string := v_datetime_string || '_MM_DD_HH24MISS'; - END IF; - - ELSE - - IF v_time_interval >= '1 year' THEN - v_base_timestamp := date_trunc('year', v_start_time); - IF v_time_interval >= '10 years' THEN - v_base_timestamp := date_trunc('decade', v_start_time); - IF v_time_interval >= '100 years' THEN - v_base_timestamp := date_trunc('century', v_start_time); - IF v_time_interval >= '1000 years' THEN - v_base_timestamp := date_trunc('millennium', v_start_time); - END IF; -- 1000 - END IF; -- 100 - END IF; -- 10 - END IF; -- 1 - - IF v_time_interval < '1 year' THEN - IF p_interval = 'quarterly' THEN - v_base_timestamp := date_trunc('quarter', v_start_time); - v_datetime_string = 'YYYY"q"Q'; - ELSE - v_base_timestamp := date_trunc('month', v_start_time); - v_datetime_string := v_datetime_string || '_MM'; - END IF; - IF v_time_interval < '1 month' THEN - IF p_interval = 'weekly' THEN - v_base_timestamp := date_trunc('week', v_start_time); - v_datetime_string := 'IYYY"w"IW'; - ELSE - v_base_timestamp := date_trunc('day', v_start_time); - v_datetime_string := v_datetime_string || '_DD'; - END IF; - IF v_time_interval < '1 day' THEN - v_base_timestamp := date_trunc('hour', v_start_time); - v_datetime_string := v_datetime_string || '_HH24MI'; - IF v_time_interval < '1 minute' THEN - v_base_timestamp := date_trunc('minute', v_start_time); - v_datetime_string := v_datetime_string || 'SS'; - END IF; -- minute - END IF; -- day - END IF; -- month - END IF; -- year - - END IF; -- end p_date_trunc_interval IF + SELECT base_timestamp, datetime_string + INTO v_base_timestamp, v_datetime_string + FROM @extschema@.calculate_time_partition_info(v_time_interval, v_start_time, p_date_trunc_interval); RAISE DEBUG 'create_parent(): parent_table: %, v_base_timestamp: %', p_parent_table, v_base_timestamp; @@ -470,7 +333,7 @@ IF v_control_type = 'time' OR (v_control_type = 'id' AND p_epoch <> 'none') THEN v_partition_time := (v_base_timestamp + (v_time_interval * v_count))::timestamptz; v_partition_time_array := array_append(v_partition_time_array, v_partition_time); EXCEPTION WHEN datetime_field_overflow THEN - RAISE WARNING 'Attempted partition time interval is outside PostgreSQL''s supported time range. + RAISE WARNING 'Attempted partition time interval is outside PostgreSQL''s supported time range. Child partition creation after time % skipped', v_partition_time; v_step_overflow_id := add_step(v_job_id, 'Attempted partition time interval is outside PostgreSQL''s supported time range.'); PERFORM update_step(v_step_overflow_id, 'CRITICAL', 'Child partition creation after time '||v_partition_time||' skipped'); @@ -492,13 +355,11 @@ IF v_control_type = 'time' OR (v_control_type = 'id' AND p_epoch <> 'none') THEN , constraint_cols , datetime_string , automatic_maintenance - , inherit_fk - , jobmon - , upsert - , trigger_return_null + , jobmon , template_table - , publications - , inherit_privileges) + , inherit_privileges + , default_table + , date_trunc_interval) VALUES ( p_parent_table , p_type @@ -509,19 +370,17 @@ IF v_control_type = 'time' OR (v_control_type = 'id' AND p_epoch <> 'none') THEN , p_constraint_cols , v_datetime_string , p_automatic_maintenance - , p_inherit_fk , p_jobmon - , p_upsert - , p_trigger_return_null , v_template_schema||'.'||v_template_tablename - , p_publications - , v_inherit_privileges); + , v_inherit_privileges + , p_default_table + , p_date_trunc_interval); RAISE DEBUG 'create_parent: v_partition_time_array: %', v_partition_time_array; - v_last_partition_created := @extschema@.create_partition_time(p_parent_table, v_partition_time_array, false); + v_last_partition_created := @extschema@.create_partition_time(p_parent_table, v_partition_time_array); - IF v_last_partition_created = false THEN + IF v_last_partition_created = false THEN -- This can happen with subpartitioning when future or past partitions prevent child creation because they're out of range of the parent -- First see if this parent is a subpartition managed by pg_partman WITH top_oid AS ( @@ -531,8 +390,8 @@ IF v_control_type = 'time' OR (v_control_type = 'id' AND p_epoch <> 'none') THEN JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE c.relname = v_parent_tablename::name AND n.nspname = v_parent_schema::name - ) SELECT n.nspname, c.relname - INTO v_top_parent_schema, v_top_parent_table + ) SELECT n.nspname, c.relname + INTO v_top_parent_schema, v_top_parent_table FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace JOIN top_oid t ON c.oid = t.top_parent_oid @@ -554,7 +413,7 @@ IF v_control_type = 'time' OR (v_control_type = 'id' AND p_epoch <> 'none') THEN END IF; v_partition_time_array := NULL; v_partition_time_array := array_append(v_partition_time_array, v_base_timestamp); - v_last_partition_created := @extschema@.create_partition_time(p_parent_table, v_partition_time_array, false); + v_last_partition_created := @extschema@.create_partition_time(p_parent_table, v_partition_time_array); ELSE RAISE WARNING 'No child tables created. Check that all child tables did not already exist and may not have been part of partition set. Given parent has still been configured with pg_partman, but may not have expected children. Please review schema and config to confirm things are ok.'; @@ -570,7 +429,7 @@ IF v_control_type = 'time' OR (v_control_type = 'id' AND p_epoch <> 'none') THEN EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', v_old_search_path, 'false'); RETURN v_success; - END IF; + END IF; END IF; -- End v_last_partition IF IF v_jobmon_schema IS NOT NULL THEN @@ -581,11 +440,12 @@ END IF; IF v_control_type = 'id' AND p_epoch = 'none' THEN v_id_interval := p_interval::bigint; - IF p_type <> 'native' AND v_id_interval < 10 THEN - RAISE EXCEPTION 'Interval for serial, non-native partitioning must be greater than or equal to 10'; - END IF; + -- TODO: When list partitioning is supported, do not support interval less than 2 for ranged + -- IF v_id_interval < 2 THEN + -- RAISE EXCEPTION 'Interval for range partitioning must be greater than or equal to 2. Use LIST partitioning for single value partitions.'; + -- END IF; - -- Check if parent table is a subpartition of an already existing id partition set managed by pg_partman. + -- Check if parent table is a subpartition of an already existing id partition set managed by pg_partman. WHILE v_higher_parent_table IS NOT NULL LOOP -- initially set in DECLARE WITH top_oid AS ( SELECT i.inhparent AS top_parent_oid @@ -631,12 +491,12 @@ IF v_control_type = 'id' AND p_epoch = 'none' THEN v_starting_partition_id := v_max - (v_max % v_id_interval); FOR i IN 0..p_premake LOOP -- Only make previous partitions if ID value is less than the starting value and positive (and custom start partition wasn't set) - IF p_start_partition IS NULL AND - (v_starting_partition_id - (v_id_interval*i)) > 0 AND - (v_starting_partition_id - (v_id_interval*i)) < v_starting_partition_id + IF p_start_partition IS NULL AND + (v_starting_partition_id - (v_id_interval*i)) > 0 AND + (v_starting_partition_id - (v_id_interval*i)) < v_starting_partition_id THEN v_partition_id_array = array_append(v_partition_id_array, (v_starting_partition_id - v_id_interval*i)); - END IF; + END IF; v_partition_id_array = array_append(v_partition_id_array, (v_id_interval*i) + v_starting_partition_id); END LOOP; @@ -648,13 +508,11 @@ IF v_control_type = 'id' AND p_epoch = 'none' THEN , premake , constraint_cols , automatic_maintenance - , inherit_fk , jobmon - , upsert - , trigger_return_null , template_table - , publications - , inherit_privileges) + , inherit_privileges + , default_table + , date_trunc_interval) VALUES ( p_parent_table , p_type @@ -662,16 +520,14 @@ IF v_control_type = 'id' AND p_epoch = 'none' THEN , p_control , p_premake , p_constraint_cols - , p_automatic_maintenance - , p_inherit_fk + , p_automatic_maintenance , p_jobmon - , p_upsert - , p_trigger_return_null , v_template_schema||'.'||v_template_tablename - , p_publications - , v_inherit_privileges); + , v_inherit_privileges + , p_default_table + , p_date_trunc_interval); - v_last_partition_created := @extschema@.create_partition_id(p_parent_table, v_partition_id_array, false); + v_last_partition_created := @extschema@.create_partition_id(p_parent_table, v_partition_id_array); IF v_last_partition_created = false THEN -- This can happen with subpartitioning when future or past partitions prevent child creation because they're out of range of the parent @@ -706,7 +562,7 @@ IF v_control_type = 'id' AND p_epoch = 'none' THEN END IF; v_partition_id_array = NULL; v_partition_id_array = array_append(v_partition_id_array, v_starting_partition_id); - v_last_partition_created := @extschema@.create_partition_id(p_parent_table, v_partition_id_array, false); + v_last_partition_created := @extschema@.create_partition_id(p_parent_table, v_partition_id_array); ELSE -- Currently unknown edge case if code gets here RAISE WARNING 'No child tables created. Check that all child tables did not already exist and may not have been part of partition set. Given parent has still been configured with pg_partman, but may not have expected children. Please review schema and config to confirm things are ok.'; @@ -727,70 +583,34 @@ IF v_control_type = 'id' AND p_epoch = 'none' THEN END IF; -- End IF id -IF p_type = 'native' AND current_setting('server_version_num')::int >= 110000 THEN - -- Add default partition to native sets in PG11+ +IF p_default_table THEN + -- Add default partition v_default_partition := @extschema@.check_name_length(v_parent_tablename, '_default', FALSE); - v_sql := 'CREATE'; + v_sql := 'CREATE'; - -- Left this here as reminder to revisit once native figures out how it is handling changing unlogged stats + -- Left this here as reminder to revisit once core PG figures out how it is handling changing unlogged stats -- Currently handed via template table below - /* + /* IF v_unlogged = 'u' THEN v_sql := v_sql ||' UNLOGGED'; END IF; */ -- Same INCLUDING list is used in create_partition_*(). INDEXES is handled when partition is attached if it's supported. - v_sql := v_sql || format(' TABLE %I.%I (LIKE %I.%I INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING STORAGE INCLUDING COMMENTS ' + v_sql := v_sql || format(' TABLE %I.%I (LIKE %I.%I INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING STORAGE INCLUDING COMMENTS INCLUDING GENERATED)' , v_parent_schema, v_default_partition, v_parent_schema, v_parent_tablename); - IF current_setting('server_version_num')::int >= 120000 THEN - v_sql := v_sql || ' INCLUDING GENERATED '; - END IF; - v_sql := v_sql || ')'; EXECUTE v_sql; + v_sql := format('ALTER TABLE %I.%I ATTACH PARTITION %I.%I DEFAULT' , v_parent_schema, v_parent_tablename, v_parent_schema, v_default_partition); EXECUTE v_sql; - IF p_publications IS NOT NULL THEN - -- NOTE: Native publication inheritance is only supported on PG14+ - PERFORM @extschema@.apply_publications(p_parent_table, v_parent_schema, v_default_partition); - END IF; - - - IF current_setting('server_version_num')::int >= 120000 AND v_parent_tablespace IS NOT NULL THEN - -- Tablespace managed via inherit_template_properties() call below if PG11 or earliser - EXECUTE format('ALTER TABLE %I.%I SET TABLESPACE %I', v_parent_schema, v_default_partition, v_parent_tablespace); - END IF; - - -- Manage template inherited properies + -- Manage template inherited properties PERFORM @extschema@.inherit_template_properties(p_parent_table, v_parent_schema, v_default_partition); END IF; -IF p_type <> 'native' THEN - IF v_jobmon_schema IS NOT NULL THEN - v_step_id := add_step(v_job_id, 'Creating partition function'); - END IF; - IF v_control_type = 'time' OR (v_control_type = 'id' AND p_epoch <> 'none') THEN - PERFORM @extschema@.create_function_time(p_parent_table, v_job_id); - IF v_jobmon_schema IS NOT NULL THEN - PERFORM update_step(v_step_id, 'OK', 'Time function created'); - END IF; - ELSIF v_control_type = 'id' THEN - PERFORM @extschema@.create_function_id(p_parent_table, v_job_id); - IF v_jobmon_schema IS NOT NULL THEN - PERFORM update_step(v_step_id, 'OK', 'ID function created'); - END IF; - END IF; - - IF v_jobmon_schema IS NOT NULL THEN - v_step_id := add_step(v_job_id, 'Creating partition trigger'); - END IF; - PERFORM @extschema@.create_trigger(p_parent_table); -END IF; -- end native check - IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); @@ -829,4 +649,3 @@ DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; - diff --git a/sql/functions/create_partition_id.sql b/sql/functions/create_partition_id.sql index e4eb825c..7456369f 100644 --- a/sql/functions/create_partition_id.sql +++ b/sql/functions/create_partition_id.sql @@ -1,5 +1,10 @@ -CREATE FUNCTION @extschema@.create_partition_id(p_parent_table text, p_partition_ids bigint[], p_analyze boolean DEFAULT true, p_start_partition text DEFAULT NULL) RETURNS boolean - LANGUAGE plpgsql +CREATE FUNCTION @extschema@.create_partition_id( + p_parent_table text + , p_partition_ids bigint[] + , p_start_partition text DEFAULT NULL +) + RETURNS boolean + LANGUAGE plpgsql AS $$ DECLARE @@ -7,40 +12,29 @@ ex_context text; ex_detail text; ex_hint text; ex_message text; -v_all text[] := ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER']; -v_analyze boolean := FALSE; v_control text; v_control_type text; v_exists text; -v_grantees text[]; -v_hasoids boolean; v_id bigint; -v_inherit_fk boolean; v_inherit_privileges boolean; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_new_search_path text; v_old_search_path text; -v_parent_grant record; v_parent_schema text; v_parent_tablename text; -v_parent_tablespace text; v_partition_interval bigint; v_partition_created boolean := false; v_partition_name text; -v_partition_type text; -v_publications text[]; -v_revoke text; v_row record; v_sql text; v_step_id bigint; v_sub_control text; -v_sub_partition_type text; +v_sub_partition_type text; v_sub_id_max bigint; v_sub_id_min bigint; v_template_table text; -v_unlogged char; BEGIN /* @@ -48,20 +42,14 @@ BEGIN */ SELECT control - , partition_type - , partition_interval - , inherit_fk + , partition_interval::bigint -- this shared field also used in partition_time as interval , jobmon , template_table - , publications , inherit_privileges INTO v_control - , v_partition_type , v_partition_interval - , v_inherit_fk , v_jobmon , v_template_table - , v_publications , v_inherit_privileges FROM @extschema@.part_config WHERE parent_table = p_parent_table; @@ -70,11 +58,10 @@ IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; -SELECT n.nspname, c.relname, t.spcname -INTO v_parent_schema, v_parent_tablename, v_parent_tablespace +SELECT n.nspname, c.relname +INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid -LEFT OUTER JOIN pg_catalog.pg_tablespace t ON c.reltablespace = t.oid WHERE n.nspname = split_part(p_parent_table, '.', 1)::name AND c.relname = split_part(p_parent_table, '.', 2)::name; @@ -106,7 +93,7 @@ IF v_jobmon_schema IS NOT NULL THEN END IF; FOREACH v_id IN ARRAY p_partition_ids LOOP --- Do not create the child table if it's outside the bounds of the top parent. +-- Do not create the child table if it's outside the bounds of the top parent. IF v_sub_id_min IS NOT NULL THEN IF v_id < v_sub_id_min OR v_id >= v_sub_id_max THEN CONTINUE; @@ -116,7 +103,7 @@ FOREACH v_id IN ARRAY p_partition_ids LOOP v_partition_name := @extschema@.check_name_length(v_parent_tablename, v_id::text, TRUE); -- If child table already exists, skip creation -- Have to check pg_class because if subpartitioned, table will not be in pg_tables - SELECT c.relname INTO v_exists + SELECT c.relname INTO v_exists FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid WHERE n.nspname = v_parent_schema::name AND c.relname = v_partition_name::name; @@ -124,116 +111,45 @@ FOREACH v_id IN ARRAY p_partition_ids LOOP CONTINUE; END IF; - -- Ensure analyze is run if a new partition is created. Otherwise if one isn't, will be false and analyze will be skipped - v_analyze := TRUE; - IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Creating new partition '||v_partition_name||' with interval from '||v_id||' to '||(v_id + v_partition_interval)-1); END IF; - v_sql := 'CREATE'; - - -- As of PG12, the unlogged/logged status of a native parent table cannot be changed via an ALTER TABLE in order to affect its children. - -- As of v4.2x, the unlogged state will be managed via the template table - SELECT relpersistence INTO v_unlogged - FROM pg_catalog.pg_class c - JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid - WHERE c.relname = v_parent_tablename::name - AND n.nspname = v_parent_schema::name; - IF v_unlogged = 'u' and v_partition_type != 'native' THEN - v_sql := v_sql || ' UNLOGGED'; - END IF; - - -- Close parentheses on LIKE are below due to differing requirements of native subpartitioning + -- Close parentheses on LIKE are below due to differing requirements of subpartitioning -- Same INCLUDING list is used in create_parent() - v_sql := v_sql || format(' TABLE %I.%I (LIKE %I.%I INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING STORAGE INCLUDING COMMENTS ' + v_sql := format('CREATE TABLE %I.%I (LIKE %I.%I INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING STORAGE INCLUDING COMMENTS INCLUDING GENERATED ' , v_parent_schema , v_partition_name , v_parent_schema , v_parent_tablename); - IF current_setting('server_version_num')::int >= 120000 THEN - v_sql := v_sql || ' INCLUDING GENERATED '; - END IF; - - SELECT sub_partition_type, sub_control INTO v_sub_partition_type, v_sub_control - FROM @extschema@.part_config_sub + SELECT sub_partition_type, sub_control INTO v_sub_partition_type, v_sub_control + FROM @extschema@.part_config_sub WHERE sub_parent = p_parent_table; - IF v_sub_partition_type = 'native' THEN - -- INCLUDING INDEXES isn't necessary for native partitioning. It isn't supported in v10 and - -- for v11+ index inheritance is automatically handled when the partition is attached + IF v_sub_partition_type = 'range' THEN v_sql := v_sql || format(') PARTITION BY RANGE (%I) ', v_sub_control); ELSE - v_sql := v_sql || format(' INCLUDING INDEXES) ', v_sub_control); - END IF; - - - IF current_setting('server_version_num')::int < 120000 THEN - -- column removed from pgclass in pg12 - SELECT relhasoids INTO v_hasoids - FROM pg_catalog.pg_class c - JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid - WHERE c.relname = v_parent_tablename::name - AND n.nspname = v_parent_schema::name; - IF v_hasoids IS TRUE THEN - v_sql := v_sql || ' WITH (OIDS)'; - END IF; + v_sql := v_sql || format(')'); END IF; RAISE DEBUG 'create_partition_id v_sql: %', v_sql; EXECUTE v_sql; - IF v_partition_type = 'native' THEN - - IF current_setting('server_version_num')::int >= 120000 THEN - -- PG12 fixed tablespace marking on the parent of a native partition set - -- Versions older than 12 handle tablespace setting via inherit_template_properties() call below - IF v_parent_tablespace IS NOT NULL THEN - EXECUTE format('ALTER TABLE %I.%I SET TABLESPACE %I', v_parent_schema, v_partition_name, v_parent_tablespace); - END IF; - END IF; - - IF v_template_table IS NOT NULL THEN - PERFORM @extschema@.inherit_template_properties(p_parent_table, v_parent_schema, v_partition_name); - END IF; - - EXECUTE format('ALTER TABLE %I.%I ATTACH PARTITION %I.%I FOR VALUES FROM (%L) TO (%L)' - , v_parent_schema - , v_parent_tablename - , v_parent_schema - , v_partition_name - , v_id - , v_id + v_partition_interval); - - ELSE -- non-native - - IF v_parent_tablespace IS NOT NULL THEN - EXECUTE format('ALTER TABLE %I.%I SET TABLESPACE %I', v_parent_schema, v_partition_name, v_parent_tablespace); - END IF; - - EXECUTE format('ALTER TABLE %I.%I ADD CONSTRAINT %I CHECK (%I >= %s AND %I < %s )' - , v_parent_schema - , v_partition_name - , v_partition_name||'_partition_check' - , v_control - , v_id - , v_control - , v_id + v_partition_interval); + IF v_template_table IS NOT NULL THEN + PERFORM @extschema@.inherit_template_properties(p_parent_table, v_parent_schema, v_partition_name); + END IF; - EXECUTE format('ALTER TABLE %I.%I INHERIT %I.%I', v_parent_schema, v_partition_name, v_parent_schema, v_parent_tablename); + EXECUTE format('ALTER TABLE %I.%I ATTACH PARTITION %I.%I FOR VALUES FROM (%L) TO (%L)' + , v_parent_schema + , v_parent_tablename + , v_parent_schema + , v_partition_name + , v_id + , v_id + v_partition_interval); - -- Indexes cannot be created on the parent, so clustering cannot be used for native yet. - PERFORM @extschema@.apply_cluster(v_parent_schema, v_parent_tablename, v_parent_schema, v_partition_name); - -- Foreign keys to other tables not supported on native parent tables - IF v_inherit_fk THEN - PERFORM @extschema@.apply_foreign_keys(p_parent_table, v_parent_schema||'.'||v_partition_name, v_job_id); - END IF; - - END IF; - - -- NOTE: Privileges not automatically inherited for native. Only do so if config flag is set - IF v_partition_type != 'native' OR (v_partition_type = 'native' AND v_inherit_privileges = TRUE) THEN + -- NOTE: Privileges not automatically inherited. Only do so if config flag is set + IF v_inherit_privileges = TRUE THEN PERFORM @extschema@.apply_privileges(v_parent_schema, v_parent_tablename, v_parent_schema, v_partition_name, v_job_id); END IF; @@ -243,35 +159,31 @@ FOREACH v_id IN ARRAY p_partition_ids LOOP -- Will only loop once and only if sub_partitioning is actually configured -- This seemed easier than assigning a bunch of variables then doing an IF condition - -- This column list must be kept consistent between: + -- This column list must be kept consistent between: -- create_parent, check_subpart_sameconfig, create_partition_id, create_partition_time, dump_partitioned_table_definition, and table definition - FOR v_row IN - SELECT sub_parent - , sub_partition_type + FOR v_row IN + SELECT + sub_parent , sub_control , sub_partition_interval - , sub_constraint_cols + , sub_partition_type , sub_premake - , sub_optimize_trigger - , sub_optimize_constraint - , sub_epoch - , sub_inherit_fk + , sub_automatic_maintenance + , sub_template_table , sub_retention , sub_retention_schema - , sub_retention_keep_table , sub_retention_keep_index + , sub_retention_keep_table + , sub_epoch + , sub_constraint_cols + , sub_optimize_constraint , sub_infinite_time_partitions - , sub_automatic_maintenance , sub_jobmon - , sub_trigger_exception_handling - , sub_upsert - , sub_trigger_return_null - , sub_template_table , sub_inherit_privileges , sub_constraint_valid - , sub_subscription_refresh , sub_date_trunc_interval , sub_ignore_default_data + , sub_default_table FROM @extschema@.part_config_sub WHERE sub_parent = p_parent_table LOOP @@ -283,23 +195,23 @@ FOREACH v_id IN ARRAY p_partition_ids LOOP , p_control := %L , p_type := %L , p_interval := %L + , p_default_table := %L , p_constraint_cols := %L , p_premake := %L , p_automatic_maintenance := %L - , p_inherit_fk := %L , p_epoch := %L , p_template_table := %L , p_jobmon := %L - , p_start_partition := %L + , p_start_partition := %L , p_date_trunc_interval := %L )' , v_parent_schema||'.'||v_partition_name , v_row.sub_control , v_row.sub_partition_type , v_row.sub_partition_interval + , v_row.sub_default_table , v_row.sub_constraint_cols , v_row.sub_premake , v_row.sub_automatic_maintenance - , v_row.sub_inherit_fk , v_row.sub_epoch , v_row.sub_template_table , v_row.sub_jobmon @@ -308,19 +220,13 @@ FOREACH v_id IN ARRAY p_partition_ids LOOP RAISE DEBUG 'create_partition_id (create_parent loop): %', v_sql; EXECUTE v_sql; - UPDATE @extschema@.part_config SET + UPDATE @extschema@.part_config SET retention_schema = v_row.sub_retention_schema , retention_keep_table = v_row.sub_retention_keep_table - , retention_keep_index = v_row.sub_retention_keep_index - , optimize_trigger = v_row.sub_optimize_trigger , optimize_constraint = v_row.sub_optimize_constraint , infinite_time_partitions = v_row.sub_infinite_time_partitions - , trigger_exception_handling = v_row.sub_trigger_exception_handling - , upsert = v_row.sub_upsert , inherit_privileges = v_row.sub_inherit_privileges - , trigger_return_null = v_row.sub_trigger_return_null , constraint_valid = v_row.sub_constraint_valid - , subscription_refresh = v_row.sub_subscription_refresh , ignore_default_data = v_row.sub_ignore_default_data WHERE parent_table = v_parent_schema||'.'||v_partition_name; @@ -329,14 +235,9 @@ FOREACH v_id IN ARRAY p_partition_ids LOOP END IF; END LOOP; -- end sub partitioning LOOP - - -- Manage additonal constraints if set - PERFORM @extschema@.apply_constraints(p_parent_table, p_job_id := v_job_id); - IF v_publications IS NOT NULL THEN - -- NOTE: Native publication inheritance is only supported on PG14+ - PERFORM @extschema@.apply_publications(p_parent_table, v_parent_schema, v_partition_name); - END IF; + -- Manage additional constraints if set + PERFORM @extschema@.apply_constraints(p_parent_table, p_job_id := v_job_id); v_partition_created := true; @@ -344,6 +245,7 @@ END LOOP; -- v_analyze is a local check if a new table is made. -- p_analyze is a parameter to say whether to run the analyze at all. Used by create_parent() to avoid long exclusive lock or run_maintenence() to avoid long creation runs. +/** REMOVE IF v_analyze AND p_analyze THEN IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, format('Analyzing partition set: %s', p_parent_table)); @@ -355,6 +257,7 @@ IF v_analyze AND p_analyze THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; +REMOVE **/ IF v_jobmon_schema IS NOT NULL THEN IF v_partition_created = false THEN @@ -364,7 +267,7 @@ IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); END IF; - + EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', v_old_search_path, 'false'); RETURN v_partition_created; @@ -388,7 +291,6 @@ EXCEPTION RAISE EXCEPTION '% CONTEXT: % DETAIL: % -HINT: %', ex_message, ex_context, ex_detail, ex_hint; +HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; - diff --git a/sql/functions/create_partition_time.sql b/sql/functions/create_partition_time.sql index 3dde5d3e..531ab3be 100644 --- a/sql/functions/create_partition_time.sql +++ b/sql/functions/create_partition_time.sql @@ -1,5 +1,9 @@ -CREATE FUNCTION @extschema@.create_partition_time(p_parent_table text, p_partition_times timestamptz[], p_analyze boolean DEFAULT true, p_start_partition text DEFAULT NULL) -RETURNS boolean +CREATE FUNCTION @extschema@.create_partition_time( + p_parent_table text + , p_partition_times timestamptz[] + , p_start_partition text DEFAULT NULL +) + RETURNS boolean LANGUAGE plpgsql AS $$ DECLARE @@ -8,77 +12,55 @@ ex_context text; ex_detail text; ex_hint text; ex_message text; -v_all text[] := ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER']; -v_analyze boolean := FALSE; v_control text; v_control_type text; v_datetime_string text; v_epoch text; v_exists smallint; -v_grantees text[]; -v_hasoids boolean; v_inherit_privileges boolean; -v_inherit_fk boolean; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_new_search_path text; v_old_search_path text; -v_parent_grant record; v_parent_schema text; v_parent_tablename text; -v_part_col text; v_partition_created boolean := false; v_partition_name text; v_partition_suffix text; -v_parent_tablespace text; v_partition_expression text; v_partition_interval interval; v_partition_timestamp_end timestamptz; v_partition_timestamp_start timestamptz; -v_publications text[]; -v_quarter text; -v_revoke text; v_row record; v_sql text; v_step_id bigint; v_step_overflow_id bigint; v_sub_control text; -v_sub_parent text; v_sub_partition_type text; v_sub_timestamp_max timestamptz; v_sub_timestamp_min timestamptz; v_template_table text; -v_trunc_value text; v_time timestamptz; -v_partition_type text; -v_unlogged char; -v_year text; BEGIN /* * Function to create a child table in a time-based partition set */ -SELECT partition_type - , control - , partition_interval +SELECT control + , partition_interval::interval -- this shared field also used in partition_id as bigint , epoch - , inherit_fk , jobmon , datetime_string , template_table - , publications , inherit_privileges -INTO v_partition_type - , v_control +INTO v_control , v_partition_interval , v_epoch - , v_inherit_fk , v_jobmon , v_datetime_string , v_template_table - , v_publications , v_inherit_privileges FROM @extschema@.part_config WHERE parent_table = p_parent_table; @@ -87,16 +69,15 @@ IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; -SELECT n.nspname, c.relname, t.spcname -INTO v_parent_schema, v_parent_tablename, v_parent_tablespace +SELECT n.nspname, c.relname +INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid -LEFT OUTER JOIN pg_catalog.pg_tablespace t ON c.reltablespace = t.oid WHERE n.nspname = split_part(p_parent_table, '.', 1)::name AND c.relname = split_part(p_parent_table, '.', 2)::name; SELECT general_type INTO v_control_type FROM @extschema@.check_control_type(v_parent_schema, v_parent_tablename, v_control); -IF v_control_type <> 'time' THEN +IF v_control_type <> 'time' THEN IF (v_control_type = 'id' AND v_epoch = 'none') OR v_control_type <> 'id' THEN RAISE EXCEPTION 'Cannot run on partition set without time based control column or epoch flag set with an id column. Found control: %, epoch: %', v_control_type, v_epoch; END IF; @@ -131,12 +112,12 @@ v_partition_expression := CASE END; RAISE DEBUG 'create_partition_time: v_partition_expression: %', v_partition_expression; -FOREACH v_time IN ARRAY p_partition_times LOOP +FOREACH v_time IN ARRAY p_partition_times LOOP v_partition_timestamp_start := v_time; BEGIN v_partition_timestamp_end := v_time + v_partition_interval; EXCEPTION WHEN datetime_field_overflow THEN - RAISE WARNING 'Attempted partition time interval is outside PostgreSQL''s supported time range. + RAISE WARNING 'Attempted partition time interval is outside PostgreSQL''s supported time range. Child partition creation after time % skipped', v_time; v_step_overflow_id := add_step(v_job_id, 'Attempted partition time interval is outside PostgreSQL''s supported time range.'); PERFORM update_step(v_step_overflow_id, 'CRITICAL', 'Child partition creation after time '||v_time||' skipped'); @@ -144,7 +125,7 @@ FOREACH v_time IN ARRAY p_partition_times LOOP CONTINUE; END; - -- Do not create the child table if it's outside the bounds of the top parent. + -- Do not create the child table if it's outside the bounds of the top parent. IF v_sub_timestamp_min IS NOT NULL THEN IF v_time < v_sub_timestamp_min OR v_time >= v_sub_timestamp_max THEN @@ -158,20 +139,17 @@ FOREACH v_time IN ARRAY p_partition_times LOOP -- This suffix generation code is in partition_data_time() as well v_partition_suffix := to_char(v_time, v_datetime_string); v_partition_name := @extschema@.check_name_length(v_parent_tablename, v_partition_suffix, TRUE); - -- Check if child exists. + -- Check if child exists. SELECT count(*) INTO v_exists FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid - WHERE n.nspname = v_parent_schema::name + WHERE n.nspname = v_parent_schema::name AND c.relname = v_partition_name::name; IF v_exists > 0 THEN CONTINUE; END IF; - -- Ensure analyze is run if a new partition is created. Otherwise if one isn't, will be false and analyze will be skipped - v_analyze := TRUE; - IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, format('Creating new partition %s.%s with interval from %s to %s' , v_parent_schema @@ -182,123 +160,82 @@ FOREACH v_time IN ARRAY p_partition_times LOOP v_sql := 'CREATE'; - -- As of PG12, the unlogged/logged status of a native parent table cannot be changed via an ALTER TABLE in order to affect its children. - -- As of v4.2x, the unlogged state will be managed via the template table - SELECT relpersistence INTO v_unlogged + /* + -- As of PG12, the unlogged/logged status of a parent table cannot be changed via an ALTER TABLE in order to affect its children. + -- As of partman v4.2x, the unlogged state will be managed via the template table + -- TODO Test UNLOGGED status in PG16 to see if this can be done without template yet. Add to create_partition_id then as well. + SELECT relpersistence INTO v_unlogged FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid WHERE c.relname = v_parent_tablename::name AND n.nspname = v_parent_schema::name; - IF v_unlogged = 'u' and v_partition_type != 'native' THEN + + IF v_unlogged = 'u' THEN v_sql := v_sql || ' UNLOGGED'; END IF; + */ - -- Close parentheses on LIKE are below due to differing requirements of native subpartitioning + -- Close parentheses on LIKE are below due to differing requirements of subpartitioning -- Same INCLUDING list is used in create_parent() - v_sql := v_sql || format(' TABLE %I.%I (LIKE %I.%I INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING STORAGE INCLUDING COMMENTS ' + v_sql := v_sql || format(' TABLE %I.%I (LIKE %I.%I INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING STORAGE INCLUDING COMMENTS INCLUDING GENERATED ' , v_parent_schema , v_partition_name , v_parent_schema , v_parent_tablename); - IF current_setting('server_version_num')::int >= 120000 THEN - v_sql := v_sql || ' INCLUDING GENERATED '; - END IF; - - - SELECT sub_partition_type, sub_control INTO v_sub_partition_type, v_sub_control - FROM @extschema@.part_config_sub + SELECT sub_partition_type, sub_control INTO v_sub_partition_type, v_sub_control + FROM @extschema@.part_config_sub WHERE sub_parent = p_parent_table; - IF v_sub_partition_type = 'native' THEN - -- INCLUDING INDEXES isn't necessary for native partitioning. It isn't supported in v10 and - -- for v11+ index inheritance is automatically handled when the partition is attached + IF v_sub_partition_type = 'range' THEN v_sql := v_sql || format(') PARTITION BY RANGE (%I) ', v_sub_control); ELSE - v_sql := v_sql || format(' INCLUDING INDEXES) '); - END IF; - - IF current_setting('server_version_num')::int < 120000 THEN - -- column removed from pgclass in pg12 - SELECT relhasoids INTO v_hasoids - FROM pg_catalog.pg_class c - JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid - WHERE c.relname = v_parent_tablename::name - AND n.nspname = v_parent_schema::name; - IF v_hasoids IS TRUE THEN - v_sql := v_sql || ' WITH (OIDS)'; - END IF; + v_sql := v_sql || format(')'); END IF; RAISE DEBUG 'create_partition_time v_sql: %', v_sql; EXECUTE v_sql; + IF v_template_table IS NOT NULL THEN + PERFORM @extschema@.inherit_template_properties(p_parent_table, v_parent_schema, v_partition_name); + END IF; - IF v_partition_type = 'native' THEN - - IF current_setting('server_version_num')::int >= 120000 THEN - -- PG12 fixed tablespace marking on the parent of a native partition set - -- Versions older than 12 handle tablespace setting via inherit_template_properties() call below - IF v_parent_tablespace IS NOT NULL THEN - EXECUTE format('ALTER TABLE %I.%I SET TABLESPACE %I', v_parent_schema, v_partition_name, v_parent_tablespace); - END IF; - END IF; - - IF v_template_table IS NOT NULL THEN - PERFORM @extschema@.inherit_template_properties(p_parent_table, v_parent_schema, v_partition_name); - END IF; - - IF v_epoch = 'none' THEN - -- Attach with normal, time-based values for native constraint + IF v_epoch = 'none' THEN + -- Attach with normal, time-based values for built-in constraint + EXECUTE format('ALTER TABLE %I.%I ATTACH PARTITION %I.%I FOR VALUES FROM (%L) TO (%L)' + , v_parent_schema + , v_parent_tablename + , v_parent_schema + , v_partition_name + , v_partition_timestamp_start + , v_partition_timestamp_end); + ELSE + -- Must attach with integer based values for built-in constraint and epoch + IF v_epoch = 'seconds' THEN EXECUTE format('ALTER TABLE %I.%I ATTACH PARTITION %I.%I FOR VALUES FROM (%L) TO (%L)' , v_parent_schema , v_parent_tablename , v_parent_schema , v_partition_name - , v_partition_timestamp_start - , v_partition_timestamp_end); - ELSE - -- Must attach with integer based values for native constraint and epoch - IF v_epoch = 'seconds' THEN - EXECUTE format('ALTER TABLE %I.%I ATTACH PARTITION %I.%I FOR VALUES FROM (%L) TO (%L)' - , v_parent_schema - , v_parent_tablename - , v_parent_schema - , v_partition_name - , EXTRACT('epoch' FROM v_partition_timestamp_start)::bigint - , EXTRACT('epoch' FROM v_partition_timestamp_end)::bigint); - ELSIF v_epoch = 'milliseconds' THEN - EXECUTE format('ALTER TABLE %I.%I ATTACH PARTITION %I.%I FOR VALUES FROM (%L) TO (%L)' - , v_parent_schema - , v_parent_tablename - , v_parent_schema - , v_partition_name - , EXTRACT('epoch' FROM v_partition_timestamp_start)::bigint * 1000 - , EXTRACT('epoch' FROM v_partition_timestamp_end)::bigint * 1000); - ELSIF v_epoch = 'nanoseconds' THEN - EXECUTE format('ALTER TABLE %I.%I ATTACH PARTITION %I.%I FOR VALUES FROM (%L) TO (%L)' - , v_parent_schema - , v_parent_tablename - , v_parent_schema - , v_partition_name - , EXTRACT('epoch' FROM v_partition_timestamp_start)::bigint * 1000000000 - , EXTRACT('epoch' FROM v_partition_timestamp_end)::bigint * 1000000000); - END IF; - -- Create secondary, time-based constraint since native's constraint is already integer based - EXECUTE format('ALTER TABLE %I.%I ADD CONSTRAINT %I CHECK (%s >= %L AND %4$s < %6$L)' + , EXTRACT('epoch' FROM v_partition_timestamp_start)::bigint + , EXTRACT('epoch' FROM v_partition_timestamp_end)::bigint); + ELSIF v_epoch = 'milliseconds' THEN + EXECUTE format('ALTER TABLE %I.%I ATTACH PARTITION %I.%I FOR VALUES FROM (%L) TO (%L)' + , v_parent_schema + , v_parent_tablename , v_parent_schema , v_partition_name - , v_partition_name||'_partition_check' - , v_partition_expression - , v_partition_timestamp_start - , v_partition_timestamp_end); - END IF; - ELSE -- non-native - - IF v_parent_tablespace IS NOT NULL THEN - EXECUTE format('ALTER TABLE %I.%I SET TABLESPACE %I', v_parent_schema, v_partition_name, v_parent_tablespace); + , EXTRACT('epoch' FROM v_partition_timestamp_start)::bigint * 1000 + , EXTRACT('epoch' FROM v_partition_timestamp_end)::bigint * 1000); + ELSIF v_epoch = 'nanoseconds' THEN + EXECUTE format('ALTER TABLE %I.%I ATTACH PARTITION %I.%I FOR VALUES FROM (%L) TO (%L)' + , v_parent_schema + , v_parent_tablename + , v_parent_schema + , v_partition_name + , EXTRACT('epoch' FROM v_partition_timestamp_start)::bigint * 1000000000 + , EXTRACT('epoch' FROM v_partition_timestamp_end)::bigint * 1000000000); END IF; - - -- Non-native always gets time-based constraint + -- Create secondary, time-based constraint since built-in's constraint is already integer based EXECUTE format('ALTER TABLE %I.%I ADD CONSTRAINT %I CHECK (%s >= %L AND %4$s < %6$L)' , v_parent_schema , v_partition_name @@ -306,60 +243,10 @@ FOREACH v_time IN ARRAY p_partition_times LOOP , v_partition_expression , v_partition_timestamp_start , v_partition_timestamp_end); - IF v_epoch = 'seconds' THEN - -- Non-native needs secondary, integer based constraint for epoch - EXECUTE format('ALTER TABLE %I.%I ADD CONSTRAINT %I CHECK (%I >= %L AND %I < %L)' - , v_parent_schema - , v_partition_name - , v_partition_name||'_partition_int_check' - , v_control - , EXTRACT('epoch' from v_partition_timestamp_start)::bigint - , v_control - , EXTRACT('epoch' from v_partition_timestamp_end)::bigint ); - ELSIF v_epoch = 'milliseconds' THEN - EXECUTE format('ALTER TABLE %I.%I ADD CONSTRAINT %I CHECK (%I >= %L AND %I < %L)' - , v_parent_schema - , v_partition_name - , v_partition_name||'_partition_int_check' - , v_control - , EXTRACT('epoch' from v_partition_timestamp_start)::bigint * 1000 - , v_control - , EXTRACT('epoch' from v_partition_timestamp_end)::bigint * 1000); - ELSIF v_epoch = 'nanoseconds' THEN - EXECUTE format('ALTER TABLE %I.%I ADD CONSTRAINT %I CHECK (%I >= %L AND %I < %L)' - , v_parent_schema - , v_partition_name - , v_partition_name||'_partition_int_check' - , v_control - , EXTRACT('epoch' from v_partition_timestamp_start)::bigint * 1000000000 - , v_control - , EXTRACT('epoch' from v_partition_timestamp_end)::bigint * 1000000000); - END IF; - - EXECUTE format('ALTER TABLE %I.%I INHERIT %I.%I' - , v_parent_schema - , v_partition_name - , v_parent_schema - , v_parent_tablename); - - -- If custom time, set extra config options. - IF v_partition_type = 'time-custom' THEN - INSERT INTO @extschema@.custom_time_partitions (parent_table, child_table, partition_range) - VALUES ( p_parent_table, v_parent_schema||'.'||v_partition_name, tstzrange(v_partition_timestamp_start, v_partition_timestamp_end, '[)') ); - END IF; - - -- Indexes cannot be created on the parent, so clustering cannot be used for native yet. - PERFORM @extschema@.apply_cluster(v_parent_schema, v_parent_tablename, v_parent_schema, v_partition_name); - - -- Foreign keys to other tables not supported in native - IF v_inherit_fk THEN - PERFORM @extschema@.apply_foreign_keys(p_parent_table, v_parent_schema||'.'||v_partition_name, v_job_id); - END IF; - - END IF; -- end native check + END IF; - -- NOTE: Privileges not automatically inherited for native. Only do so if config flag is set - IF v_partition_type != 'native' OR (v_partition_type = 'native' AND v_inherit_privileges = TRUE) THEN + -- NOTE: Privileges not automatically inherited. Only do so if config flag is set + IF v_inherit_privileges = TRUE THEN PERFORM @extschema@.apply_privileges(v_parent_schema, v_parent_tablename, v_parent_schema, v_partition_name, v_job_id); END IF; @@ -368,36 +255,32 @@ FOREACH v_time IN ARRAY p_partition_times LOOP END IF; -- Will only loop once and only if sub_partitioning is actually configured - -- This seemed easier than assigning a bunch of variables then doing an IF condition - -- This column list must be kept consistent between: + -- This seemed easier than assigning a bunch of variables and doing an IF condition + -- This column list must be kept consistent between: -- create_parent, check_subpart_sameconfig, create_partition_id, create_partition_time, dump_partitioned_table_definition, and table definition - FOR v_row IN - SELECT sub_parent - , sub_partition_type + FOR v_row IN + SELECT + sub_parent , sub_control , sub_partition_interval - , sub_constraint_cols + , sub_partition_type , sub_premake - , sub_optimize_trigger - , sub_optimize_constraint - , sub_epoch - , sub_inherit_fk + , sub_automatic_maintenance + , sub_template_table , sub_retention , sub_retention_schema - , sub_retention_keep_table , sub_retention_keep_index + , sub_retention_keep_table + , sub_epoch + , sub_constraint_cols + , sub_optimize_constraint , sub_infinite_time_partitions - , sub_automatic_maintenance , sub_jobmon - , sub_trigger_exception_handling - , sub_upsert - , sub_trigger_return_null - , sub_template_table , sub_inherit_privileges , sub_constraint_valid - , sub_subscription_refresh , sub_date_trunc_interval , sub_ignore_default_data + , sub_default_table FROM @extschema@.part_config_sub WHERE sub_parent = p_parent_table LOOP @@ -407,12 +290,12 @@ FOREACH v_time IN ARRAY p_partition_times LOOP v_sql := format('SELECT @extschema@.create_parent( p_parent_table := %L , p_control := %L - , p_type := %L , p_interval := %L + , p_type := %L + , p_default_table := %L , p_constraint_cols := %L , p_premake := %L , p_automatic_maintenance := %L - , p_inherit_fk := %L , p_epoch := %L , p_template_table := %L , p_jobmon := %L @@ -420,63 +303,39 @@ FOREACH v_time IN ARRAY p_partition_times LOOP , p_date_trunc_interval := %L )' , v_parent_schema||'.'||v_partition_name , v_row.sub_control - , v_row.sub_partition_type , v_row.sub_partition_interval + , v_row.sub_partition_type + , v_row.sub_default_table , v_row.sub_constraint_cols , v_row.sub_premake , v_row.sub_automatic_maintenance - , v_row.sub_inherit_fk , v_row.sub_epoch , v_row.sub_template_table , v_row.sub_jobmon , p_start_partition , v_row.sub_date_trunc_interval); - + RAISE DEBUG 'create_partition_time (create_parent loop): %', v_sql; EXECUTE v_sql; - UPDATE @extschema@.part_config SET + UPDATE @extschema@.part_config SET retention_schema = v_row.sub_retention_schema , retention_keep_table = v_row.sub_retention_keep_table - , retention_keep_index = v_row.sub_retention_keep_index - , optimize_trigger = v_row.sub_optimize_trigger , optimize_constraint = v_row.sub_optimize_constraint , infinite_time_partitions = v_row.sub_infinite_time_partitions - , trigger_exception_handling = v_row.sub_trigger_exception_handling - , upsert = v_row.sub_upsert , inherit_privileges = v_row.sub_inherit_privileges - , trigger_return_null = v_row.sub_trigger_return_null , constraint_valid = v_row.sub_constraint_valid - , subscription_refresh = v_row.sub_subscription_refresh , ignore_default_data = v_row.sub_ignore_default_data WHERE parent_table = v_parent_schema||'.'||v_partition_name; END LOOP; -- end sub partitioning LOOP - -- Manage additonal constraints if set + -- Manage additional constraints if set PERFORM @extschema@.apply_constraints(p_parent_table, p_job_id := v_job_id); - IF v_publications IS NOT NULL THEN - -- NOTE: Native publication inheritance is only supported on PG14+ - PERFORM @extschema@.apply_publications(p_parent_table, v_parent_schema, v_partition_name); - END IF; - v_partition_created := true; END LOOP; --- v_analyze is a local check if a new table is made. --- p_analyze is a parameter to say whether to run the analyze at all. Used by create_parent() to avoid long exclusive lock or run_maintenence() to avoid long creation runs. -IF v_analyze AND p_analyze THEN - IF v_jobmon_schema IS NOT NULL THEN - v_step_id := add_step(v_job_id, format('Analyzing partition set: %s', p_parent_table)); - END IF; - - EXECUTE format('ANALYZE %I.%I', v_parent_schema, v_parent_tablename); - - IF v_jobmon_schema IS NOT NULL THEN - PERFORM update_step(v_step_id, 'OK', 'Done'); - END IF; -END IF; IF v_jobmon_schema IS NOT NULL THEN IF v_partition_created = false THEN @@ -517,4 +376,3 @@ DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; - diff --git a/sql/functions/create_sub_parent.sql b/sql/functions/create_sub_parent.sql index 67da7b0b..a753617e 100644 --- a/sql/functions/create_sub_parent.sql +++ b/sql/functions/create_sub_parent.sql @@ -1,20 +1,19 @@ CREATE FUNCTION @extschema@.create_sub_parent( p_top_parent text , p_control text - , p_type text , p_interval text - , p_native_check text DEFAULT NULL - , p_constraint_cols text[] DEFAULT NULL + , p_type text DEFAULT 'range' + , p_default_table boolean DEFAULT true + , p_declarative_check text DEFAULT NULL + , p_constraint_cols text[] DEFAULT NULL , p_premake int DEFAULT 4 , p_start_partition text DEFAULT NULL - , p_inherit_fk boolean DEFAULT true - , p_epoch text DEFAULT 'none' - , p_upsert text DEFAULT '' - , p_trigger_return_null boolean DEFAULT true + , p_epoch text DEFAULT 'none' , p_jobmon boolean DEFAULT true - , p_date_trunc_interval text DEFAULT NULL) -RETURNS boolean - LANGUAGE plpgsql + , p_date_trunc_interval text DEFAULT NULL +) + RETURNS boolean + LANGUAGE plpgsql AS $$ DECLARE @@ -24,25 +23,19 @@ v_child_start_time timestamptz; v_control text; v_control_parent_type text; v_control_sub_type text; -v_last_partition text; v_parent_epoch text; v_parent_interval text; -v_parent_relkind char; v_parent_schema text; v_parent_tablename text; -v_parent_type text; v_part_col text; v_partition_id_array bigint[]; v_partition_time_array timestamptz[]; v_relkind char; v_recreate_child boolean := false; v_row record; -v_row_last_part record; -v_run_maint boolean; v_sql text; v_success boolean := false; v_template_table text; -v_top_type text; BEGIN /* @@ -52,7 +45,7 @@ BEGIN * Uses another config table that allows for turning all future child partitions into a new parent automatically. */ -SELECT n.nspname, c.relname, c.relkind INTO v_parent_schema, v_parent_tablename, v_parent_relkind +SELECT n.nspname, c.relname INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid WHERE n.nspname = split_part(p_top_parent, '.', 1)::name @@ -65,67 +58,50 @@ IF NOT @extschema@.check_partition_type(p_type) THEN RAISE EXCEPTION '% is not a valid partitioning type', p_type; END IF; -IF v_parent_relkind = 'p' AND p_type <> 'native' THEN - RAISE EXCEPTION 'Cannot create a non-native sub-partition of a native parent table. All levels of a sub-partition set must be either all native or all non-native'; -END IF; - -SELECT partition_type, partition_interval, control, automatic_maintenance, epoch, template_table -INTO v_parent_type, v_parent_interval, v_control, v_run_maint, v_parent_epoch, v_template_table -FROM @extschema@.part_config +SELECT partition_interval, control, epoch, template_table +INTO v_parent_interval, v_control, v_parent_epoch, v_template_table +FROM @extschema@.part_config WHERE parent_table = p_top_parent; -IF v_parent_type IS NULL THEN +IF v_parent_interval IS NULL THEN RAISE EXCEPTION 'Cannot subpartition a table that is not managed by pg_partman already. Given top parent table not found in @extschema@.part_config: %', p_top_parent; END IF; -IF p_type = 'native' AND (lower(p_native_check) <> 'yes' OR p_native_check IS NULL) THEN - RAISE EXCEPTION 'The sub-partitioning of a natively partitioned table is a DESTRUCTIVE process unless all child tables are already natively subpartitioned. All child tables, and therefore ALL DATA, may be destroyed since the parent table must be declared as partitioned on first creation and cannot be altered later. See docs for more info. Set p_native_check parameter to "yes" if you are sure this is ok.'; -END IF; - -IF p_upsert <> '' THEN - IF current_setting('server_version_num')::int < 90500 THEN - RAISE EXCEPTION 'INSERT ... ON CONFLICT (UPSERT) feature is only supported in PostgreSQL 9.5 and later'; - END IF; - IF p_type = 'native' AND current_setting('server_version_num')::int >= 110000 THEN - RAISE EXCEPTION 'The pg_partman upsert feature is not supported with native partitioning in PG11+. Use the built-in support for INSERT ON CONFLICT with native partitioning instead.'; - END IF; +IF (lower(p_declarative_check) <> 'yes' OR p_declarative_check IS NULL) THEN + RAISE EXCEPTION 'Subpartitioning is a DESTRUCTIVE process unless all child tables are already themselves subpartitioned. All child tables, and therefore ALL DATA, may be destroyed since the parent table must be declared as partitioned on first creation and cannot be altered later. See docs for more info. Set p_declarative_check parameter to "yes" if you are sure this is ok.'; END IF; SELECT general_type INTO v_control_parent_type FROM @extschema@.check_control_type(v_parent_schema, v_parent_tablename, v_control); --- Add the given parameters to the part_config_sub table first in case create_partition_* functions are called below --- All sub-partition parents must use the same template table for native partitioning, so ensure the one from the given parent is obtained and used. +-- Add the given parameters to the part_config_sub table first in case create_partition_* functions are called below +-- All sub-partition parents must use the same template table, so ensure the one from the given parent is obtained and used. INSERT INTO @extschema@.part_config_sub ( sub_parent , sub_control - , sub_partition_type , sub_partition_interval + , sub_partition_type + , sub_default_table , sub_constraint_cols , sub_premake - , sub_inherit_fk , sub_automatic_maintenance , sub_epoch - , sub_upsert , sub_jobmon - , sub_trigger_return_null , sub_template_table , sub_date_trunc_interval) VALUES ( p_top_parent , p_control - , p_type , p_interval + , p_type + , p_default_table , p_constraint_cols , p_premake - , p_inherit_fk - , 'on' + , 'on' , p_epoch - , p_upsert , p_jobmon - , p_trigger_return_null , v_template_table , p_date_trunc_interval); -FOR v_row IN +FOR v_row IN -- Loop through all current children to turn them into partitioned tables SELECT partition_schemaname AS child_schema, partition_tablename AS child_tablename FROM @extschema@.show_partitions(p_top_parent) LOOP @@ -141,36 +117,18 @@ LOOP -- If both parent and sub-parent are the same partition type (time/id), ensure intereval of sub-parent is less than parent IF (v_control_parent_type = 'time' AND v_control_sub_type = 'time') OR (v_control_parent_type = 'id' AND v_parent_epoch <> 'none' AND v_control_sub_type = 'id' AND p_epoch <> 'none') THEN - CASE - WHEN p_interval = 'yearly' THEN - v_child_interval := '1 year'; - WHEN p_interval = 'quarterly' THEN - v_child_interval := '3 months'; - WHEN p_interval = 'monthly' THEN - v_child_interval := '1 month'; - WHEN p_interval = 'weekly' THEN - v_child_interval := '1 week'; - WHEN p_interval = 'daily' THEN - v_child_interval := '1 day'; - WHEN p_interval = 'hourly' THEN - v_child_interval := '1 hour'; - WHEN p_interval = 'half-hour' THEN - v_child_interval := '30 mins'; - WHEN p_interval = 'quarter-hour' THEN - v_child_interval := '15 mins'; - ELSE - v_child_interval := p_interval::interval; - IF v_child_interval < '1 second'::interval THEN - RAISE EXCEPTION 'Partitioning interval must be 1 second or greater'; - END IF; - END CASE; + + v_child_interval := p_interval::interval; + IF v_child_interval < '1 second'::interval THEN + RAISE EXCEPTION 'Partitioning interval must be 1 second or greater'; + END IF; IF v_child_interval >= v_parent_interval::interval THEN RAISE EXCEPTION 'Sub-partition interval cannot be greater than or equal to the given parent interval'; END IF; IF (v_child_interval = '1 week' AND v_parent_interval::interval > '1 week'::interval) OR (p_date_trunc_interval = 'week') THEN - RAISE EXCEPTION 'Due to conflicting data boundaries between ISO weeks and any larger interval of time, pg_partman cannot support a sub-partition interval of weekly time periods'; + RAISE EXCEPTION 'Due to conflicting data boundaries between weeks and any larger interval of time, pg_partman cannot support a sub-partition interval of weekly time periods'; END IF; ELSIF v_control_parent_type = 'id' AND v_control_sub_type = 'id' AND v_parent_epoch = 'none' AND p_epoch = 'none' THEN @@ -178,46 +136,43 @@ LOOP RAISE EXCEPTION 'Sub-partition interval cannot be greater than or equal to the given parent interval'; END IF; END IF; - - IF p_type = 'native' THEN - IF v_relkind <> 'p' THEN - -- Not natively partitioned already. Drop it and recreate as such. - RAISE WARNING 'Child table % is not natively partitioned. Dropping and recreating with native partitioning' - , v_row.child_schema||'.'||v_row.child_tablename; - SELECT child_start_time, child_start_id INTO v_child_start_time, v_child_start_id - FROM @extschema@.show_partition_info(v_row.child_schema||'.'||v_row.child_tablename - , v_parent_interval - , p_top_parent); - EXECUTE format('DROP TABLE %I.%I', v_row.child_schema, v_row.child_tablename); - v_recreate_child := true; - - IF v_child_start_id IS NOT NULL THEN - v_partition_id_array[0] := v_child_start_id; - PERFORM @extschema@.create_partition_id(p_top_parent, v_partition_id_array, true, p_start_partition); - ELSIF v_child_start_time IS NOT NULL THEN - v_partition_time_array[0] := v_child_start_time; - PERFORM @extschema@.create_partition_time(p_top_parent, v_partition_time_array, true, p_start_partition); - END IF; - ELSE - SELECT a.attname - INTO v_part_col - FROM pg_attribute a - JOIN pg_class c ON a.attrelid = c.oid - JOIN pg_namespace n ON c.relnamespace = n.oid - WHERE n.nspname = v_row.child_schema::name - AND c.relname = v_row.child_tablename::name - AND attnum IN (SELECT unnest(partattrs) FROM pg_partitioned_table p WHERE a.attrelid = p.partrelid); - - IF p_control <> v_part_col THEN - RAISE EXCEPTION 'Attempted to natively sub-partition an existing table that has the partition column (%) defined differently than the control column given (%)', v_part_col, p_control; - ELSE -- Child table is already natively subpartitioned properly. Skip the rest. - CONTINUE; - END IF; - END IF; -- end 'p' relkind check - END IF; -- end native check + IF v_relkind <> 'p' THEN + -- Not partitioned already. Drop it and recreate as such. + RAISE WARNING 'Child table % is not partitioned. Dropping and recreating with partitioning' + , v_row.child_schema||'.'||v_row.child_tablename; + SELECT child_start_time, child_start_id INTO v_child_start_time, v_child_start_id + FROM @extschema@.show_partition_info(v_row.child_schema||'.'||v_row.child_tablename + , v_parent_interval + , p_top_parent); + EXECUTE format('DROP TABLE %I.%I', v_row.child_schema, v_row.child_tablename); + v_recreate_child := true; + + IF v_child_start_id IS NOT NULL THEN + v_partition_id_array[0] := v_child_start_id; + PERFORM @extschema@.create_partition_id(p_top_parent, v_partition_id_array, p_start_partition); + ELSIF v_child_start_time IS NOT NULL THEN + v_partition_time_array[0] := v_child_start_time; + PERFORM @extschema@.create_partition_time(p_top_parent, v_partition_time_array, p_start_partition); + END IF; + ELSE + SELECT a.attname + INTO v_part_col + FROM pg_attribute a + JOIN pg_class c ON a.attrelid = c.oid + JOIN pg_namespace n ON c.relnamespace = n.oid + WHERE n.nspname = v_row.child_schema::name + AND c.relname = v_row.child_tablename::name + AND attnum IN (SELECT unnest(partattrs) FROM pg_partitioned_table p WHERE a.attrelid = p.partrelid); + + IF p_control <> v_part_col THEN + RAISE EXCEPTION 'Attempted to sub-partition an existing table that has the partition column (%) defined differently than the control column given (%)', v_part_col, p_control; + ELSE -- Child table is already subpartitioned properly. Skip the rest. + CONTINUE; + END IF; + END IF; -- end 'p' relkind check - IF v_recreate_child = false THEN +IF v_recreate_child = false THEN -- Always call create_parent() if child table wasn't recreated above. -- If it was, the create_partition_*() functions called above also call create_parent if any of the tables -- it creates are in the part_config_sub table. Since it was inserted there above, @@ -225,34 +180,31 @@ LOOP v_sql := format('SELECT @extschema@.create_parent( p_parent_table := %L , p_control := %L - , p_type := %L , p_interval := %L + , p_type := %L + , p_default_table := %L , p_constraint_cols := %L , p_premake := %L , p_automatic_maintenance := %L , p_start_partition := %L - , p_inherit_fk := %L , p_epoch := %L - , p_upsert := %L - , p_trigger_return_null := %L , p_template_table := %L , p_jobmon := %L , p_date_trunc_interval := %L)' , v_row.child_schema||'.'||v_row.child_tablename , p_control - , p_type , p_interval + , p_type + , p_default_table , p_constraint_cols , p_premake , 'on' , p_start_partition - , p_inherit_fk , p_epoch - , p_upsert - , p_trigger_return_null , v_template_table , p_jobmon , p_date_trunc_interval); + RAISE DEBUG 'create_sub_parent: create parent v_sql: %', v_sql; EXECUTE v_sql; END IF; -- end recreate check @@ -264,4 +216,3 @@ RETURN v_success; END $$; - diff --git a/sql/functions/drop_partition_column.sql b/sql/functions/drop_partition_column.sql deleted file mode 100644 index 67ac6e56..00000000 --- a/sql/functions/drop_partition_column.sql +++ /dev/null @@ -1,42 +0,0 @@ -CREATE FUNCTION @extschema@.drop_partition_column(p_parent_table text, p_column text) RETURNS void - LANGUAGE plpgsql - AS $$ -DECLARE - -v_parent_oid oid; -v_parent_schema text; -v_parent_tablename text; -v_row record; - -BEGIN -/* - * Function to ensure a column is dropped in all child tables, no matter when it was created - */ -SELECT c.oid, n.nspname, c.relname INTO v_parent_oid, v_parent_schema, v_parent_tablename -FROM pg_catalog.pg_class c -JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid -WHERE n.nspname = split_part(p_parent_table, '.', 1)::name -AND c.relname = split_part(p_parent_table, '.', 2)::name; - -IF v_parent_oid IS NULL THEN - RAISE EXCEPTION 'Given parent table does not exist: %', p_parent_table; -END IF; - -EXECUTE format('ALTER TABLE %I.%I DROP COLUMN IF EXISTS %I', v_parent_schema, v_parent_tablename, p_column); - -FOR v_row IN - SELECT n.nspname AS child_schema, c.relname AS child_table - FROM pg_catalog.pg_inherits h - JOIN pg_catalog.pg_class c ON h.inhrelid = c.oid - JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid - WHERE inhparent = v_parent_oid -LOOP - - EXECUTE format('ALTER TABLE %I.%I DROP COLUMN IF EXISTS %I', v_row.child_schema, v_row.child_table, p_column); - -END LOOP; - -END -$$; - - diff --git a/sql/functions/drop_partition_id.sql b/sql/functions/drop_partition_id.sql index 00c410ef..ec4f670d 100644 --- a/sql/functions/drop_partition_id.sql +++ b/sql/functions/drop_partition_id.sql @@ -1,4 +1,11 @@ -CREATE FUNCTION @extschema@.drop_partition_id(p_parent_table text, p_retention bigint DEFAULT NULL, p_keep_table boolean DEFAULT NULL, p_keep_index boolean DEFAULT NULL, p_retention_schema text DEFAULT NULL) RETURNS int +CREATE FUNCTION @extschema@.drop_partition_id( + p_parent_table text + , p_retention bigint DEFAULT NULL + , p_keep_table boolean DEFAULT NULL + , p_keep_index boolean DEFAULT NULL + , p_retention_schema text DEFAULT NULL +) + RETURNS int LANGUAGE plpgsql AS $$ DECLARE @@ -11,7 +18,6 @@ v_adv_lock boolean; v_control text; v_control_type text; v_count int; -v_drop_cascade_fk boolean; v_drop_count int := 0; v_index record; v_job_id bigint; @@ -24,7 +30,6 @@ v_parent_schema text; v_parent_tablename text; v_partition_interval bigint; v_partition_id bigint; -v_partition_type text; v_retention bigint; v_retention_keep_index boolean; v_retention_keep_table boolean; @@ -37,8 +42,8 @@ v_sub_parent text; BEGIN /* - * Function to drop child tables from an id-based partition set. - * Options to move table to different schema, drop only indexes or actually drop the table from the database. + * Function to drop child tables from an id-based partition set. + * Options to move table to different schema or actually drop the table from the database. */ v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman drop_partition_id')); @@ -48,53 +53,45 @@ IF v_adv_lock = 'false' THEN END IF; IF p_retention IS NULL THEN - SELECT + SELECT partition_interval::bigint - , partition_type , control , retention::bigint , retention_keep_table , retention_keep_index , retention_schema , jobmon - , drop_cascade_fk INTO v_partition_interval - , v_partition_type , v_control , v_retention , v_retention_keep_table , v_retention_keep_index , v_retention_schema , v_jobmon - , v_drop_cascade_fk - FROM @extschema@.part_config - WHERE parent_table = p_parent_table + FROM @extschema@.part_config + WHERE parent_table = p_parent_table AND retention IS NOT NULL; IF v_partition_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table with a retention period not found: %', p_parent_table; END IF; ELSE -- Allow override of configuration options - SELECT + SELECT partition_interval::bigint - , partition_type , control , retention_keep_table , retention_keep_index , retention_schema , jobmon - , drop_cascade_fk INTO v_partition_interval - , v_partition_type , v_control , v_retention_keep_table , v_retention_keep_index , v_retention_schema , v_jobmon - , v_drop_cascade_fk - FROM @extschema@.part_config + FROM @extschema@.part_config WHERE parent_table = p_parent_table; v_retention := p_retention; @@ -138,7 +135,7 @@ WHERE schemaname = split_part(p_parent_table, '.', 1)::name AND tablename = split_part(p_parent_table, '.', 2)::name; -- Loop through child tables starting from highest to get current max value in partition set --- Avoids doing a scan on entire partition set and/or getting any values accidentally in parent. +-- Avoids doing a scan on entire partition set and/or getting any values accidentally in default. FOR v_row_max_id IN SELECT partition_schemaname, partition_tablename FROM @extschema@.show_partitions(p_parent_table, 'DESC') LOOP @@ -152,7 +149,7 @@ SELECT sub_parent INTO v_sub_parent FROM @extschema@.part_config_sub WHERE sub_p -- Loop through child tables of the given parent -- Must go in ascending order to avoid dropping what may be the "last" partition in the set after dropping tables that match retention period -FOR v_row IN +FOR v_row IN SELECT partition_schemaname, partition_tablename FROM @extschema@.show_partitions(p_parent_table, 'ASC') LOOP SELECT child_start_id INTO v_partition_id FROM @extschema@.show_partition_info(v_row.partition_schemaname||'.'||v_row.partition_tablename @@ -165,7 +162,7 @@ LOOP -- Do not allow final partition to be dropped if it is not a sub-partition parent SELECT count(*) INTO v_count FROM @extschema@.show_partitions(p_parent_table); IF v_count = 1 AND v_sub_parent IS NULL THEN - RAISE WARNING 'Attempt to drop final partition in partition set % as part of retention policy. If you see this message multiple times for the same table, advise reviewing retention policy and/or data entry into the partition set. Also consider setting "infinite_time_partitions = true" if there are large gaps in data insertion.).', p_parent_table; + RAISE WARNING 'Attempt to drop final partition in partition set % as part of retention policy. If you see this message multiple times for the same table, advise reviewing retention policy and/or data entry into the partition set. Also consider setting "infinite_time_partitions = true" if there are large gaps in data insertion.', p_parent_table; CONTINUE; END IF; @@ -180,24 +177,47 @@ LOOP IF v_retention_keep_table = true OR v_retention_schema IS NOT NULL THEN -- No need to detach partition before dropping since it's going away anyway + -- TODO Review this to see how to handle based on recent FK issues -- Avoids issue of FKs not allowing detachment (Github Issue #294). - IF v_partition_type = 'native' THEN - v_sql := format('ALTER TABLE %I.%I DETACH PARTITION %I.%I' - , v_parent_schema - , v_parent_tablename - , v_row.partition_schemaname - , v_row.partition_tablename); - EXECUTE v_sql; - ELSE - EXECUTE format('ALTER TABLE %I.%I NO INHERIT %I.%I' - , v_row.partition_schemaname - , v_row.partition_tablename - , v_parent_schema - , v_parent_tablename); - IF v_jobmon_schema IS NOT NULL THEN - PERFORM update_step(v_step_id, 'OK', 'Done'); - END IF; - END IF; + v_sql := format('ALTER TABLE %I.%I DETACH PARTITION %I.%I' + , v_parent_schema + , v_parent_tablename + , v_row.partition_schemaname + , v_row.partition_tablename); + EXECUTE v_sql; + + IF v_retention_keep_index = false THEN + FOR v_index IN + WITH child_info AS ( + SELECT c1.oid + FROM pg_catalog.pg_class c1 + JOIN pg_catalog.pg_namespace n1 ON c1.relnamespace = n1.oid + WHERE c1.relname = v_row.partition_tablename::name + AND n1.nspname = v_row.partition_schemaname::name + ) + SELECT c.relname as name + , con.conname + FROM pg_catalog.pg_index i + JOIN pg_catalog.pg_class c ON i.indexrelid = c.oid + LEFT JOIN pg_catalog.pg_constraint con ON i.indexrelid = con.conindid + JOIN child_info ON i.indrelid = child_info.oid + LOOP + IF v_jobmon_schema IS NOT NULL THEN + v_step_id := add_step(v_job_id, format('Drop index %s from %s.%s' + , v_index.name + , v_row.partition_schemaname + , v_row.partition_tablename)); + END IF; + IF v_index.conname IS NOT NULL THEN + EXECUTE format('ALTER TABLE %I.%I DROP CONSTRAINT %I', v_row.partition_schemaname, v_row.partition_tablename, v_index.conname); + ELSE + EXECUTE format('DROP INDEX %I.%I', v_row.partition_schemaname, v_index.name); + END IF; + IF v_jobmon_schema IS NOT NULL THEN + PERFORM update_step(v_step_id, 'OK', 'Done'); + END IF; + END LOOP; + END IF; -- end v_retention_keep_index IF END IF; IF v_retention_schema IS NULL THEN @@ -206,49 +226,11 @@ LOOP v_step_id := add_step(v_job_id, format('Drop table %s.%s', v_row.partition_schemaname, v_row.partition_tablename)); END IF; v_sql := 'DROP TABLE %I.%I'; - IF v_drop_cascade_fk OR v_sub_parent IS NOT NULL THEN - v_sql := v_sql || ' CASCADE'; - END IF; EXECUTE format(v_sql, v_row.partition_schemaname, v_row.partition_tablename); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; - ELSIF v_retention_keep_index = false THEN - IF v_partition_type = 'partman' OR - ( v_partition_type = 'native' AND current_setting('server_version_num')::int < 110000) THEN - -- Cannot drop child indexes on native partition sets in PG11+ - FOR v_index IN - WITH child_info AS ( - SELECT c1.oid - FROM pg_catalog.pg_class c1 - JOIN pg_catalog.pg_namespace n1 ON c1.relnamespace = n1.oid - WHERE c1.relname = v_row.partition_tablename::name - AND n1.nspname = v_row.partition_schema::name - ) - SELECT c.relname as name - , con.conname - FROM pg_catalog.pg_index i - JOIN pg_catalog.pg_class c ON i.indexrelid = c.oid - LEFT JOIN pg_catalog.pg_constraint con ON i.indexrelid = con.conindid - JOIN child_info ON i.indrelid = child_info.oid - LOOP - IF v_jobmon_schema IS NOT NULL THEN - v_step_id := add_step(v_job_id, format('Drop index %s from %s.%s' - , v_index.name - , v_row.partition_schemaname - , v_row.partition_tablename)); - END IF; - IF v_index.conname IS NOT NULL THEN - EXECUTE format('ALTER TABLE %I.%I DROP CONSTRAINT %I', v_row.partition_schemaname, v_row.partition_tablename, v_index.conname); - ELSE - EXECUTE format('DROP INDEX %I.%I', v_row.partition_schemaname, v_index.name); - END IF; - IF v_jobmon_schema IS NOT NULL THEN - PERFORM update_step(v_step_id, 'OK', 'Done'); - END IF; - END LOOP; - END IF; -- end native/11 check - END IF; -- end v_retention_keep_index IF + END IF; ELSE -- Move to new schema IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, format('Moving table %s.%s to schema %s' @@ -309,5 +291,3 @@ DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; - - diff --git a/sql/functions/drop_partition_time.sql b/sql/functions/drop_partition_time.sql index 353a748a..5e6199f0 100644 --- a/sql/functions/drop_partition_time.sql +++ b/sql/functions/drop_partition_time.sql @@ -1,4 +1,12 @@ -CREATE FUNCTION @extschema@.drop_partition_time(p_parent_table text, p_retention interval DEFAULT NULL, p_keep_table boolean DEFAULT NULL, p_keep_index boolean DEFAULT NULL, p_retention_schema text DEFAULT NULL, p_reference_timestamp timestamptz DEFAULT CURRENT_TIMESTAMP) RETURNS int +CREATE FUNCTION @extschema@.drop_partition_time( + p_parent_table text + , p_retention interval DEFAULT NULL + , p_keep_table boolean DEFAULT NULL + , p_keep_index boolean DEFAULT NULL + , p_retention_schema text DEFAULT NULL + , p_reference_timestamp timestamptz DEFAULT CURRENT_TIMESTAMP +) + RETURNS int LANGUAGE plpgsql AS $$ DECLARE @@ -11,8 +19,6 @@ v_adv_lock boolean; v_control text; v_control_type text; v_count int; -v_datetime_string text; -v_drop_cascade_fk boolean; v_drop_count int := 0; v_epoch text; v_index record; @@ -25,7 +31,6 @@ v_parent_schema text; v_parent_tablename text; v_partition_interval interval; v_partition_timestamp timestamptz; -v_partition_type text; v_retention interval; v_retention_keep_index boolean; v_retention_keep_table boolean; @@ -49,59 +54,47 @@ END IF; -- Allow override of configuration options IF p_retention IS NULL THEN - SELECT - partition_type - , control + SELECT + control , partition_interval::interval , epoch , retention::interval , retention_keep_table , retention_keep_index - , drop_cascade_fk - , datetime_string , retention_schema , jobmon INTO - v_partition_type - , v_control + v_control , v_partition_interval , v_epoch , v_retention , v_retention_keep_table , v_retention_keep_index - , v_drop_cascade_fk - , v_datetime_string , v_retention_schema , v_jobmon - FROM @extschema@.part_config - WHERE parent_table = p_parent_table + FROM @extschema@.part_config + WHERE parent_table = p_parent_table AND retention IS NOT NULL; IF v_partition_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table with a retention period not found: %', p_parent_table; END IF; ELSE - SELECT - partition_type - , partition_interval::interval + SELECT + partition_interval::interval , epoch , retention_keep_table , retention_keep_index - , drop_cascade_fk - , datetime_string , retention_schema , jobmon INTO - v_partition_type - , v_partition_interval + v_partition_interval , v_epoch , v_retention_keep_table , v_retention_keep_index - , v_drop_cascade_fk - , v_datetime_string , v_retention_schema , v_jobmon - FROM @extschema@.part_config + FROM @extschema@.part_config WHERE parent_table = p_parent_table; v_retention := p_retention; @@ -111,7 +104,7 @@ ELSE END IF; SELECT general_type INTO v_control_type FROM @extschema@.check_control_type(v_parent_schema, v_parent_tablename, v_control); -IF v_control_type <> 'time' THEN +IF v_control_type <> 'time' THEN IF (v_control_type = 'id' AND v_epoch = 'none') OR v_control_type <> 'id' THEN RAISE EXCEPTION 'Cannot run on partition set without time based control column or epoch flag set with an id column. Found control: %, epoch: %', v_control_type, v_epoch; END IF; @@ -150,7 +143,7 @@ SELECT sub_parent INTO v_sub_parent FROM @extschema@.part_config_sub WHERE sub_p -- Loop through child tables of the given parent -- Must go in ascending order to avoid dropping what may be the "last" partition in the set after dropping tables that match retention period -FOR v_row IN +FOR v_row IN SELECT partition_schemaname, partition_tablename FROM @extschema@.show_partitions(p_parent_table, 'ASC') LOOP -- pull out datetime portion of partition's tablename to make the next one @@ -180,47 +173,17 @@ LOOP END IF; IF v_retention_keep_table = true OR v_retention_schema IS NOT NULL THEN -- No need to detach partition before dropping since it's going away anyway + -- TODO Review this to see how to handle based on recent FK issues -- Avoids issue of FKs not allowing detachment (Github Issue #294). - IF v_partition_type = 'native' THEN - v_sql := format('ALTER TABLE %I.%I DETACH PARTITION %I.%I' - , v_parent_schema - , v_parent_tablename - , v_row.partition_schemaname - , v_row.partition_tablename); - EXECUTE v_sql; - ELSE - EXECUTE format('ALTER TABLE %I.%I NO INHERIT %I.%I' - , v_row.partition_schemaname - , v_row.partition_tablename - , v_parent_schema - , v_parent_tablename); - END IF; - END IF; - IF v_partition_type = 'time-custom' THEN - DELETE FROM @extschema@.custom_time_partitions WHERE parent_table = p_parent_table AND child_table = v_row.partition_schemaname||'.'||v_row.partition_tablename; - END IF; - IF v_jobmon_schema IS NOT NULL THEN - PERFORM update_step(v_step_id, 'OK', 'Done'); - END IF; + v_sql := format('ALTER TABLE %I.%I DETACH PARTITION %I.%I' + , v_parent_schema + , v_parent_tablename + , v_row.partition_schemaname + , v_row.partition_tablename); + EXECUTE v_sql; - IF v_retention_schema IS NULL THEN - IF v_retention_keep_table = false THEN - IF v_jobmon_schema IS NOT NULL THEN - v_step_id := add_step(v_job_id, format('Drop table %s.%s', v_row.partition_schemaname, v_row.partition_tablename)); - END IF; - v_sql := 'DROP TABLE %I.%I'; - IF v_drop_cascade_fk OR v_sub_parent IS NOT NULL THEN - v_sql := v_sql || ' CASCADE'; - END IF; - EXECUTE format(v_sql, v_row.partition_schemaname, v_row.partition_tablename); - IF v_jobmon_schema IS NOT NULL THEN - PERFORM update_step(v_step_id, 'OK', 'Done'); - END IF; - ELSIF v_retention_keep_index = false THEN - IF v_partition_type = 'partman' OR - ( v_partition_type = 'native' AND current_setting('server_version_num')::int < 110000) THEN - -- Cannot drop child indexes on native partition sets in PG11+ - FOR v_index IN + IF v_retention_keep_index = false THEN + FOR v_index IN WITH child_info AS ( SELECT c1.oid FROM pg_catalog.pg_class c1 @@ -253,8 +216,24 @@ LOOP PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END LOOP; - END IF; -- end native/11 check END IF; -- end v_retention_keep_index IF + END IF; + + IF v_jobmon_schema IS NOT NULL THEN + PERFORM update_step(v_step_id, 'OK', 'Done'); + END IF; + + IF v_retention_schema IS NULL THEN + IF v_retention_keep_table = false THEN + IF v_jobmon_schema IS NOT NULL THEN + v_step_id := add_step(v_job_id, format('Drop table %s.%s', v_row.partition_schemaname, v_row.partition_tablename)); + END IF; + v_sql := 'DROP TABLE %I.%I'; + EXECUTE format(v_sql, v_row.partition_schemaname, v_row.partition_tablename); + IF v_jobmon_schema IS NOT NULL THEN + PERFORM update_step(v_step_id, 'OK', 'Done'); + END IF; + END IF; ELSE -- Move to new schema IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, format('Moving table %s.%s to schema %s' @@ -313,4 +292,3 @@ DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; - diff --git a/sql/functions/dump_partitioned_table_definition.sql b/sql/functions/dump_partitioned_table_definition.sql index d86ea390..1623a2da 100644 --- a/sql/functions/dump_partitioned_table_definition.sql +++ b/sql/functions/dump_partitioned_table_definition.sql @@ -1,206 +1,157 @@ CREATE FUNCTION @extschema@.dump_partitioned_table_definition( - p_parent_table TEXT, - p_ignore_template_table BOOLEAN DEFAULT false -) RETURNS TEXT - LANGUAGE PLPGSQL STABLE -AS $$ + p_parent_table text, + p_ignore_template_table boolean DEFAULT false +) + RETURNS text + LANGUAGE PLPGSQL STABLE + AS $$ DECLARE - v_create_parent_definition TEXT; - v_update_part_config_definition TEXT; - -- Columns from part_config table. - v_parent_table TEXT; -- NOT NULL - v_control TEXT; -- NOT NULL - v_partition_type TEXT; -- NOT NULL - v_partition_interval TEXT; -- NOT NULL - v_constraint_cols TEXT[]; - v_premake integer; -- NOT NULL - v_optimize_trigger integer; -- NOT NULL - v_optimize_constraint integer; -- NOT NULL - v_epoch text; -- NOT NULL - v_inherit_fk BOOLEAN; -- NOT NULL - v_retention TEXT; - v_retention_schema TEXT; - v_retention_keep_table BOOLEAN; -- NOT NULL - v_retention_keep_index BOOLEAN; -- NOT NULL - v_infinite_time_partitions BOOLEAN; -- NOT NULL - v_datetime_string TEXT; - v_automatic_maintenance TEXT; -- NOT NULL - v_jobmon BOOLEAN; -- NOT NULL - v_sub_partition_set_full BOOLEAN; -- NOT NULL - v_trigger_exception_handling BOOLEAN; - v_upsert TEXT; -- NOT NULL - v_trigger_return_null BOOLEAN; -- NOT NULL - v_template_table TEXT; - v_publications TEXT[]; - v_inherit_privileges BOOLEAN; -- DEFAULT false - v_constraint_valid BOOLEAN; -- DEFAULT true NOT NULL - v_subscription_refresh text; - v_drop_cascade_fk boolean; -- DEFAULT false NOT NULL - v_ignore_default_data boolean; -- DEFAULT false NOT NULL + v_create_parent_definition text; + v_update_part_config_definition text; + -- Columns from part_config table. + v_parent_table text; -- NOT NULL + v_control text; -- NOT NULL + v_partition_type text; -- NOT NULL + v_partition_interval text; -- NOT NULL + v_constraint_cols TEXT[]; + v_premake integer; -- NOT NULL + v_optimize_constraint integer; -- NOT NULL + v_epoch text; -- NOT NULL + v_retention text; + v_retention_schema text; + v_retention_keep_index boolean; + v_retention_keep_table boolean; -- NOT NULL + v_infinite_time_partitions boolean; -- NOT NULL + v_datetime_string text; + v_automatic_maintenance text; -- NOT NULL + v_jobmon boolean; -- NOT NULL + v_sub_partition_set_full boolean; -- NOT NULL + v_template_table text; + v_inherit_privileges boolean; -- DEFAULT false + v_constraint_valid boolean; -- DEFAULT true NOT NULL + v_ignore_default_data boolean; -- DEFAULT false NOT NULL + v_date_trunc_interval text; + v_default_table boolean ; BEGIN - SELECT - pc.parent_table, - pc.control, - pc.partition_type, - pc.partition_interval, - pc.constraint_cols, - pc.premake, - pc.optimize_trigger, - pc.optimize_constraint, - pc.epoch, - pc.inherit_fk, - pc.retention, - pc.retention_schema, - pc.retention_keep_table, - pc.retention_keep_index, - pc.infinite_time_partitions, - pc.datetime_string, - pc.automatic_maintenance, - pc.jobmon, - pc.sub_partition_set_full, - pc.trigger_exception_handling, - pc.upsert, - pc.trigger_return_null, - pc.template_table, - pc.publications, - pc.inherit_privileges, - pc.constraint_valid, - pc.subscription_refresh, - pc.drop_cascade_fk, - pc.ignore_default_data - INTO - v_parent_table, - v_control, - v_partition_type, - v_partition_interval, - v_constraint_cols, - v_premake, - v_optimize_trigger, - v_optimize_constraint, - v_epoch, - v_inherit_fk, - v_retention, - v_retention_schema, - v_retention_keep_table, - v_retention_keep_index, - v_infinite_time_partitions, - v_datetime_string, - v_automatic_maintenance, - v_jobmon, - v_sub_partition_set_full, - v_trigger_exception_handling, - v_upsert, - v_trigger_return_null, - v_template_table, - v_publications, - v_inherit_privileges, - v_constraint_valid, - v_subscription_refresh, - v_drop_cascade_fk, - v_ignore_default_data - FROM @extschema@.part_config pc - WHERE pc.parent_table = p_parent_table; + SELECT + pc.parent_table, + pc.control, + pc.partition_type, + pc.partition_interval, + pc.constraint_cols, + pc.premake, + pc.optimize_constraint, + pc.epoch, + pc.retention, + pc.retention_schema, + pc.retention_keep_index, + pc.retention_keep_table, + pc.infinite_time_partitions, + pc.datetime_string, + pc.automatic_maintenance, + pc.jobmon, + pc.sub_partition_set_full, + pc.template_table, + pc.inherit_privileges, + pc.constraint_valid, + pc.ignore_default_data, + pc.date_trunc_interval, + pc.default_table + INTO + v_parent_table, + v_control, + v_partition_type, + v_partition_interval, + v_constraint_cols, + v_premake, + v_optimize_constraint, + v_epoch, + v_retention, + v_retention_schema, + v_retention_keep_index, + v_retention_keep_table, + v_infinite_time_partitions, + v_datetime_string, + v_automatic_maintenance, + v_jobmon, + v_sub_partition_set_full, + v_template_table, + v_inherit_privileges, + v_constraint_valid, + v_ignore_default_data, + v_date_trunc_interval, + v_default_table + FROM @extschema@.part_config pc + WHERE pc.parent_table = p_parent_table; - IF v_partition_type = 'partman' THEN - CASE - WHEN v_partition_interval::INTERVAL = '1 year'::INTERVAL THEN - v_partition_interval := 'yearly'; - WHEN v_partition_interval::INTERVAL = '3 months'::INTERVAL THEN - v_partition_interval := 'quarterly'; - WHEN v_partition_interval::INTERVAL = '1 month'::INTERVAL THEN - v_partition_interval := 'monthly'; - WHEN v_partition_interval::INTERVAL = '1 week'::INTERVAL THEN - v_partition_interval := 'weekly'; - WHEN v_partition_interval::INTERVAL = '1 day'::INTERVAL THEN - v_partition_interval := 'daily'; - WHEN v_partition_interval::INTERVAL = '1 hour'::INTERVAL THEN - v_partition_interval := 'hourly'; - WHEN v_partition_interval::INTERVAL = '30 mins'::INTERVAL THEN - v_partition_interval := 'half-hour'; - WHEN v_partition_interval::INTERVAL = '15 mins'::INTERVAL THEN - v_partition_interval := 'quarter-hour'; - ELSE - RAISE EXCEPTION 'Partitioning interval not recognized for "partman" partitioning type'; - END CASE; - END IF; + IF v_parent_table IS NULL THEN + RAISE EXCEPTION 'Given parent table not found in pg_partman configuration table: %', p_parent_table; + END IF; - IF v_partition_type = 'native' AND p_ignore_template_table THEN - v_template_table := NULL; - END IF; + IF p_ignore_template_table THEN + v_template_table := NULL; + END IF; - v_create_parent_definition := format( + v_create_parent_definition := format( E'SELECT @extschema@.create_parent( \tp_parent_table := %L, \tp_control := %L, -\tp_type := %L, \tp_interval := %L, -\tp_constraint_cols := %L, +\tp_type := %L, +\tp_epoch := %L, \tp_premake := %s, +\tp_default_table := %L, \tp_automatic_maintenance := %L, -\tp_inherit_fk := %L, -\tp_epoch := %L, -\tp_upsert := %L, -\tp_publications := %L, -\tp_trigger_return_null := %L, +\tp_constraint_cols := %L, \tp_template_table := %L, -\tp_jobmon := %L -\t-- v_start_partition is intentionally ignored as there -\t-- isn''t any obviously correct definition. +\tp_jobmon := %L, +\tp_date_trunc_interval := %L );', - v_parent_table, - v_control, - v_partition_type, - v_partition_interval, - v_constraint_cols, - v_premake, - v_automatic_maintenance, - v_inherit_fk, - v_epoch, - v_upsert, - v_publications, - v_trigger_return_null, - v_template_table, - v_jobmon - ); + v_parent_table, + v_control, + v_partition_interval, + v_partition_type, + v_epoch, + v_premake, + v_default_table, + v_automatic_maintenance, + v_constraint_cols, + v_template_table, + v_jobmon, + v_date_trunc_interval + ); - v_update_part_config_definition := format( + v_update_part_config_definition := format( E'UPDATE @extschema@.part_config SET -\toptimize_trigger = %s, \toptimize_constraint = %s, \tretention = %L, \tretention_schema = %L, -\tretention_keep_table = %L, \tretention_keep_index = %L, +\tretention_keep_table = %L, \tinfinite_time_partitions = %L, \tdatetime_string = %L, \tsub_partition_set_full = %L, -\ttrigger_exception_handling = %L, \tinherit_privileges = %L, \tconstraint_valid = %L, -\tsubscription_refresh = %L, \tignore_default_data = %L WHERE parent_table = %L;', - v_optimize_trigger, - v_optimize_constraint, - v_retention, - v_retention_schema, - v_retention_keep_table, - v_retention_keep_index, - v_infinite_time_partitions, - v_datetime_string, - v_sub_partition_set_full, - v_trigger_exception_handling, - v_inherit_privileges, - v_constraint_valid, - v_subscription_refresh, - v_ignore_default_data, - v_parent_table - ); + v_optimize_constraint, + v_retention, + v_retention_schema, + v_retention_keep_index, + v_retention_keep_table, + v_infinite_time_partitions, + v_datetime_string, + v_sub_partition_set_full, + v_inherit_privileges, + v_constraint_valid, + v_ignore_default_data, + v_parent_table + ); - RETURN concat_ws(E'\n', - v_create_parent_definition, - v_update_part_config_definition - ); + RETURN concat_ws(E'\n', + v_create_parent_definition, + v_update_part_config_definition + ); END $$; - diff --git a/sql/functions/inherit_template_properties.sql b/sql/functions/inherit_template_properties.sql index a0183abf..5918553d 100644 --- a/sql/functions/inherit_template_properties.sql +++ b/sql/functions/inherit_template_properties.sql @@ -1,5 +1,10 @@ -CREATE FUNCTION @extschema@.inherit_template_properties (p_parent_table text, p_child_schema text, p_child_tablename text) RETURNS boolean - LANGUAGE plpgsql +CREATE FUNCTION @extschema@.inherit_template_properties( + p_parent_table text + , p_child_schema text + , p_child_tablename text +) + RETURNS boolean + LANGUAGE plpgsql AS $$ DECLARE @@ -8,9 +13,7 @@ v_child_schema text; v_child_tablename text; v_child_unlogged char; v_dupe_found boolean := false; -v_fk_list record; v_index_list record; -v_inherit_fk boolean; v_parent_index_list record; v_parent_oid oid; v_parent_table text; @@ -20,18 +23,16 @@ v_template_oid oid; v_template_schemaname text; v_template_table text; v_template_tablename name; -v_template_tablespace name; v_template_unlogged char; BEGIN /* * Function to inherit the properties of the template table to newly created child tables. - * Currently used for PostgreSQL 10 to inherit indexes and FKs since that is not natively available - * For PG11, used to inherit non-partition-key unique indexes & primary keys + * For PG14+, used to inherit non-partition-key unique indexes & primary keys and unlogged status */ -SELECT parent_table, template_table, inherit_fk -INTO v_parent_table, v_template_table, v_inherit_fk +SELECT parent_table, template_table +INTO v_parent_table, v_template_table FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_parent_table IS NULL THEN @@ -48,7 +49,7 @@ AND c.relname = split_part(p_parent_table, '.', 2)::name; IF v_parent_oid IS NULL THEN RAISE EXCEPTION 'Unable to find given parent table in system catalogs: %', p_parent_table; END IF; - + SELECT n.nspname, c.relname, c.relkind INTO v_child_schema, v_child_tablename, v_child_relkind FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid @@ -57,7 +58,7 @@ AND c.relname = p_child_tablename::name; IF v_child_tablename IS NULL THEN RAISE EXCEPTION 'Unable to find given child table in system catalogs: %.%', v_child_schema, v_child_tablename; END IF; - + IF v_child_relkind = 'p' THEN -- Subpartitioned parent, do not apply properties RAISE DEBUG 'inherit_template_properties: found given child is subpartition parent, so properties not inherited'; @@ -67,7 +68,7 @@ END IF; v_template_schemaname := split_part(v_template_table, '.', 1)::name; v_template_tablename := split_part(v_template_table, '.', 2)::name; -SELECT c.oid, ts.spcname INTO v_template_oid, v_template_tablespace +SELECT c.oid INTO v_template_oid FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid LEFT OUTER JOIN pg_catalog.pg_tablespace ts ON c.reltablespace = ts.oid @@ -77,121 +78,93 @@ AND c.relname = v_template_tablename; RAISE EXCEPTION 'Unable to find configured template table in system catalogs: %', v_template_table; END IF; --- Index creation (Required for all indexes in PG10. Only for non-unique, non-partition key indexes in PG11) -IF current_setting('server_version_num')::int >= 100000 THEN - FOR v_index_list IN +-- Index creation (Only for unique, non-partition key indexes) +FOR v_index_list IN + SELECT + array_to_string(regexp_matches(pg_get_indexdef(indexrelid), ' USING .*'),',') AS statement + , i.indisprimary + , i.indisunique + , ( SELECT array_agg( a.attname ORDER by x.r ) + FROM pg_catalog.pg_attribute a + JOIN ( SELECT k, row_number() over () as r + FROM unnest(i.indkey) k ) as x + ON a.attnum = x.k AND a.attrelid = i.indrelid + ) AS indkey_names + , c.relname AS index_name + , ts.spcname AS tablespace_name + FROM pg_catalog.pg_index i + JOIN pg_catalog.pg_class c ON i.indexrelid = c.oid + LEFT OUTER JOIN pg_catalog.pg_tablespace ts ON c.reltablespace = ts.oid + WHERE i.indrelid = v_template_oid + AND i.indisvalid + AND (i.indisprimary OR i.indisunique) + ORDER BY 1 +LOOP + v_dupe_found := false; + + FOR v_parent_index_list IN SELECT array_to_string(regexp_matches(pg_get_indexdef(indexrelid), ' USING .*'),',') AS statement , i.indisprimary - , i.indisunique , ( SELECT array_agg( a.attname ORDER by x.r ) FROM pg_catalog.pg_attribute a JOIN ( SELECT k, row_number() over () as r FROM unnest(i.indkey) k ) as x ON a.attnum = x.k AND a.attrelid = i.indrelid ) AS indkey_names - , c.relname AS index_name - , ts.spcname AS tablespace_name FROM pg_catalog.pg_index i - JOIN pg_catalog.pg_class c ON i.indexrelid = c.oid - LEFT OUTER JOIN pg_catalog.pg_tablespace ts ON c.reltablespace = ts.oid - WHERE i.indrelid = v_template_oid + WHERE i.indrelid = v_parent_oid AND i.indisvalid ORDER BY 1 LOOP - v_dupe_found := false; - - IF current_setting('server_version_num')::int >= 110000 THEN - FOR v_parent_index_list IN - SELECT - array_to_string(regexp_matches(pg_get_indexdef(indexrelid), ' USING .*'),',') AS statement - , i.indisprimary - , ( SELECT array_agg( a.attname ORDER by x.r ) - FROM pg_catalog.pg_attribute a - JOIN ( SELECT k, row_number() over () as r - FROM unnest(i.indkey) k ) as x - ON a.attnum = x.k AND a.attrelid = i.indrelid - ) AS indkey_names - FROM pg_catalog.pg_index i - WHERE i.indrelid = v_parent_oid - AND i.indisvalid - ORDER BY 1 - LOOP - - IF v_parent_index_list.indisprimary AND v_index_list.indisprimary THEN - IF v_parent_index_list.indkey_names = v_index_list.indkey_names THEN - RAISE DEBUG 'inherit_template_properties: Ignoring duplicate primary key on template table: % ', v_index_list.indkey_names; - v_dupe_found := true; - CONTINUE; -- only continue within this nested loop - END IF; - END IF; - - IF v_parent_index_list.statement = v_index_list.statement THEN - RAISE DEBUG 'inherit_template_properties: Ignoring duplicate index on template table: %', v_index_list.statement; - v_dupe_found := true; - CONTINUE; -- only continue within this nested loop - END IF; - - END LOOP; -- end parent index loop - END IF; -- End PG11 check - - IF v_dupe_found = true THEN - -- Only used in PG11 and should skip trying to create indexes that already existed on the parent - CONTINUE; - END IF; - IF v_index_list.indisprimary THEN - v_sql := format('ALTER TABLE %I.%I ADD PRIMARY KEY (%s)' - , v_child_schema - , v_child_tablename - , '"' || array_to_string(v_index_list.indkey_names, '","') || '"'); - IF v_index_list.tablespace_name IS NOT NULL THEN - v_sql := v_sql || format(' USING INDEX TABLESPACE %I', v_index_list.tablespace_name); - END IF; - RAISE DEBUG 'inherit_template_properties: Create pk: %', v_sql; - EXECUTE v_sql; - ELSE - -- statement column should be just the portion of the index definition that defines what it actually is - v_sql := format('CREATE %s INDEX ON %I.%I %s', CASE WHEN v_index_list.indisunique = TRUE THEN 'UNIQUE' ELSE '' END, v_child_schema, v_child_tablename, v_index_list.statement); - IF v_index_list.tablespace_name IS NOT NULL THEN - v_sql := v_sql || format(' TABLESPACE %I', v_index_list.tablespace_name); + IF v_parent_index_list.indisprimary AND v_index_list.indisprimary THEN + IF v_parent_index_list.indkey_names = v_index_list.indkey_names THEN + RAISE DEBUG 'inherit_template_properties: Ignoring duplicate primary key on template table: % ', v_index_list.indkey_names; + v_dupe_found := true; + CONTINUE; -- only continue within this nested loop END IF; + END IF; - RAISE DEBUG 'inherit_template_properties: Create index: %', v_sql; - EXECUTE v_sql; - + IF v_parent_index_list.statement = v_index_list.statement THEN + RAISE DEBUG 'inherit_template_properties: Ignoring duplicate unique index on template table: %', v_index_list.statement; + v_dupe_found := true; + CONTINUE; -- only continue within this nested loop END IF; - END LOOP; -END IF; --- End index creation + END LOOP; -- end parent index loop --- Foreign key creation (PG10 only) -IF current_setting('server_version_num')::int >= 100000 AND current_setting('server_version_num')::int < 110000 THEN - IF v_inherit_fk THEN - FOR v_fk_list IN - SELECT pg_get_constraintdef(con.oid) AS constraint_def - FROM pg_catalog.pg_constraint con - JOIN pg_catalog.pg_class c ON con.conrelid = c.oid - WHERE c.oid = v_template_oid - AND contype = 'f' - LOOP - v_sql := format('ALTER TABLE %I.%I ADD %s', v_child_schema, v_child_tablename, v_fk_list.constraint_def); - RAISE DEBUG 'inherit_template_properties: Create FK: %', v_sql; - EXECUTE v_sql; - END LOOP; + IF v_dupe_found = true THEN + CONTINUE; END IF; -END IF; --- End foreign key creation --- Tablespace inheritance on PG11 and earlier -IF current_setting('server_version_num')::int < 120000 AND v_template_tablespace IS NOT NULL THEN - v_sql := format('ALTER TABLE %I.%I SET TABLESPACE %I', v_child_schema, v_child_tablename, v_template_tablespace); - RAISE DEBUG 'inherit_template_properties: Alter tablespace: %', v_sql; - EXECUTE v_sql; -END IF; + IF v_index_list.indisprimary THEN + v_sql := format('ALTER TABLE %I.%I ADD PRIMARY KEY (%s)' + , v_child_schema + , v_child_tablename + , '"' || array_to_string(v_index_list.indkey_names, '","') || '"'); + IF v_index_list.tablespace_name IS NOT NULL THEN + v_sql := v_sql || format(' USING INDEX TABLESPACE %I', v_index_list.tablespace_name); + END IF; + RAISE DEBUG 'inherit_template_properties: Create pk: %', v_sql; + EXECUTE v_sql; + ELSIF v_index_list.indisunique THEN + -- statement column should be just the portion of the index definition that defines what it actually is + v_sql := format('CREATE UNIQUE INDEX ON %I.%I %s', v_child_schema, v_child_tablename, v_index_list.statement); + IF v_index_list.tablespace_name IS NOT NULL THEN + v_sql := v_sql || format(' TABLESPACE %I', v_index_list.tablespace_name); + END IF; --- UNLOGGED status. Currently waiting on final stance of how native will handle this property being changed for its children. + RAISE DEBUG 'inherit_template_properties: Create index: %', v_sql; + EXECUTE v_sql; + ELSE + RAISE EXCEPTION 'inherit_template_properties: Unexpected code path in unique index creation. Please report the steps that lead to this error to extension maintainers.'; + END IF; + +END LOOP; +-- End index creation + +-- UNLOGGED status. Currently waiting on final stance of how upstream will handle this property being changed for its children. -- See release notes for v4.2.0 SELECT relpersistence INTO v_template_unlogged FROM pg_catalog.pg_class c @@ -208,14 +181,14 @@ AND c.relname = v_child_tablename::name; IF v_template_unlogged = 'u' AND v_child_unlogged = 'p' THEN v_sql := format ('ALTER TABLE %I.%I SET UNLOGGED', v_child_schema, v_child_tablename); RAISE DEBUG 'inherit_template_properties: Alter UNLOGGED: %', v_sql; - EXECUTE v_sql; + EXECUTE v_sql; ELSIF v_template_unlogged = 'p' AND v_child_unlogged = 'u' THEN v_sql := format ('ALTER TABLE %I.%I SET LOGGED', v_child_schema, v_child_tablename); RAISE DEBUG 'inherit_template_properties: Alter UNLOGGED: %', v_sql; - EXECUTE v_sql; + EXECUTE v_sql; END IF; --- Relation options are not being inherited for PG <= 13 +-- Relation options are not either not being inherited or not supported (autovac tuning) on <= PG15 FOR v_relopt IN SELECT unnest(reloptions) as value FROM pg_catalog.pg_class @@ -232,4 +205,3 @@ RETURN true; END $$; - diff --git a/sql/functions/partition_data_id.sql b/sql/functions/partition_data_id.sql index da8c7e74..3a9a67aa 100644 --- a/sql/functions/partition_data_id.sql +++ b/sql/functions/partition_data_id.sql @@ -1,17 +1,19 @@ -CREATE FUNCTION @extschema@.partition_data_id(p_parent_table text +CREATE FUNCTION @extschema@.partition_data_id( + p_parent_table text , p_batch_count int DEFAULT 1 , p_batch_interval bigint DEFAULT NULL , p_lock_wait numeric DEFAULT 0 , p_order text DEFAULT 'ASC' , p_analyze boolean DEFAULT true , p_source_table text DEFAULT NULL - , p_ignored_columns text[] DEFAULT NULL) -RETURNS bigint -LANGUAGE plpgsql -AS $$ + , p_ignored_columns text[] DEFAULT NULL +) + RETURNS bigint + LANGUAGE plpgsql + AS $$ DECLARE -v_col text; +v_analyze boolean := FALSE; v_column_list text; v_control text; v_control_type text; @@ -24,10 +26,10 @@ v_lock_iter int := 1; v_lock_obtained boolean := FALSE; v_max_partition_id bigint; v_min_partition_id bigint; +v_parent_schema text; v_parent_tablename text; v_partition_interval bigint; v_partition_id bigint[]; -v_partition_type text; v_rowcount bigint; v_source_schemaname text; v_source_tablename text; @@ -37,18 +39,16 @@ v_total_rows bigint := 0; BEGIN /* - * Populate the child table(s) of an id-based partition set with old data from the original parent + * Populate the child table(s) of an id-based partition set with data from the default or other given source */ SELECT partition_interval::bigint - , partition_type , control , epoch INTO v_partition_interval - , v_partition_type , v_control , v_epoch - FROM @extschema@.part_config + FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: No entry in part_config found for given table: %', p_parent_table; @@ -59,8 +59,9 @@ FROM pg_catalog.pg_tables WHERE schemaname = split_part(p_parent_table, '.', 1)::name AND tablename = split_part(p_parent_table, '.', 2)::name; --- Preserve real parent tablename for use below -v_parent_tablename := v_source_tablename; +-- Preserve given parent tablename for use below +v_parent_schema := v_source_schemaname; +v_parent_tablename := v_source_tablename; SELECT general_type INTO v_control_type FROM @extschema@.check_control_type(v_source_schemaname, v_source_tablename, v_control); @@ -81,25 +82,24 @@ IF p_source_table IS NOT NULL THEN IF v_source_tablename IS NULL THEN RAISE EXCEPTION 'Given source table does not exist in system catalogs: %', p_source_table; END IF; -ELSIF v_partition_type = 'native' AND current_setting('server_version_num')::int >= 110000 THEN + +ELSE IF p_batch_interval IS NOT NULL AND p_batch_interval != v_partition_interval THEN -- This is true because all data for a given child table must be moved out of the default partition before the child table can be created. -- So cannot create the child table when only some of the data has been moved out of the default partition. - RAISE EXCEPTION 'Custom intervals are not allowed when moving data out of the DEFAULT partition in a native set. Please leave p_interval/p_batch_interval parameters unset or NULL to allow use of partition set''s default partitioning interval.'; + RAISE EXCEPTION 'Custom intervals are not allowed when moving data out of the DEFAULT partition. Please leave p_interval/p_batch_interval parameters unset or NULL to allow use of partition set''s default partitioning interval.'; END IF; - -- Set source table to default table if PG11+, p_source_table is not set, and it exists - -- Otherwise just return with a DEBUG that no data source exists - v_sql := format('SELECT n.nspname::text, c.relname::text FROM - pg_catalog.pg_inherits h - JOIN pg_catalog.pg_class c ON c.oid = h.inhrelid - JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid - WHERE h.inhparent = ''%I.%I''::regclass - AND pg_get_expr(relpartbound, c.oid) = ''DEFAULT''' - , v_source_schemaname - , v_source_tablename); - EXECUTE v_sql INTO v_default_schemaname, v_default_tablename; + -- Set source table to default table if p_source_table is not set, and it exists + -- Otherwise just return with a DEBUG that no data source exists + SELECT n.nspname::text, c.relname::text + INTO v_default_schemaname, v_default_tablename + FROM pg_catalog.pg_inherits h + JOIN pg_catalog.pg_class c ON c.oid = h.inhrelid + JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid + WHERE h.inhparent = format('%I.%I', v_source_schemaname, v_source_tablename)::regclass + AND pg_get_expr(relpartbound, c.oid) = 'DEFAULT'; IF v_default_tablename IS NOT NULL THEN v_source_schemaname := v_default_schemaname; @@ -119,23 +119,16 @@ IF p_batch_interval IS NULL OR p_batch_interval > v_partition_interval THEN END IF; -- Generate column list to use in SELECT/INSERT statements below. Allows for exclusion of GENERATED (or any other desired) columns. -v_sql := format ('SELECT ''"''||string_agg(attname, ''","'')||''"'' FROM pg_catalog.pg_attribute a - JOIN pg_catalog.pg_class c ON a.attrelid = c.oid - JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid - WHERE n.nspname = %L - AND c.relname = %L - AND a.attnum > 0 - AND a.attisdropped = false' - , v_source_schemaname - , v_source_tablename); - -IF p_ignored_columns IS NOT NULL THEN - FOREACH v_col IN ARRAY p_ignored_columns LOOP - v_sql := v_sql || format(' AND attname != %L ', v_col); - END LOOP; -END IF; - -EXECUTE v_sql INTO v_column_list; +SELECT string_agg(quote_ident(attname), ',') +INTO v_column_list +FROM pg_catalog.pg_attribute a +JOIN pg_catalog.pg_class c ON a.attrelid = c.oid +JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid +WHERE n.nspname = v_source_schemaname +AND c.relname = v_source_tablename +AND a.attnum > 0 +AND a.attisdropped = false +AND attname <> ALL(COALESCE(p_ignored_columns, ARRAY[]::text[])); FOR i IN 1..p_batch_count LOOP @@ -202,7 +195,7 @@ v_current_partition_name := @extschema@.check_name_length(COALESCE(v_parent_tabl IF v_default_exists THEN - -- Child tables cannot be created in native partitioning if data that belongs to it exists in the default + -- Child tables cannot be created if data that belongs to it exists in the default -- Have to move data out to temporary location, create child table, then move it back -- Temp table created above to avoid excessive temp creation in loop @@ -216,7 +209,8 @@ IF v_default_exists THEN , v_max_partition_id , v_column_list); - PERFORM @extschema@.create_partition_id(p_parent_table, v_partition_id, p_analyze); + -- Set analyze to true if a table is created + v_analyze := @extschema@.create_partition_id(p_parent_table, v_partition_id); EXECUTE format('WITH partition_data AS ( DELETE FROM partman_temp_data_storage RETURNING *) @@ -227,7 +221,8 @@ IF v_default_exists THEN ELSE - PERFORM @extschema@.create_partition_id(p_parent_table, v_partition_id, p_analyze); + -- Set analyze to true if a table is created + v_analyze := @extschema@.create_partition_id(p_parent_table, v_partition_id); EXECUTE format('WITH partition_data AS ( DELETE FROM ONLY %1$I.%2$I WHERE %3$I >= %4$s AND %3$I < %5$s RETURNING *) @@ -250,12 +245,15 @@ END IF; END LOOP; -IF v_partition_type = 'partman' THEN - PERFORM @extschema@.create_function_id(p_parent_table); +-- v_analyze is a local check if a new table is made. +-- p_analyze is a parameter to say whether to run the analyze at all. Used by create_parent() to avoid long exclusive lock or run_maintenence() to avoid long creation runs. +IF v_analyze AND p_analyze THEN + RAISE DEBUG 'partiton_data_time: Begin analyze of %.%', v_parent_schema, v_parent_tablename; + EXECUTE format('ANALYZE %I.%I', v_parent_schema, v_parent_tablename); + RAISE DEBUG 'partiton_data_time: End analyze of %.%', v_parent_schema, v_parent_tablename; END IF; RETURN v_total_rows; END $$; - diff --git a/sql/functions/partition_data_time.sql b/sql/functions/partition_data_time.sql index 07536cc1..b50a6a04 100644 --- a/sql/functions/partition_data_time.sql +++ b/sql/functions/partition_data_time.sql @@ -1,18 +1,19 @@ CREATE FUNCTION @extschema@.partition_data_time( - p_parent_table text - , p_batch_count int DEFAULT 1 - , p_batch_interval interval DEFAULT NULL - , p_lock_wait numeric DEFAULT 0 - , p_order text DEFAULT 'ASC' - , p_analyze boolean DEFAULT true - , p_source_table text DEFAULT NULL - , p_ignored_columns text[] DEFAULT NULL) + p_parent_table text + , p_batch_count int DEFAULT 1 + , p_batch_interval interval DEFAULT NULL + , p_lock_wait numeric DEFAULT 0 + , p_order text DEFAULT 'ASC' + , p_analyze boolean DEFAULT true + , p_source_table text DEFAULT NULL + , p_ignored_columns text[] DEFAULT NULL +) RETURNS bigint - LANGUAGE plpgsql + LANGUAGE plpgsql AS $$ DECLARE -v_col text; +v_analyze boolean := FALSE; v_column_list text; v_control text; v_control_type text; @@ -27,17 +28,15 @@ v_lock_iter int := 1; v_lock_obtained boolean := FALSE; v_max_partition_timestamp timestamptz; v_min_partition_timestamp timestamptz; +v_parent_schema text; v_parent_tablename text; -v_parent_tablename_real text; v_partition_expression text; v_partition_interval interval; v_partition_suffix text; v_partition_timestamp timestamptz[]; -v_partition_type text; v_source_schemaname text; v_source_tablename text; v_rowcount bigint; -v_sql text; v_start_control timestamptz; v_total_rows bigint := 0; @@ -46,17 +45,15 @@ BEGIN * Populate the child table(s) of a time-based partition set with old data from the original parent */ -SELECT partition_type - , partition_interval::interval +SELECT partition_interval::interval , control , datetime_string , epoch -INTO v_partition_type - , v_partition_interval +INTO v_partition_interval , v_control , v_datetime_string , v_epoch -FROM @extschema@.part_config +FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: No entry in part_config found for given table: %', p_parent_table; @@ -68,11 +65,12 @@ WHERE schemaname = split_part(p_parent_table, '.', 1)::name AND tablename = split_part(p_parent_table, '.', 2)::name; -- Preserve real parent tablename for use below +v_parent_schema := v_source_schemaname; v_parent_tablename := v_source_tablename; SELECT general_type INTO v_control_type FROM @extschema@.check_control_type(v_source_schemaname, v_source_tablename, v_control); -IF v_control_type <> 'time' THEN +IF v_control_type <> 'time' THEN IF (v_control_type = 'id' AND v_epoch = 'none') OR v_control_type <> 'id' THEN RAISE EXCEPTION 'Cannot run on partition set without time based control column or epoch flag set with an id column. Found control: %, epoch: %', v_control_type, v_epoch; END IF; @@ -94,25 +92,24 @@ IF p_source_table IS NOT NULL THEN END IF; -ELSIF v_partition_type = 'native' AND current_setting('server_version_num')::int >= 110000 THEN +ELSE IF p_batch_interval IS NOT NULL AND p_batch_interval != v_partition_interval THEN -- This is true because all data for a given child table must be moved out of the default partition before the child table can be created. -- So cannot create the child table when only some of the data has been moved out of the default partition. - RAISE EXCEPTION 'Custom intervals are not allowed when moving data out of the DEFAULT partition in a native set. Please leave p_interval/p_batch_interval parameters unset or NULL to allow use of partition set''s default partitioning interval.'; + RAISE EXCEPTION 'Custom intervals are not allowed when moving data out of the DEFAULT partition. Please leave p_interval/p_batch_interval parameters unset or NULL to allow use of partition set''s default partitioning interval.'; END IF; - -- Set source table to default table if PG11+, p_source_table is not set, and it exists + + -- Set source table to default table if p_source_table is not set, and it exists -- Otherwise just return with a DEBUG that no data source exists - v_sql := format('SELECT n.nspname::text, c.relname::text FROM - pg_catalog.pg_inherits h - JOIN pg_catalog.pg_class c ON c.oid = h.inhrelid - JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid - WHERE h.inhparent = ''%I.%I''::regclass - AND pg_get_expr(relpartbound, c.oid) = ''DEFAULT''' - , v_source_schemaname - , v_source_tablename); - - EXECUTE v_sql INTO v_default_schemaname, v_default_tablename; + SELECT n.nspname::text, c.relname::text + INTO v_default_schemaname, v_default_tablename + FROM pg_catalog.pg_inherits h + JOIN pg_catalog.pg_class c ON c.oid = h.inhrelid + JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid + WHERE h.inhparent = format('%I.%I', v_source_schemaname, v_source_tablename)::regclass + AND pg_get_expr(relpartbound, c.oid) = 'DEFAULT'; + IF v_default_tablename IS NOT NULL THEN v_source_schemaname := v_default_schemaname; v_source_tablename := v_default_tablename; @@ -120,7 +117,7 @@ ELSIF v_partition_type = 'native' AND current_setting('server_version_num')::int v_default_exists := true; EXECUTE format ('CREATE TEMP TABLE IF NOT EXISTS partman_temp_data_storage (LIKE %I.%I INCLUDING INDEXES) ON COMMIT DROP', v_source_schemaname, v_source_tablename); ELSE - RAISE DEBUG 'No default table found when partition_data_id() was called'; + RAISE DEBUG 'No default table found when partition_data_time() was called'; RETURN v_total_rows; END IF; END IF; @@ -139,23 +136,16 @@ v_partition_expression := CASE END; -- Generate column list to use in SELECT/INSERT statements below. Allows for exclusion of GENERATED (or any other desired) columns. -v_sql := format ('SELECT ''"''||string_agg(attname, ''","'')||''"'' FROM pg_catalog.pg_attribute a - JOIN pg_catalog.pg_class c ON a.attrelid = c.oid - JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid - WHERE n.nspname = %L - AND c.relname = %L - AND a.attnum > 0 - AND a.attisdropped = false' - , v_source_schemaname - , v_source_tablename); - -IF p_ignored_columns IS NOT NULL THEN - FOREACH v_col IN ARRAY p_ignored_columns LOOP - v_sql := v_sql || format(' AND attname != %L ', v_col); - END LOOP; -END IF; - -EXECUTE v_sql INTO v_column_list; +SELECT string_agg(quote_ident(attname), ',') +INTO v_column_list +FROM pg_catalog.pg_attribute a +JOIN pg_catalog.pg_class c ON a.attrelid = c.oid +JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid +WHERE n.nspname = v_source_schemaname +AND c.relname = v_source_tablename +AND a.attnum > 0 +AND a.attisdropped = false +AND attname <> ALL(COALESCE(p_ignored_columns, ARRAY[]::text[])); FOR i IN 1..p_batch_count LOOP @@ -171,55 +161,31 @@ FOR i IN 1..p_batch_count LOOP EXIT; END IF; - IF v_partition_type = 'partman' THEN - CASE - WHEN v_partition_interval = '15 mins' THEN - v_min_partition_timestamp := date_trunc('hour', v_start_control) + - '15min'::interval * floor(date_part('minute', v_start_control) / 15.0); - WHEN v_partition_interval = '30 mins' THEN - v_min_partition_timestamp := date_trunc('hour', v_start_control) + - '30min'::interval * floor(date_part('minute', v_start_control) / 30.0); - WHEN v_partition_interval = '1 hour' THEN - v_min_partition_timestamp := date_trunc('hour', v_start_control); - WHEN v_partition_interval = '1 day' THEN - v_min_partition_timestamp := date_trunc('day', v_start_control); - WHEN v_partition_interval = '1 week' THEN - v_min_partition_timestamp := date_trunc('week', v_start_control); - WHEN v_partition_interval = '1 month' THEN - v_min_partition_timestamp := date_trunc('month', v_start_control); - WHEN v_partition_interval = '3 months' THEN - v_min_partition_timestamp := date_trunc('quarter', v_start_control); - WHEN v_partition_interval = '1 year' THEN - v_min_partition_timestamp := date_trunc('year', v_start_control); - END CASE; - ELSIF v_partition_type IN ('time-custom', 'native') THEN - SELECT child_start_time INTO v_min_partition_timestamp FROM @extschema@.show_partition_info(v_source_schemaname||'.'||v_last_partition - , v_partition_interval::text - , p_parent_table); - v_max_partition_timestamp := v_min_partition_timestamp + v_partition_interval; - LOOP - IF v_start_control >= v_min_partition_timestamp AND v_start_control < v_max_partition_timestamp THEN - EXIT; - ELSE - BEGIN - IF v_start_control >= v_max_partition_timestamp THEN - -- Keep going forward in time, checking if child partition time interval encompasses the current v_start_control value - v_min_partition_timestamp := v_max_partition_timestamp; - v_max_partition_timestamp := v_max_partition_timestamp + v_partition_interval; - - ELSE - -- Keep going backwards in time, checking if child partition time interval encompasses the current v_start_control value - v_max_partition_timestamp := v_min_partition_timestamp; - v_min_partition_timestamp := v_min_partition_timestamp - v_partition_interval; - END IF; - EXCEPTION WHEN datetime_field_overflow THEN - RAISE EXCEPTION 'Attempted partition time interval is outside PostgreSQL''s supported time range. - Unable to create partition with interval before timestamp % ', v_min_partition_timestamp; - END; - END IF; - END LOOP; - - END IF; + SELECT child_start_time INTO v_min_partition_timestamp FROM @extschema@.show_partition_info(v_source_schemaname||'.'||v_last_partition + , v_partition_interval::text + , p_parent_table); + v_max_partition_timestamp := v_min_partition_timestamp + v_partition_interval; + LOOP + IF v_start_control >= v_min_partition_timestamp AND v_start_control < v_max_partition_timestamp THEN + EXIT; + ELSE + BEGIN + IF v_start_control >= v_max_partition_timestamp THEN + -- Keep going forward in time, checking if child partition time interval encompasses the current v_start_control value + v_min_partition_timestamp := v_max_partition_timestamp; + v_max_partition_timestamp := v_max_partition_timestamp + v_partition_interval; + + ELSE + -- Keep going backwards in time, checking if child partition time interval encompasses the current v_start_control value + v_max_partition_timestamp := v_min_partition_timestamp; + v_min_partition_timestamp := v_min_partition_timestamp - v_partition_interval; + END IF; + EXCEPTION WHEN datetime_field_overflow THEN + RAISE EXCEPTION 'Attempted partition time interval is outside PostgreSQL''s supported time range. + Unable to create partition with interval before timestamp % ', v_min_partition_timestamp; + END; + END IF; + END LOOP; v_partition_timestamp := ARRAY[v_min_partition_timestamp]; IF p_order = 'ASC' THEN @@ -271,7 +237,7 @@ FOR i IN 1..p_batch_count LOOP v_current_partition_name := @extschema@.check_name_length(v_parent_tablename, v_partition_suffix, TRUE); IF v_default_exists THEN - -- Child tables cannot be created in native partitioning if data that belongs to it exists in the default + -- Child tables cannot be created if data that belongs to it exists in the default -- Have to move data out to temporary location, create child table, then move it back -- Temp table created above to avoid excessive temp creation in loop @@ -285,7 +251,8 @@ FOR i IN 1..p_batch_count LOOP , v_max_partition_timestamp , v_column_list); - PERFORM @extschema@.create_partition_time(p_parent_table, v_partition_timestamp, p_analyze); + -- Set analyze to true if a table is created + v_analyze := @extschema@.create_partition_time(p_parent_table, v_partition_timestamp); EXECUTE format('WITH partition_data AS ( DELETE FROM partman_temp_data_storage RETURNING *) @@ -296,7 +263,8 @@ FOR i IN 1..p_batch_count LOOP ELSE - PERFORM @extschema@.create_partition_time(p_parent_table, v_partition_timestamp, p_analyze); + -- Set analyze to true if a table is created + v_analyze := @extschema@.create_partition_time(p_parent_table, v_partition_timestamp); EXECUTE format('WITH partition_data AS ( DELETE FROM ONLY %I.%I WHERE %s >= %L AND %3$s < %5$L RETURNING *) @@ -316,14 +284,17 @@ FOR i IN 1..p_batch_count LOOP EXIT; END IF; -END LOOP; +END LOOP; -IF v_partition_type IN ('partman', 'time-custom') THEN - PERFORM @extschema@.create_function_time(p_parent_table); +-- v_analyze is a local check if a new table is made. +-- p_analyze is a parameter to say whether to run the analyze at all. Used by create_parent() to avoid long exclusive lock or run_maintenence() to avoid long creation runs. +IF v_analyze AND p_analyze THEN + RAISE DEBUG 'partiton_data_time: Begin analyze of %.%', v_parent_schema, v_parent_tablename; + EXECUTE format('ANALYZE %I.%I', v_parent_schema, v_parent_tablename); + RAISE DEBUG 'partiton_data_time: End analyze of %.%', v_parent_schema, v_parent_tablename; END IF; RETURN v_total_rows; END $$; - diff --git a/sql/functions/run_maintenance.sql b/sql/functions/run_maintenance.sql index 3b2f4430..c725b713 100644 --- a/sql/functions/run_maintenance.sql +++ b/sql/functions/run_maintenance.sql @@ -1,5 +1,11 @@ -CREATE FUNCTION @extschema@.run_maintenance(p_parent_table text DEFAULT NULL, p_analyze boolean DEFAULT NULL, p_jobmon boolean DEFAULT true) RETURNS void - LANGUAGE plpgsql +CREATE FUNCTION @extschema@.run_maintenance( + p_parent_table text DEFAULT NULL + -- If these defaults change reflect them in `run_maintenance_proc`! + , p_analyze boolean DEFAULT false + , p_jobmon boolean DEFAULT true +) + RETURNS void + LANGUAGE plpgsql AS $$ DECLARE @@ -8,24 +14,22 @@ ex_detail text; ex_hint text; ex_message text; v_adv_lock boolean; -v_analyze boolean; +v_analyze boolean := FALSE; v_check_subpart int; v_control_type text; v_create_count int := 0; -v_current_partition text; v_current_partition_id bigint; v_current_partition_timestamp timestamptz; v_default_tablename text; v_drop_count int := 0; v_is_default text; v_job_id bigint; -v_jobmon boolean; v_jobmon_schema text; v_last_partition text; v_last_partition_created boolean; v_last_partition_id bigint; v_last_partition_timestamp timestamptz; -v_max_id_parent bigint; +v_max_id_default bigint; v_max_time_default timestamptz; v_new_search_path text; v_next_partition_id bigint; @@ -37,14 +41,9 @@ v_parent_schema text; v_parent_tablename text; v_partition_expression text; v_premade_count int; -v_premake_id_max bigint; -v_premake_id_min bigint; -v_premake_timestamp_min timestamptz; -v_premake_timestamp_max timestamptz; v_row record; v_row_max_id record; v_row_max_time record; -v_row_sub record; v_sql text; v_step_id bigint; v_step_overflow_id bigint; @@ -52,11 +51,9 @@ v_sub_id_max bigint; v_sub_id_max_suffix bigint; v_sub_id_min bigint; v_sub_parent text; -v_sub_refresh_done text[]; v_sub_timestamp_max timestamptz; v_sub_timestamp_max_suffix timestamptz; v_sub_timestamp_min timestamptz; -v_tablename text; v_tables_list_sql text; BEGIN @@ -65,8 +62,7 @@ BEGIN * Also manages dropping old partitions if the retention option is set. * If p_parent_table is passed, will only run run_maintenance() on that one table (no matter what the configuration table may have set for it) * Otherwise, will run on all tables in the config table with p_automatic_maintenance() set to true. - * For large partition sets, running analyze can cause maintenance to take longer than expected. Can set p_analyze to false to avoid a forced analyze run on PG versions before 11. 11+ does not analyze by default anymore. - * Be aware that constraint exclusion may not work properly until an analyze on the partition set is run. + * For large partition sets, running analyze can cause maintenance to take longer than expected so is not done by default. Can set p_analyze to true to force analyze. Be aware that constraint exclusion may not work properly until an analyze on the partition set is run. */ v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman run_maintenance')); @@ -104,13 +100,13 @@ v_tables_list_sql := 'SELECT parent_table , epoch , infinite_time_partitions , retention - , subscription_refresh , ignore_default_data + , datetime_string FROM @extschema@.part_config WHERE undo_in_progress = false'; IF p_parent_table IS NULL THEN - v_tables_list_sql := v_tables_list_sql || ' AND automatic_maintenance = ''on'''; + v_tables_list_sql := v_tables_list_sql || format(' AND automatic_maintenance = %L', 'on'); ELSE v_tables_list_sql := v_tables_list_sql || format(' AND parent_table = %L', p_parent_table); END IF; @@ -120,37 +116,34 @@ LOOP CONTINUE WHEN v_row.undo_in_progress; - -- When sub-partitioning, retention may drop tables that were already put into the query loop values. + -- When sub-partitioning, retention may drop tables that were already put into the query loop values. -- Check if they still exist in part_config before continuing v_parent_exists := NULL; SELECT parent_table INTO v_parent_exists FROM @extschema@.part_config WHERE parent_table = v_row.parent_table; RAISE DEBUG 'Parent table possibly removed from part_config by retenion'; CONTINUE WHEN v_parent_exists IS NULL; - - -- Check for consistent data in part_config_sub table. Was unable to get this working properly as either a constraint or trigger. + + -- Check for old quarterly and ISO weekly partitioning from prior to version 5.x. Error out to avoid breaking these partition sets + -- with new datetime_string formats + IF v_row.datetime_string IN ('YYYY"q"Q', 'IYYY"w"IW') THEN + RAISE EXCEPTION 'Quarterly and ISO weekly partitioning is no longer supported in pg_partman 5.0.0 and greater. Please see documentation for migrating away from these partitioning patterns. Partition set: %', v_row.parent_table; + END IF; + + -- Check for consistent data in part_config_sub table. Was unable to get this working properly as either a constraint or trigger. -- Would either delay raising an error until the next write (which I cannot predict) or disallow future edits to update a sub-partition set's configuration. -- This way at least provides a consistent way to check that I know will run. If anyone can get a working constraint/trigger, please help! SELECT sub_parent INTO v_sub_parent FROM @extschema@.part_config_sub WHERE sub_parent = v_row.parent_table; IF v_sub_parent IS NOT NULL THEN SELECT count(*) INTO v_check_subpart FROM @extschema@.check_subpart_sameconfig(v_row.parent_table); IF v_check_subpart > 1 THEN - RAISE EXCEPTION 'Inconsistent data in part_config_sub table. Sub-partition tables that are themselves sub-partitions cannot have differing configuration values among their siblings. - Run this query: "SELECT * FROM @extschema@.check_subpart_sameconfig(''%'');" This should only return a single row or nothing. - If multiple rows are returned, the results are differing configurations in the part_config_sub table for children of the given parent. - Determine the child tables of the given parent and look up their entries based on the "part_config_sub.sub_parent" column. + RAISE EXCEPTION 'Inconsistent data in part_config_sub table. Sub-partition tables that are themselves sub-partitions cannot have differing configuration values among their siblings. + Run this query: "SELECT * FROM @extschema@.check_subpart_sameconfig(''%'');" This should only return a single row or nothing. + If multiple rows are returned, the results are differing configurations in the part_config_sub table for children of the given parent. + Determine the child tables of the given parent and look up their entries based on the "part_config_sub.sub_parent" column. Update the differing values to be consistent for your desired values.', v_row.parent_table; END IF; END IF; - -- Shouldn't need to analyze tables for most statistics for native sets on PG11+ by default anymore - IF p_analyze IS NULL THEN - IF v_row.partition_type = 'native' AND current_setting('server_version_num')::int >= 110000 THEN - v_analyze := false; - ELSE - v_analyze := true; - END IF; - END IF; - SELECT n.nspname, c.relname, c.oid INTO v_parent_schema, v_parent_tablename, v_parent_oid FROM pg_catalog.pg_class c @@ -158,22 +151,18 @@ LOOP WHERE n.nspname = split_part(v_row.parent_table, '.', 1)::name AND c.relname = split_part(v_row.parent_table, '.', 2)::name; - -- Used below to see if there's any data in the parent (<=PG10) or default (PG11+) child table. - IF v_row.partition_type = 'native' AND current_setting('server_version_num')::int >= 110000 THEN - -- Always returns the default partition first if it exists - SELECT partition_tablename INTO v_default_tablename - FROM @extschema@.show_partitions(v_row.parent_table, p_include_default := true) LIMIT 1; + -- Always returns the default partition first if it exists + SELECT partition_tablename INTO v_default_tablename + FROM @extschema@.show_partitions(v_row.parent_table, p_include_default := true) LIMIT 1; - SELECT pg_get_expr(relpartbound, v_parent_oid) INTO v_is_default - FROM pg_catalog.pg_class c - JOIN pg_catalog.pg_namespace n on c.relnamespace = n.oid - WHERE n.nspname = v_parent_schema - AND c.relname = v_default_tablename; + SELECT pg_get_expr(relpartbound, v_parent_oid) INTO v_is_default + FROM pg_catalog.pg_class c + JOIN pg_catalog.pg_namespace n on c.relnamespace = n.oid + WHERE n.nspname = v_parent_schema + AND c.relname = v_default_tablename; - IF v_is_default != 'DEFAULT' THEN - v_default_tablename := v_parent_tablename; - END IF; - ELSE + IF v_is_default != 'DEFAULT' THEN + -- Parent table will never have data, but allows code below to "just work" v_default_tablename := v_parent_tablename; END IF; @@ -194,49 +183,47 @@ LOOP -- Run retention if needed IF v_row.retention IS NOT NULL THEN - v_drop_count := v_drop_count + @extschema@.drop_partition_time(v_row.parent_table); - IF v_drop_count > 0 AND v_row.partition_type <> 'native' THEN - PERFORM @extschema@.create_function_time(v_row.parent_table, v_job_id); - END IF; + v_drop_count := v_drop_count + @extschema@.drop_partition_time(v_row.parent_table); END IF; IF v_row.sub_partition_set_full THEN CONTINUE; END IF; - SELECT child_start_time INTO v_last_partition_timestamp + SELECT child_start_time INTO v_last_partition_timestamp FROM @extschema@.show_partition_info(v_parent_schema||'.'||v_last_partition, v_row.partition_interval, v_row.parent_table); + -- Loop through child tables starting from highest to get current max value in partition set - -- Avoids doing a scan on entire partition set and/or getting any values accidentally in parent. - - IF v_row.infinite_time_partitions IS TRUE THEN - -- Set it to "now" so new partitions continue to be created - -- For infinite_time_partitions, don't bother getting the max value in the partitions - v_current_partition_timestamp = CURRENT_TIMESTAMP; - ELSE - FOR v_row_max_time IN - SELECT partition_schemaname, partition_tablename FROM @extschema@.show_partitions(v_row.parent_table, 'DESC') - LOOP - EXECUTE format('SELECT max(%s)::text FROM %I.%I' - , v_partition_expression - , v_row_max_time.partition_schemaname - , v_row_max_time.partition_tablename - ) INTO v_current_partition_timestamp; - - IF v_current_partition_timestamp IS NOT NULL THEN - SELECT suffix_timestamp INTO v_current_partition_timestamp FROM @extschema@.show_partition_name(v_row.parent_table, v_current_partition_timestamp::text); - EXIT; - END IF; - END LOOP; - END IF; -- end infinite time check + -- Avoids doing a scan on entire partition set and/or getting any values accidentally in default. + FOR v_row_max_time IN + SELECT partition_schemaname, partition_tablename FROM @extschema@.show_partitions(v_row.parent_table, 'DESC', false) + LOOP + EXECUTE format('SELECT max(%s)::text FROM %I.%I' + , v_partition_expression + , v_row_max_time.partition_schemaname + , v_row_max_time.partition_tablename + ) INTO v_current_partition_timestamp; + + IF v_row.infinite_time_partitions AND (v_current_partition_timestamp < CURRENT_TIMESTAMP) THEN + -- No new data has been inserted relative to "now", but keep making child tables anyway + v_current_partition_timestamp = CURRENT_TIMESTAMP; + -- Nothing else to do in this case so just end early + EXIT; + END IF; + IF v_current_partition_timestamp IS NOT NULL THEN + SELECT suffix_timestamp INTO v_current_partition_timestamp FROM @extschema@.show_partition_name(v_row.parent_table, v_current_partition_timestamp::text); + EXIT; + END IF; + END LOOP; - -- Check for values in the parent/default table. If they are there and greater than all child values, use that instead - -- This option will likely be reverted in 5.x. Data should not remain in the default and maintenance failing because it is should be the default occurance. For now, adding an option to allow users to ignore this and avoid giant gaps in child tables when future data is inserted into the default (Github Issue #462). - IF v_row.ignore_default_data THEN + -- If not ignoring the default table, check for max values there. If they are there and greater than all child values, use that instead + -- Note the default is NOT to care about data in the default, so maintenance will fail if new child table boundaries overlap with + -- data that exists in the default. This is intentional so user removes data from default to avoid larger problems. + IF v_row.ignore_default_data THEN v_max_time_default := NULL; ELSE EXECUTE format('SELECT max(%s) FROM ONLY %I.%I', v_partition_expression, v_parent_schema, v_default_tablename) INTO v_max_time_default; - END IF; + END IF; RAISE DEBUG 'run_maint: v_current_partition_timestamp: %, v_max_time_default: %', v_current_partition_timestamp, v_max_time_default; - IF v_current_partition_timestamp IS NULL AND v_max_time_default IS NULL THEN + IF v_current_partition_timestamp IS NULL AND v_max_time_default IS NULL THEN -- Partition set is completely empty and infinite time partitions not set -- Nothing to do CONTINUE; @@ -260,7 +247,7 @@ LOOP v_premade_count = round(EXTRACT('epoch' FROM age(v_last_partition_timestamp, v_current_partition_timestamp)) / EXTRACT('epoch' FROM v_row.partition_interval::interval)); v_next_partition_timestamp := v_last_partition_timestamp; RAISE DEBUG 'run_maint before loop: current_partition_timestamp: %, v_premade_count: %, v_sub_timestamp_min: %, v_sub_timestamp_max: %' - , v_current_partition_timestamp + , v_current_partition_timestamp , v_premade_count , v_sub_timestamp_min , v_sub_timestamp_max; @@ -274,7 +261,7 @@ LOOP BEGIN v_next_partition_timestamp := v_next_partition_timestamp + v_row.partition_interval::interval; EXCEPTION WHEN datetime_field_overflow THEN - v_premade_count := v_row.premake; -- do this so it can exit the premake check loop and continue in the outer for loop + v_premade_count := v_row.premake; -- do this so it can exit the premake check loop and continue in the outer for loop IF v_jobmon_schema IS NOT NULL THEN v_step_overflow_id := add_step(v_job_id, 'Attempted partition time interval is outside PostgreSQL''s supported time range.'); PERFORM update_step(v_step_overflow_id, 'CRITICAL', format('Child partition creation skipped for parent table: %s', v_partition_time)); @@ -284,13 +271,10 @@ LOOP END; v_last_partition_created := @extschema@.create_partition_time(v_row.parent_table - , ARRAY[v_next_partition_timestamp] - , v_analyze); + , ARRAY[v_next_partition_timestamp]); IF v_last_partition_created THEN + v_analyze := true; v_create_count := v_create_count + 1; - IF v_row.partition_type <> 'native' THEN - PERFORM @extschema@.create_function_time(v_row.parent_table, v_job_id); - END IF; END IF; v_premade_count = round(EXTRACT('epoch' FROM age(v_next_partition_timestamp, v_current_partition_timestamp)) / EXTRACT('epoch' FROM v_row.partition_interval::interval)); @@ -300,20 +284,16 @@ LOOP -- Run retention if needed IF v_row.retention IS NOT NULL THEN - v_drop_count := v_drop_count + @extschema@.drop_partition_id(v_row.parent_table); - IF v_drop_count > 0 AND v_row.partition_type <> 'native' THEN - PERFORM @extschema@.create_function_id(v_row.parent_table, v_job_id); - END IF; + v_drop_count := v_drop_count + @extschema@.drop_partition_id(v_row.parent_table); END IF; IF v_row.sub_partition_set_full THEN CONTINUE; END IF; - -- Loop through child tables starting from highest to get current max value in partition set - -- Avoids doing a scan on entire partition set and/or getting any values accidentally in parent. - FOR v_row_max_id IN - SELECT partition_schemaname, partition_tablename FROM @extschema@.show_partitions(v_row.parent_table, 'DESC') + SELECT partition_schemaname, partition_tablename FROM @extschema@.show_partitions(v_row.parent_table, 'DESC', false) LOOP + -- Loop through child tables starting from highest to get current max value in partition set + -- Avoids doing a scan on entire partition set and/or getting any values accidentally in default. EXECUTE format('SELECT max(%I)::text FROM %I.%I' , v_row.control , v_row_max_id.partition_schemaname @@ -323,16 +303,21 @@ LOOP EXIT; END IF; END LOOP; - -- Check for values in the parent/default table. If they are there and greater than all child values, use that instead - -- This allows maintenance to continue working properly if there is a large gap in data insertion. Data will remain in parent, but new tables will be created - EXECUTE format('SELECT max(%I) FROM ONLY %I.%I', v_row.control, v_parent_schema, v_default_tablename) INTO v_max_id_parent; - IF v_max_id_parent > v_current_partition_id THEN - SELECT suffix_id INTO v_current_partition_id FROM @extschema@.show_partition_name(v_row.parent_table, v_max_id_parent::text); + -- If not ignoring the default table, check for max values there. If they are there and greater than all child values, use that instead + -- Note the default is NOT to care about data in the default, so maintenance will fail if new child table boundaries overlap with + -- data that exists in the default. This is intentional so user removes data from default to avoid larger problems. + IF v_row.ignore_default_data THEN + v_max_id_default := NULL; + ELSE + EXECUTE format('SELECT max(%I) FROM ONLY %I.%I', v_row.control, v_parent_schema, v_default_tablename) INTO v_max_id_default; END IF; - IF v_current_partition_id IS NULL THEN + IF v_current_partition_id IS NULL AND v_max_id_default IS NULL THEN -- Partition set is completely empty. Nothing to do CONTINUE; END IF; + IF v_current_partition_id IS NULL OR (v_max_id_default > v_current_partition_id) THEN + SELECT suffix_id INTO v_current_partition_id FROM @extschema@.show_partition_name(v_row.parent_table, v_max_id_default::text); + END IF; SELECT child_start_id INTO v_last_partition_id FROM @extschema@.show_partition_info(v_parent_schema||'.'||v_last_partition, v_row.partition_interval, v_row.parent_table); @@ -350,42 +335,39 @@ LOOP v_next_partition_id := v_last_partition_id; v_premade_count := ((v_last_partition_id - v_current_partition_id) / v_row.partition_interval::bigint); -- Loop premaking until config setting is met. Allows it to catch up if it fell behind or if premake changed. - WHILE (v_premade_count < v_row.premake) LOOP + WHILE (v_premade_count < v_row.premake) LOOP RAISE DEBUG 'run_maint: parent_table: %, v_premade_count: %, v_next_partition_id: %', v_row.parent_table, v_premade_count, v_next_partition_id; IF v_next_partition_id < v_sub_id_min OR v_next_partition_id > v_sub_id_max THEN -- With subpartitioning, no need to run if the id is not in the parent table's range EXIT; END IF; v_next_partition_id := v_next_partition_id + v_row.partition_interval::bigint; - v_last_partition_created := @extschema@.create_partition_id(v_row.parent_table, ARRAY[v_next_partition_id], v_analyze); + v_last_partition_created := @extschema@.create_partition_id(v_row.parent_table, ARRAY[v_next_partition_id]); IF v_last_partition_created THEN + v_analyze := true; v_create_count := v_create_count + 1; - IF v_row.partition_type <> 'native' THEN - PERFORM @extschema@.create_function_id(v_row.parent_table, v_job_id); - END IF; END IF; v_premade_count := ((v_next_partition_id - v_current_partition_id) / v_row.partition_interval::bigint); END LOOP; END IF; -- end main IF check for time or id - -- Refresh subscriptions in order to catch new tables that may have been created in the publication - -- Keep track of which ones have been refreshed so it doesn't needlessly run more than once - -- in a single maintenance run - IF v_row.subscription_refresh IS NOT NULL THEN - IF v_sub_refresh_done @> ARRAY[v_row.subscription_refresh] THEN - CONTINUE; - ELSE - v_sql = format('ALTER SUBSCRIPTION %I REFRESH PUBLICATION', v_row.subscription_refresh); - RAISE DEBUG '%', v_sql; - EXECUTE v_sql; - PERFORM array_append(v_sub_refresh_done, v_row.subscription_refresh); + IF v_analyze AND p_analyze THEN + IF v_jobmon_schema IS NOT NULL THEN + v_step_id := add_step(v_job_id, format('Analyzing partition set: %s', v_row.parent_table)); + END IF; + + EXECUTE format('ANALYZE %I.%I',v_parent_schema, v_parent_tablename); + + IF v_jobmon_schema IS NOT NULL THEN + PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; END LOOP; -- end of main loop through part_config IF v_jobmon_schema IS NOT NULL THEN + v_step_id := add_step(v_job_id, format('Finished maintenance')); PERFORM update_step(v_step_id, 'OK', format('Partition maintenance finished. %s partitions made. %s partitions dropped.', v_create_count, v_drop_count)); IF v_step_overflow_id IS NOT NULL THEN PERFORM fail_job(v_job_id); @@ -418,4 +400,3 @@ DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; - diff --git a/sql/functions/show_partition_info.sql b/sql/functions/show_partition_info.sql index 44afa837..ed512f29 100644 --- a/sql/functions/show_partition_info.sql +++ b/sql/functions/show_partition_info.sql @@ -1,36 +1,31 @@ -CREATE FUNCTION @extschema@.show_partition_info(p_child_table text +CREATE FUNCTION @extschema@.show_partition_info( + p_child_table text , p_partition_interval text DEFAULT NULL , p_parent_table text DEFAULT NULL , OUT child_start_time timestamptz , OUT child_end_time timestamptz , OUT child_start_id bigint - , OUT child_end_id bigint - , OUT suffix text) -RETURNS record + , OUT child_end_id bigint + , OUT suffix text +) + RETURNS record LANGUAGE plpgsql STABLE AS $$ DECLARE v_child_schema text; v_child_tablename text; -v_start_time_string text; v_control text; v_control_type text; -v_datetime_string text; v_epoch text; v_parent_table text; v_partition_interval text; -v_partition_type text; -v_quarter text; -v_start_time_epoch double precision; -v_suffix text; -v_suffix_position int; -v_year text; +v_start_string text; BEGIN /* - * Show the data boundries for a given child table as well as the suffix that will be used. - * Passing the parent table argument improves performance by avoiding a catalog lookup. + * Show the data boundaries for a given child table as well as the suffix that will be used. + * Passing the parent table argument slightly improves performance by avoiding a catalog lookup. * Passing an interval lets you set one different than the default configured one if desired. */ @@ -41,7 +36,11 @@ WHERE n.nspname = split_part(p_child_table, '.', 1)::name AND c.relname = split_part(p_child_table, '.', 2)::name; IF v_child_tablename IS NULL THEN - RAISE EXCEPTION 'Child table given does not exist (%)', p_child_table; + IF p_parent_table IS NOT NULL THEN + RAISE EXCEPTION 'Child table given does not exist (%) for given parent table (%)', p_child_table, p_parent_table; + ELSE + RAISE EXCEPTION 'Child table given does not exist (%)', p_child_table; + END IF; END IF; IF p_parent_table IS NULL THEN @@ -55,13 +54,13 @@ ELSE END IF; IF p_partition_interval IS NULL THEN - SELECT control, partition_interval, partition_type, datetime_string, epoch - INTO v_control, v_partition_interval, v_partition_type, v_datetime_string, v_epoch + SELECT control, partition_interval, epoch + INTO v_control, v_partition_interval, v_epoch FROM @extschema@.part_config WHERE parent_table = v_parent_table; ELSE v_partition_interval := p_partition_interval; - SELECT control, partition_type, datetime_string, epoch - INTO v_control, v_partition_type, v_datetime_string, v_epoch + SELECT control, epoch + INTO v_control, v_epoch FROM @extschema@.part_config WHERE parent_table = v_parent_table; END IF; @@ -71,79 +70,55 @@ END IF; SELECT general_type INTO v_control_type FROM @extschema@.check_control_type(v_child_schema, v_child_tablename, v_control); -v_suffix_position := (length(v_child_tablename) - position('p_' in reverse(v_child_tablename))) + 2; -v_suffix := substring(v_child_tablename from v_suffix_position); +RAISE DEBUG 'show_partition_info: v_child_schema: %, v_child_tablename: %', + v_child_schema, v_child_tablename; -RAISE DEBUG 'show_partition_info: v_child_schema: %, v_child_tablename: %, v_suffix: %', - v_child_schema, v_child_tablename, v_suffix; +-- Look at actual partition bounds in catalog and pull values from there. +SELECT (regexp_match(pg_get_expr(c.relpartbound, c.oid, true) + , $REGEX$\(([^)]+)\) TO \(([^)]+)\)$REGEX$))[1]::text +INTO v_start_string +FROM pg_catalog.pg_class c +JOIN pg_catalog.pg_namespace n +ON c.relnamespace = n.oid +WHERE c.relname = v_child_tablename +AND n.nspname = v_child_schema; IF v_control_type = 'time' OR (v_control_type = 'id' AND v_epoch <> 'none') THEN - IF v_partition_type = 'native' THEN - -- Look at actual partition bounds in catalog and pull values from there. - -- For native partitioning, handles any possible interval type much better than old methods in remaining conditions of this IF block - SELECT (regexp_match(pg_get_expr(c.relpartbound, c.oid, true) - , $REGEX$\(([^)]+)\) TO \(([^)]+)\)$REGEX$))[1]::text - INTO v_start_time_string - FROM pg_catalog.pg_class c - JOIN pg_catalog.pg_namespace n - ON c.relnamespace = n.oid - WHERE c.relname = v_child_tablename - AND n.nspname = v_child_schema; - - IF v_control_type = 'time' THEN - child_start_time := v_start_time_string::timestamptz; - ELSIF (v_control_type = 'id' AND v_epoch <> 'none') THEN - -- bigint data type is stored as a single-quoted string in the partition expression. Must strip quotes for valid type-cast. - v_start_time_string := trim(BOTH '''' FROM v_start_time_string); - IF v_epoch = 'seconds' THEN - child_start_time := to_timestamp(v_start_time_string::double precision); - ELSIF v_epoch = 'milliseconds' THEN - child_start_time := to_timestamp((v_start_time_string::double precision) / 1000); - ELSIF v_epoch = 'nanoseconds' THEN - child_start_time := to_timestamp((v_start_time_string::double precision) / 1000000000); - END IF; - ELSE - RAISE EXCEPTION 'Unexpected code path in show_partition_info(). Please report this bug with the configuration that lead to it.'; - END IF; - ELSIF v_partition_interval::interval <> '3 months' OR (v_partition_interval::interval = '3 months' AND v_partition_type = 'time-custom') THEN - child_start_time := to_timestamp(v_suffix, v_datetime_string); + IF v_control_type = 'time' THEN + child_start_time := v_start_string::timestamptz; + ELSIF (v_control_type = 'id' AND v_epoch <> 'none') THEN + -- bigint data type is stored as a single-quoted string in the partition expression. Must strip quotes for valid type-cast. + v_start_string := trim(BOTH '''' FROM v_start_string); + IF v_epoch = 'seconds' THEN + child_start_time := to_timestamp(v_start_string::double precision); + ELSIF v_epoch = 'milliseconds' THEN + child_start_time := to_timestamp((v_start_string::double precision) / 1000); + ELSIF v_epoch = 'nanoseconds' THEN + child_start_time := to_timestamp((v_start_string::double precision) / 1000000000); + END IF; ELSE - -- to_timestamp doesn't recognize 'Q' date string formater. Handle it - v_year := split_part(v_suffix, 'q', 1); - v_quarter := split_part(v_suffix, 'q', 2); - CASE - WHEN v_quarter = '1' THEN - child_start_time := to_timestamp(v_year || '-01-01', 'YYYY-MM-DD'); - WHEN v_quarter = '2' THEN - child_start_time := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); - WHEN v_quarter = '3' THEN - child_start_time := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); - WHEN v_quarter = '4' THEN - child_start_time := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); - ELSE - -- handle case when partition name did not use "q" convetion - child_start_time := to_timestamp(v_suffix, v_datetime_string); - END CASE; + RAISE EXCEPTION 'Unexpected code path in show_partition_info(). Please report this bug with the configuration that lead to it.'; END IF; child_end_time := (child_start_time + v_partition_interval::interval); + SELECT to_char(base_timestamp, datetime_string) + INTO suffix + FROM @extschema@.calculate_time_partition_info(v_partition_interval::interval, child_start_time); + ELSIF v_control_type = 'id' THEN - -- Note for future updates, if trigger based partitioning is dropped, do catalog lookup for integer boundaries similar to time. - child_start_id := v_suffix::bigint; + child_start_id := trim(BOTH '''' FROM v_start_string)::bigint; child_end_id := (child_start_id + v_partition_interval::bigint) - 1; ELSE RAISE EXCEPTION 'Invalid partition type encountered in show_partition_info()'; END IF; -suffix = v_suffix; RETURN; END $$; - diff --git a/sql/functions/show_partition_name.sql b/sql/functions/show_partition_name.sql index c704e6ae..facf91ce 100644 --- a/sql/functions/show_partition_name.sql +++ b/sql/functions/show_partition_name.sql @@ -1,4 +1,12 @@ -CREATE FUNCTION @extschema@.show_partition_name(p_parent_table text, p_value text, OUT partition_schema text, OUT partition_table text, OUT suffix_timestamp timestamptz, OUT suffix_id bigint, OUT table_exists boolean) RETURNS record +CREATE FUNCTION @extschema@.show_partition_name( + p_parent_table text, p_value text + , OUT partition_schema text + , OUT partition_table text + , OUT suffix_timestamp timestamptz + , OUT suffix_id bigint + , OUT table_exists boolean +) + RETURNS record LANGUAGE plpgsql STABLE AS $$ DECLARE @@ -13,8 +21,6 @@ v_control_type text; v_datetime_string text; v_epoch text; v_given_timestamp timestamptz; -v_max_range timestamptz; -v_min_range timestamptz; v_parent_schema text; v_parent_tablename text; v_partition_interval text; @@ -28,7 +34,7 @@ BEGIN * Also returns just the suffix value and true if the child table exists or false if it does not */ -SELECT partition_type +SELECT partition_type , control , partition_interval , datetime_string @@ -36,9 +42,9 @@ SELECT partition_type INTO v_type , v_control , v_partition_interval - , v_datetime_string + , v_datetime_string , v_epoch -FROM @extschema@.part_config +FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_type IS NULL THEN @@ -61,11 +67,11 @@ SELECT general_type INTO v_control_type FROM @extschema@.check_control_type(v_pa IF ( (v_control_type = 'time') OR (v_control_type = 'id' AND v_epoch <> 'none') ) THEN v_given_timestamp := p_value::timestamptz; - FOR v_row IN + FOR v_row IN SELECT partition_schemaname ||'.'|| partition_tablename AS child_table FROM @extschema@.show_partitions(p_parent_table, 'DESC') LOOP - SELECT child_start_time INTO v_child_start_time - FROM @extschema@.show_partition_info(v_row.child_table, v_partition_interval, p_parent_table); + SELECT child_start_time INTO v_child_start_time + FROM @extschema@.show_partition_info(v_row.child_table, v_partition_interval, p_parent_table); -- Don't use child_end_time from above function to avoid edge cases around user supplied timestamps v_child_end_time := v_child_start_time + v_partition_interval::interval; IF v_given_timestamp >= v_child_end_time THEN @@ -113,45 +119,12 @@ IF ( (v_control_type = 'time') OR (v_control_type = 'id' AND v_epoch <> 'none') partition_table := @extschema@.check_name_length(v_parent_tablename, to_char(suffix_timestamp, v_datetime_string), TRUE); -ELSIF v_control_type = 'id' AND v_type <> 'time-custom' THEN +ELSIF v_control_type = 'id' THEN suffix_id := (p_value::bigint - (p_value::bigint % v_partition_interval::bigint)); partition_table := @extschema@.check_name_length(v_parent_tablename, suffix_id::text, TRUE); -ELSIF v_type = 'time-custom' THEN - - SELECT child_table, lower(partition_range) INTO partition_table, suffix_timestamp FROM @extschema@.custom_time_partitions - WHERE parent_table = p_parent_table AND partition_range @> p_value::timestamptz; - - IF partition_table IS NULL THEN - SELECT max(upper(partition_range)) INTO v_max_range FROM @extschema@.custom_time_partitions WHERE parent_table = p_parent_table; - SELECT min(lower(partition_range)) INTO v_min_range FROM @extschema@.custom_time_partitions WHERE parent_table = p_parent_table; - IF p_value::timestamptz >= v_max_range THEN - suffix_timestamp := v_max_range; - LOOP - -- Keep incrementing higher until given value is below the upper range - suffix_timestamp := suffix_timestamp + v_partition_interval::interval; - IF p_value::timestamptz < suffix_timestamp THEN - -- Have to subtract one interval because the value would actually be in the partition previous - -- to this partition timestamp since the partition names contain the lower boundary - suffix_timestamp := suffix_timestamp - v_partition_interval::interval; - EXIT; - END IF; - END LOOP; - ELSIF p_value::timestamptz < v_min_range THEN - suffix_timestamp := v_min_range; - LOOP - -- Keep decrementing lower until given value is below or equal to the lower range - suffix_timestamp := suffix_timestamp - v_partition_interval::interval; - IF p_value::timestamptz >= suffix_timestamp THEN - EXIT; - END IF; - END LOOP; - ELSE - RAISE EXCEPTION 'Unable to determine a valid child table for the given parent table and value'; - END IF; - - partition_table := @extschema@.check_name_length(v_parent_tablename, to_char(suffix_timestamp, v_datetime_string), TRUE); - END IF; +ELSE + RAISE EXCEPTION 'Unexpected code path encountered in show_partition_name(). No valid control type found. Please report this issue to author with relevant partition config info.'; END IF; SELECT tablename INTO v_child_exists @@ -169,5 +142,3 @@ RETURN; END $$; - - diff --git a/sql/functions/show_partitions.sql b/sql/functions/show_partitions.sql index 3674ccba..09d10170 100644 --- a/sql/functions/show_partitions.sql +++ b/sql/functions/show_partitions.sql @@ -1,4 +1,9 @@ -CREATE FUNCTION @extschema@.show_partitions (p_parent_table text, p_order text DEFAULT 'ASC', p_include_default boolean DEFAULT false) RETURNS TABLE (partition_schemaname text, partition_tablename text) +CREATE FUNCTION @extschema@.show_partitions ( + p_parent_table text + , p_order text DEFAULT 'ASC' + , p_include_default boolean DEFAULT false +) + RETURNS TABLE (partition_schemaname text, partition_tablename text) LANGUAGE plpgsql STABLE SET search_path = @extschema@,pg_temp AS $$ @@ -9,9 +14,9 @@ v_control_type text; v_datetime_string text; v_default_sql text; v_epoch text; +v_epoch_divisor bigint; v_parent_schema text; v_parent_tablename text; -v_partition_interval text; v_partition_type text; v_sql text; @@ -23,17 +28,14 @@ BEGIN */ IF upper(p_order) NOT IN ('ASC', 'DESC') THEN - EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', v_old_search_path, 'false'); - RAISE EXCEPTION 'p_order paramter must be one of the following values: ASC, DESC'; + RAISE EXCEPTION 'p_order parameter must be one of the following values: ASC, DESC'; END IF; SELECT partition_type - , partition_interval , datetime_string , control , epoch INTO v_partition_type - , v_partition_interval , v_datetime_string , v_control , v_epoch @@ -58,55 +60,61 @@ SELECT general_type INTO v_control_type FROM @extschema@.check_control_type(v_pa RAISE DEBUG 'show_partitions: v_parent_schema: %, v_parent_tablename: %, v_datetime_string: %', v_parent_schema, v_parent_tablename, v_datetime_string; -v_sql := format('SELECT n.nspname::text AS partition_schemaname, c.relname::text AS partition_name FROM - pg_catalog.pg_inherits h +v_sql := format('SELECT n.nspname::text AS partition_schemaname + , c.relname::text AS partition_name + FROM pg_catalog.pg_inherits h JOIN pg_catalog.pg_class c ON c.oid = h.inhrelid JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid WHERE h.inhparent = ''%I.%I''::regclass' , v_parent_schema , v_parent_tablename); -IF v_partition_type = 'native' AND current_setting('server_version_num')::int >= 110000 THEN - - IF p_include_default THEN - -- Return the default partition immediately as first item in list - v_default_sql := v_sql || format(' - AND pg_get_expr(relpartbound, c.oid) = ''DEFAULT'''); - RAISE DEBUG 'show_partitions: v_default_sql: %', v_default_sql; - RETURN QUERY EXECUTE v_default_sql; - END IF; - - v_sql := v_sql || format(' - AND pg_get_expr(relpartbound, c.oid) != ''DEFAULT'''); +IF p_include_default THEN + -- Return the default partition immediately as first item in list + v_default_sql := v_sql || format(' + AND pg_get_expr(relpartbound, c.oid) = ''DEFAULT'''); + RAISE DEBUG 'show_partitions: v_default_sql: %', v_default_sql; + RETURN QUERY EXECUTE v_default_sql; END IF; -IF v_control_type = 'time' OR (v_control_type = 'id' AND v_epoch <> 'none') THEN +v_sql := v_sql || format(' + AND pg_get_expr(relpartbound, c.oid) != ''DEFAULT'' '); - IF v_partition_interval::interval <> '3 months' OR (v_partition_interval::interval = '3 months' AND v_partition_type = 'time-custom') THEN - v_sql := v_sql || format(' - ORDER BY to_timestamp(substring(c.relname from ((length(c.relname) - position(''p_'' in reverse(c.relname))) + 2) ), %L) %s' - , v_datetime_string - , p_order); - ELSE +IF v_control_type = 'time' THEN + + v_sql := v_sql || format(' + ORDER BY (regexp_match(pg_get_expr(c.relpartbound, c.oid, true), $REGEX$\(([^)]+)\) TO \(([^)]+)\)$REGEX$))[1]::text::timestamptz %s ' + , p_order); - -- For quarterly, to_timestamp() doesn't recognize "Q" in datetime string. - -- First order by just the year, then order by the quarter number (should be last character in table name) - v_sql := v_sql || format(' - ORDER BY to_timestamp(substring(c.relname from ((length(c.relname) - position(''p_'' in reverse(c.relname))) + 2) for 4), ''YYYY'') %s - , substring(reverse(c.relname) from 1 for 1) %s' - , p_order - , p_order); +ELSIF v_control_type = 'id' AND v_epoch <> 'none' THEN + IF v_epoch = 'seconds' THEN + v_epoch_divisor := 1; + ELSIF v_epoch = 'milliseconds' THEN + v_epoch_divisor := 1000; + ELSIF v_epoch = 'nanoseconds' THEN + v_epoch_divisor := 1000000000; END IF; + -- Have to do a trim here because of inconsistency in quoting different integer types. Ex: bigint boundary values are quoted but int values are not + v_sql := v_sql || format(' + ORDER BY to_timestamp(trim( BOTH $QUOTE$''$QUOTE$ from (regexp_match(pg_get_expr(c.relpartbound, c.oid, true), $REGEX$\(([^)]+)\) TO \(([^)]+)\)$REGEX$))[1]::text )::bigint /%s ) %s ' + , v_epoch_divisor + , p_order); + ELSIF v_control_type = 'id' THEN + -- Have to do a trim here because of inconsistency in quoting different integer types. Ex: bigint boundary values are quoted but int values are not v_sql := v_sql || format(' - ORDER BY substring(c.relname from ((length(c.relname) - position(''p_'' in reverse(c.relname))) + 2) )::bigint %s' + ORDER BY trim( BOTH $QUOTE$''$QUOTE$ from (regexp_match(pg_get_expr(c.relpartbound, c.oid, true), $REGEX$\(([^)]+)\) TO \(([^)]+)\)$REGEX$))[1]::text )::bigint %s ' , p_order); +ELSE + + RAISE EXCEPTION 'show_partitions: Unexpected code path in sort order determination. Please report the steps that lead to this error to extension maintainers.'; + END IF; RAISE DEBUG 'show_partitions: v_sql: %', v_sql; @@ -115,4 +123,3 @@ RETURN QUERY EXECUTE v_sql; END $$; - diff --git a/sql/functions/undo_partition.sql b/sql/functions/undo_partition.sql index 8d5da790..6aa0957d 100644 --- a/sql/functions/undo_partition.sql +++ b/sql/functions/undo_partition.sql @@ -1,16 +1,17 @@ CREATE FUNCTION @extschema@.undo_partition( p_parent_table text - , p_batch_count int DEFAULT 1 + , p_target_table text + , p_loop_count int DEFAULT 1 , p_batch_interval text DEFAULT NULL , p_keep_table boolean DEFAULT true , p_lock_wait numeric DEFAULT 0 - , p_target_table text DEFAULT NULL , p_ignored_columns text[] DEFAULT NULL , p_drop_cascade boolean DEFAULT false , OUT partitions_undone int - , OUT rows_undone bigint) + , OUT rows_undone bigint +) RETURNS record - LANGUAGE plpgsql + LANGUAGE plpgsql AS $$ DECLARE @@ -24,7 +25,6 @@ v_batch_interval_time interval; v_batch_loop_count int := 0; v_child_loop_total bigint := 0; v_child_table text; -v_col text; v_column_list text; v_control text; v_control_type text; @@ -44,8 +44,6 @@ v_parent_schema text; v_parent_tablename text; v_partition_expression text; v_partition_interval text; -v_partition_type text; -v_relkind char; v_row record; v_rowcount bigint; v_sql text; @@ -63,14 +61,13 @@ v_undo_count int := 0; BEGIN /* - * For native, moves data to new, target table since data cannot be moved to parent. - * Leaves old parent table as is and does not change name of new table. - * For trigger-based, moves data to parent + * Moves data to new, target table since data cannot be moved elsewhere in the same partition set. + * Leaves old parent table as is and does not change name of new table. */ -v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman undo_partition_native')); +v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman undo_partition')); IF v_adv_lock = 'false' THEN - RAISE NOTICE 'undo_partition_native already running.'; + RAISE NOTICE 'undo_partition already running.'; partitions_undone = -1; RETURN; END IF; @@ -80,30 +77,28 @@ IF p_parent_table = p_target_table THEN END IF; SELECT partition_interval::text - , partition_type , control , jobmon , epoch , template_table INTO v_partition_interval - , v_partition_type , v_control , v_jobmon , v_epoch , v_template_table -FROM @extschema@.part_config +FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_control IS NULL THEN RAISE EXCEPTION 'No configuration found for pg_partman for given parent table: %', p_parent_table; END IF; -IF v_partition_type = 'native' AND p_target_table IS NULL THEN - RAISE EXCEPTION 'Natively partitioned tables require setting the p_target_table option'; +IF p_target_table IS NULL THEN + RAISE EXCEPTION 'The p_target_table option must be set when undoing a partitioned table'; END IF; -SELECT n.nspname, c.relname, c.relkind -INTO v_parent_schema, v_parent_tablename, v_relkind +SELECT n.nspname, c.relname +INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid WHERE n.nspname = split_part(p_parent_table, '.', 1)::name @@ -127,7 +122,7 @@ ELSIF v_control_type = 'id' THEN v_batch_interval_id := p_batch_interval::bigint; END IF; ELSE - RAISE EXCEPTION 'Data type of control column in given partition set must be either data/time or integer.'; + RAISE EXCEPTION 'Data type of control column in given partition set must be either date/time or integer.'; END IF; SELECT current_setting('search_path') INTO v_old_search_path; @@ -146,7 +141,7 @@ EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', v_new_search_path -- Check if any child tables are themselves partitioned or part of an inheritance tree. Prevent undo at this level if so. -- Need to lock child tables at all levels before multi-level undo can be performed safely. -FOR v_row IN +FOR v_row IN SELECT partition_schemaname, partition_tablename FROM @extschema@.show_partitions(p_parent_table) LOOP SELECT count(*) INTO v_sub_count @@ -156,21 +151,16 @@ LOOP WHERE c.relname = v_row.partition_tablename::name AND n.nspname = v_row.partition_schemaname::name; IF v_sub_count > 0 THEN - RAISE EXCEPTION 'Child table for this parent has child table(s) itself (%). Run undo partitioning on this table or remove inheritance first to ensure all data is properly moved to parent', v_row.partition_schemaname||'.'||v_row.partition_tablename; + RAISE EXCEPTION 'Child table for this parent has child table(s) itself (%). Run undo partitioning on this table to ensure all data is properly moved to target table', v_row.partition_schemaname||'.'||v_row.partition_tablename; END IF; END LOOP; -IF p_target_table IS NOT NULL THEN - SELECT n.nspname, c.relname - INTO v_target_schema, v_target_tablename - FROM pg_catalog.pg_class c - JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid - WHERE n.nspname = split_part(p_target_table, '.', 1)::name - AND c.relname = split_part(p_target_table, '.', 2)::name; -ELSE - v_target_schema := v_parent_schema; - v_target_tablename := v_parent_tablename; -END IF; +SELECT n.nspname, c.relname +INTO v_target_schema, v_target_tablename +FROM pg_catalog.pg_class c +JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid +WHERE n.nspname = split_part(p_target_table, '.', 1)::name +AND c.relname = split_part(p_target_table, '.', 2)::name; IF v_target_tablename IS NULL THEN RAISE EXCEPTION 'Given target table not found in system catalogs: %', p_target_table; @@ -191,52 +181,6 @@ END; -- Stops new time partitions from being made as well as stopping child tables from being dropped if they were configured with a retention period. UPDATE @extschema@.part_config SET undo_in_progress = true WHERE parent_table = p_parent_table; -IF v_partition_type != 'native' THEN - -- Stop data going into child tables on non-native partition sets. - - v_trig_name := @extschema@.check_name_length(p_object_name := v_parent_tablename, p_suffix := '_part_trig'); - v_function_name := @extschema@.check_name_length(v_parent_tablename, '_part_trig_func', FALSE); - - -- Double-check for proper object existence - SELECT tgname INTO v_trig_name - FROM pg_catalog.pg_trigger t - JOIN pg_catalog.pg_class c ON t.tgrelid = c.oid - WHERE tgname = v_trig_name::name - AND c.relname = v_parent_tablename::name; - - SELECT proname INTO v_function_name FROM pg_catalog.pg_proc p JOIN pg_catalog.pg_namespace n ON p.pronamespace = n.oid WHERE n.nspname = v_parent_schema::name AND proname = v_function_name::name; - - IF v_trig_name IS NOT NULL THEN - -- lockwait for trigger drop - IF p_lock_wait > 0 THEN - v_lock_iter := 0; - WHILE v_lock_iter <= 5 LOOP - v_lock_iter := v_lock_iter + 1; - BEGIN - EXECUTE format('LOCK TABLE ONLY %I.%I IN ACCESS EXCLUSIVE MODE NOWAIT', v_parent_schema, v_parent_tablename); - v_lock_obtained := TRUE; - EXCEPTION - WHEN lock_not_available THEN - PERFORM pg_sleep( p_lock_wait / 5.0 ); - CONTINUE; - END; - EXIT WHEN v_lock_obtained; - END LOOP; - IF NOT v_lock_obtained THEN - RAISE NOTICE 'Unable to obtain lock on parent table to remove trigger'; - partitions_undone = -1; - RETURN; - END IF; - END IF; -- END p_lock_wait IF - EXECUTE format('DROP TRIGGER IF EXISTS %I ON %I.%I', v_trig_name, v_parent_schema, v_parent_tablename); - END IF; -- END trigger IF - v_lock_obtained := FALSE; -- reset for reuse later - - IF v_function_name IS NOT NULL THEN - EXECUTE format('DROP FUNCTION IF EXISTS %I.%I()', v_parent_schema, v_function_name); - END IF; - -END IF; -- end pg_partman trigger cleanup IF v_jobmon_schema IS NOT NULL THEN IF (v_trig_name IS NOT NULL OR v_function_name IS NOT NULL) THEN @@ -247,30 +191,23 @@ IF v_jobmon_schema IS NOT NULL THEN END IF; -- Generate column list to use in SELECT/INSERT statements below. Allows for exclusion of GENERATED (or any other desired) columns. -v_sql := format ('SELECT ''"''||string_agg(attname, ''","'')||''"'' FROM pg_catalog.pg_attribute a - JOIN pg_catalog.pg_class c ON a.attrelid = c.oid - JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid - WHERE n.nspname = %L - AND c.relname = %L - AND a.attnum > 0 - AND a.attisdropped = false' - , v_target_schema - , v_target_tablename); - -IF p_ignored_columns IS NOT NULL THEN - FOREACH v_col IN ARRAY p_ignored_columns LOOP - v_sql := v_sql || format(' AND attname != %L ', v_col); - END LOOP; -END IF; - -EXECUTE v_sql INTO v_column_list; +SELECT string_agg(quote_ident(attname), ',') +INTO v_column_list +FROM pg_catalog.pg_attribute a +JOIN pg_catalog.pg_class c ON a.attrelid = c.oid +JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid +WHERE n.nspname = v_target_schema +AND c.relname = v_target_tablename +AND a.attnum > 0 +AND a.attisdropped = false +AND attname <> ALL(COALESCE(p_ignored_columns, ARRAY[]::text[])); <> LOOP -- Get ordered list of child table in set. Store in variable one at a time per loop until none are left or batch count is reached. -- This easily allows it to loop over same child table until empty or move onto next child table after it's dropped - -- Include the native default table to ensure all data there is removed as well (final parameter = true) - SELECT partition_tablename INTO v_child_table FROM @extschema@.show_partitions(p_parent_table, 'ASC', TRUE) LIMIT 1; + -- Include the default table to ensure all data there is removed as well + SELECT partition_tablename INTO v_child_table FROM @extschema@.show_partitions(p_parent_table, 'ASC', p_include_default := TRUE) LIMIT 1; EXIT outer_child_loop WHEN v_child_table IS NULL; @@ -310,20 +247,12 @@ LOOP END IF; -- END p_lock_wait IF v_lock_obtained := FALSE; -- reset for reuse later - IF v_partition_type = 'native' THEN - v_sql := format('ALTER TABLE %I.%I DETACH PARTITION %I.%I' - , v_parent_schema - , v_parent_tablename - , v_parent_schema - , v_child_table); - EXECUTE v_sql; - ELSE - EXECUTE format('ALTER TABLE %I.%I NO INHERIT %I.%I' - , v_parent_schema - , v_child_table - , v_parent_schema - , v_parent_tablename); - END IF; + v_sql := format('ALTER TABLE %I.%I DETACH PARTITION %I.%I' + , v_parent_schema + , v_parent_tablename + , v_parent_schema + , v_child_table); + EXECUTE v_sql; IF p_keep_table = false THEN v_sql := 'DROP TABLE %I.%I'; @@ -340,12 +269,8 @@ LOOP END IF; END IF; - IF v_partition_type = 'time-custom' THEN - DELETE FROM @extschema@.custom_time_partitions WHERE parent_table = p_parent_table AND child_table = v_parent_schema||'.'||v_child_table; - END IF; - v_undo_count := v_undo_count + 1; - EXIT outer_child_loop WHEN v_batch_loop_count >= p_batch_count; -- Exit outer FOR loop if p_batch_count is reached + EXIT outer_child_loop WHEN v_batch_loop_count >= p_loop_count; -- Exit outer FOR loop if p_loop_count is reached CONTINUE outer_child_loop; -- skip data moving steps below END IF; v_inner_loop_count := 1; @@ -403,7 +328,7 @@ LOOP -- Check again if table is empty and go to outer loop again to drop it if so EXECUTE format('SELECT min(%s) FROM %I.%I', v_partition_expression, v_parent_schema, v_child_table) INTO v_child_min_time; CONTINUE outer_child_loop WHEN v_child_min_time IS NULL; - + ELSIF v_control_type = 'id' THEN IF p_lock_wait > 0 THEN @@ -458,7 +383,7 @@ LOOP END IF; -- end v_control_type check - EXIT outer_child_loop WHEN v_batch_loop_count >= p_batch_count; -- Exit outer FOR loop if p_batch_count is reached + EXIT outer_child_loop WHEN v_batch_loop_count >= p_loop_count; -- Exit outer FOR loop if p_loop_count is reached END LOOP inner_child_loop; END LOOP outer_child_loop; @@ -467,7 +392,7 @@ SELECT partition_tablename INTO v_child_table FROM @extschema@.show_partitions(p IF v_child_table IS NULL THEN DELETE FROM @extschema@.part_config WHERE parent_table = p_parent_table; - + -- Check if any other config entries still have this template table and don't remove if so -- Allows other sibling/parent tables to still keep using in case entire partition set isn't being undone SELECT count(*) INTO v_template_siblings FROM @extschema@.part_config WHERE template_table = v_template_table; @@ -526,4 +451,3 @@ DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; - diff --git a/sql/procedures/partition_data_proc.sql b/sql/procedures/partition_data_proc.sql index 8cc20d07..a1454b38 100644 --- a/sql/procedures/partition_data_proc.sql +++ b/sql/procedures/partition_data_proc.sql @@ -1,18 +1,28 @@ -CREATE PROCEDURE @extschema@.partition_data_proc (p_parent_table text, p_interval text DEFAULT NULL, p_batch int DEFAULT NULL, p_wait int DEFAULT 1, p_source_table text DEFAULT NULL, p_order text DEFAULT 'ASC', p_lock_wait int DEFAULT 0, p_lock_wait_tries int DEFAULT 10, p_quiet boolean DEFAULT false, p_ignored_columns text[] DEFAULT NULL) +CREATE PROCEDURE @extschema@.partition_data_proc ( + p_parent_table text + , p_loop_count int DEFAULT NULL + , p_interval text DEFAULT NULL + , p_lock_wait int DEFAULT 0 + , p_lock_wait_tries int DEFAULT 10 + , p_wait int DEFAULT 1 + , p_order text DEFAULT 'ASC' + , p_source_table text DEFAULT NULL + , p_ignored_columns text[] DEFAULT NULL + , p_quiet boolean DEFAULT false +) LANGUAGE plpgsql AS $$ DECLARE v_adv_lock boolean; -v_batch_count int := 0; v_control text; v_control_type text; v_epoch text; v_is_autovac_off boolean := false; v_lockwait_count int := 0; +v_loop_count int := 0; v_parent_schema text; v_parent_tablename text; -v_row record; v_rows_moved bigint; v_source_schema text; v_source_tablename text; @@ -29,7 +39,7 @@ END IF; SELECT control, epoch INTO v_control, v_epoch -FROM @extschema@.part_config +FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: No entry in part_config found for given table: %', p_parent_table; @@ -54,7 +64,7 @@ IF p_source_table IS NOT NULL THEN RAISE EXCEPTION 'Unable to find given source table in system catalogs. Ensure it is schema qualified: %', p_source_table; END IF; END IF; - + SELECT general_type INTO v_control_type FROM @extschema@.check_control_type(v_parent_schema, v_parent_tablename, v_control); IF v_control_type = 'id' AND v_epoch <> 'none' THEN @@ -71,7 +81,7 @@ IF p_autovacuum_on = false THEN -- Add this parameter back to definition END IF; */ -v_sql := format('SELECT %I.partition_data_%s (%L, p_lock_wait := %L, p_order := %L, p_analyze := false' +v_sql := format('SELECT %s.partition_data_%s (p_parent_table := %L, p_lock_wait := %L, p_order := %L, p_analyze := false' , '@extschema@', v_control_type, p_parent_table, p_lock_wait, p_order); IF p_interval IS NOT NULL THEN v_sql := v_sql || format(', p_batch_interval := %L', p_interval); @@ -89,7 +99,7 @@ LOOP EXECUTE v_sql INTO v_rows_moved; -- If lock wait timeout, do not increment the counter IF v_rows_moved != -1 THEN - v_batch_count := v_batch_count + 1; + v_loop_count := v_loop_count + 1; v_total := v_total + v_rows_moved; v_lockwait_count := 0; ELSE @@ -100,25 +110,25 @@ LOOP END IF; IF p_quiet = false THEN IF v_rows_moved > 0 THEN - RAISE NOTICE 'Batch: %, Rows moved: %', v_batch_count, v_rows_moved; + RAISE NOTICE 'Loop: %, Rows moved: %', v_loop_count, v_rows_moved; ELSIF v_rows_moved = -1 THEN RAISE NOTICE 'Unable to obtain row locks for data to be moved. Trying again...'; END IF; END IF; - -- If no rows left or given batch argument limit is reached - IF v_rows_moved = 0 OR (p_batch > 0 AND v_batch_count >= p_batch) THEN + -- If no rows left or given loop argument limit is reached + IF v_rows_moved = 0 OR (p_loop_count > 0 AND v_loop_count >= p_loop_count) THEN EXIT; END IF; COMMIT; PERFORM pg_sleep(p_wait); - RAISE DEBUG 'v_rows_moved: %, v_batch_count: %, v_total: %, v_lockwait_count: %, p_wait: %', p_wait, v_rows_moved, v_batch_count, v_total, v_lockwait_count; + RAISE DEBUG 'v_rows_moved: %, v_loop_count: %, v_total: %, v_lockwait_count: %, p_wait: %', p_wait, v_rows_moved, v_loop_count, v_total, v_lockwait_count; END LOOP; /* IF v_is_autovac_off = true THEN -- Reset autovac back to default if it was turned off by this procedure PERFORM @extschema@.autovacuum_reset(v_parent_schema, v_parent_tablename, v_source_schema, v_source_tablename); - COMMIT; + COMMIT; END IF; */ @@ -146,4 +156,3 @@ EXCEPTION */ END; $$; - diff --git a/sql/procedures/run_maintenance_proc.sql b/sql/procedures/run_maintenance_proc.sql index c80f7ceb..5be5f133 100644 --- a/sql/procedures/run_maintenance_proc.sql +++ b/sql/procedures/run_maintenance_proc.sql @@ -1,12 +1,16 @@ -CREATE PROCEDURE @extschema@.run_maintenance_proc(p_wait int DEFAULT 0, p_analyze boolean DEFAULT NULL, p_jobmon boolean DEFAULT true) +CREATE PROCEDURE @extschema@.run_maintenance_proc( + p_wait int DEFAULT 0 + -- Keep these defaults in sync with `run_maintenance`! + , p_analyze boolean DEFAULT false + , p_jobmon boolean DEFAULT true +) LANGUAGE plpgsql AS $$ DECLARE v_adv_lock boolean; -v_row record; +v_parent_table text; v_sql text; -v_tables_list_sql text; BEGIN @@ -16,22 +20,17 @@ IF v_adv_lock = false THEN RETURN; END IF; -v_tables_list_sql := 'SELECT parent_table - FROM @extschema@.part_config - WHERE undo_in_progress = false - AND automatic_maintenance = ''on'''; - -FOR v_row IN EXECUTE v_tables_list_sql +FOR v_parent_table IN + SELECT parent_table + FROM @extschema@.part_config + WHERE undo_in_progress = false + AND automatic_maintenance = 'on' LOOP /* * Run maintenance with a commit between each partition set - * TODO - Once PG11 is more mainstream, see about more full conversion of run_maintenance function as well as turning - * create_partition* functions into procedures to commit after every child table is made. May need to wait - * for more PROCEDURE features as well (return values, search_path, etc). - * - Also see about swapping names so this is the main object to call for maintenance instead of a function. */ - v_sql := format('SELECT %I.run_maintenance(%L, p_jobmon := %L', - '@extschema@', v_row.parent_table, p_jobmon); + v_sql := format('SELECT %s.run_maintenance(%L, p_jobmon := %L', + '@extschema@', v_parent_table, p_jobmon); IF p_analyze IS NOT NULL THEN v_sql := v_sql || format(', p_analyze := %L', p_analyze); @@ -51,4 +50,3 @@ END LOOP; PERFORM pg_advisory_unlock(hashtext('pg_partman run_maintenance')); END $$; - diff --git a/sql/procedures/undo_partition_proc.sql b/sql/procedures/undo_partition_proc.sql index ebdf8080..fc589a2e 100644 --- a/sql/procedures/undo_partition_proc.sql +++ b/sql/procedures/undo_partition_proc.sql @@ -1,31 +1,30 @@ CREATE PROCEDURE @extschema@.undo_partition_proc( p_parent_table text - , p_interval text DEFAULT NULL - , p_batch int DEFAULT NULL - , p_wait int DEFAULT 1 , p_target_table text DEFAULT NULL + , p_loop_count int DEFAULT NULL + , p_interval text DEFAULT NULL , p_keep_table boolean DEFAULT true , p_lock_wait int DEFAULT 0 , p_lock_wait_tries int DEFAULT 10 - , p_quiet boolean DEFAULT false + , p_wait int DEFAULT 1 , p_ignored_columns text[] DEFAULT NULL - , p_drop_cascade boolean DEFAULT false) + , p_drop_cascade boolean DEFAULT false + , p_quiet boolean DEFAULT false +) LANGUAGE plpgsql AS $$ DECLARE v_adv_lock boolean; -v_batch_count int := 0; v_is_autovac_off boolean := false; v_lockwait_count int := 0; +v_loop_count int := 0; v_parent_schema text; v_parent_tablename text; v_partition_type text; v_partitions_undone int; v_partitions_undone_total int := 0; -v_row record; v_rows_undone bigint; -v_target_schema text; v_target_tablename text; v_sql text; v_total bigint := 0; @@ -40,14 +39,14 @@ END IF; SELECT partition_type INTO v_partition_type -FROM @extschema@.part_config +FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: No entry in part_config found for given table: %', p_parent_table; END IF; -IF v_partition_type = 'native' AND p_target_table IS NULL THEN - RAISE EXCEPTION 'Natively partitioned table sets require setting the p_target_table parameter to undo partitioning.'; +IF p_target_table IS NULL THEN + RAISE EXCEPTION 'The p_target_table option must be set when undoing a partitioned table'; END IF; SELECT n.nspname, c.relname INTO v_parent_schema, v_parent_tablename @@ -60,7 +59,7 @@ AND c.relname = split_part(p_parent_table, '.', 2)::name; END IF; IF p_target_table IS NOT NULL THEN - SELECT n.nspname, c.relname INTO v_target_schema, v_target_tablename + SELECT c.relname INTO v_target_tablename FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid WHERE n.nspname = split_part(p_target_table, '.', 1)::name @@ -80,7 +79,7 @@ IF p_autovacuum_on = false THEN -- Add this parameter back to definition END IF; */ -v_sql := format('SELECT partitions_undone, rows_undone FROM %I.undo_partition (%L, p_keep_table := %L, p_lock_wait := %L' +v_sql := format('SELECT partitions_undone, rows_undone FROM %s.undo_partition (%L, p_keep_table := %L, p_lock_wait := %L' , '@extschema@' , p_parent_table , p_keep_table @@ -104,7 +103,7 @@ LOOP EXECUTE v_sql INTO v_partitions_undone, v_rows_undone; -- If lock wait timeout, do not increment the counter IF v_rows_undone != -1 THEN - v_batch_count := v_batch_count + 1; + v_loop_count := v_loop_count + 1; v_partitions_undone_total := v_partitions_undone_total + v_partitions_undone; v_total := v_total + v_rows_undone; v_lockwait_count := 0; @@ -116,15 +115,15 @@ LOOP END IF; IF p_quiet = false THEN IF v_rows_undone > 0 THEN - RAISE NOTICE 'Batch: %, Partitions undone this batch: %, Rows undone this batch: %', v_batch_count, v_partitions_undone, v_rows_undone; + RAISE NOTICE 'Batch: %, Partitions undone this batch: %, Rows undone this batch: %', v_loop_count, v_partitions_undone, v_rows_undone; ELSIF v_rows_undone = -1 THEN RAISE NOTICE 'Unable to obtain row locks for data to be moved. Trying again...'; END IF; END IF; COMMIT; - -- If no rows left or given batch argument limit is reached - IF v_rows_undone = 0 OR (p_batch > 0 AND v_batch_count >= p_batch) THEN + -- If no rows left or given loop argument limit is reached + IF v_rows_undone = 0 OR (p_loop_count > 0 AND v_loop_count >= p_loop_count) THEN EXIT; END IF; @@ -132,22 +131,22 @@ LOOP -- Added here to handle edge-case SELECT partition_type INTO v_partition_type - FROM @extschema@.part_config + FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF NOT FOUND THEN EXIT; END IF; PERFORM pg_sleep(p_wait); - - RAISE DEBUG 'v_partitions_undone: %, v_rows_undone: %, v_batch_count: %, v_total: %, v_lockwait_count: %, p_wait: %', v_partitions_undone, p_wait, v_rows_undone, v_batch_count, v_total, v_lockwait_count; + + RAISE DEBUG 'v_partitions_undone: %, v_rows_undone: %, v_loop_count: %, v_total: %, v_lockwait_count: %, p_wait: %', v_partitions_undone, p_wait, v_rows_undone, v_loop_count, v_total, v_lockwait_count; END LOOP; /* IF v_is_autovac_off = true THEN -- Reset autovac back to default if it was turned off by this procedure PERFORM @extschema@.autovacuum_reset(v_parent_schema, v_parent_tablename, v_source_schema, v_source_tablename); - COMMIT; + COMMIT; END IF; */ @@ -158,4 +157,3 @@ RAISE NOTICE 'Ensure to VACUUM ANALYZE the old parent & target table after undo END $$; - diff --git a/sql/tables/tables.sql b/sql/tables/tables.sql index 97626822..6751d983 100644 --- a/sql/tables/tables.sql +++ b/sql/tables/tables.sql @@ -2,93 +2,74 @@ CREATE TABLE @extschema@.part_config ( parent_table text NOT NULL , control text NOT NULL - , partition_type text NOT NULL , partition_interval text NOT NULL - , constraint_cols text[] + , partition_type text NOT NULL , premake int NOT NULL DEFAULT 4 - , optimize_trigger int NOT NULL DEFAULT 4 - , optimize_constraint int NOT NULL DEFAULT 30 - , epoch text NOT NULL DEFAULT 'none' - , inherit_fk boolean NOT NULL DEFAULT true + , automatic_maintenance text NOT NULL DEFAULT 'on' + , template_table text , retention text , retention_schema text - , retention_keep_table boolean NOT NULL DEFAULT true , retention_keep_index boolean NOT NULL DEFAULT true + , retention_keep_table boolean NOT NULL DEFAULT true + , epoch text NOT NULL DEFAULT 'none' + , constraint_cols text[] + , optimize_constraint int NOT NULL DEFAULT 30 , infinite_time_partitions boolean NOT NULL DEFAULT false , datetime_string text - , automatic_maintenance text NOT NULL DEFAULT 'on' , jobmon boolean NOT NULL DEFAULT true , sub_partition_set_full boolean NOT NULL DEFAULT false , undo_in_progress boolean NOT NULL DEFAULT false - , trigger_exception_handling BOOLEAN DEFAULT false - , upsert text NOT NULL DEFAULT '' - , trigger_return_null boolean NOT NULL DEFAULT true - , template_table text - , publications text[] , inherit_privileges boolean DEFAULT false , constraint_valid boolean DEFAULT true NOT NULL - , subscription_refresh text - , drop_cascade_fk BOOLEAN NOT NULL DEFAULT false - , ignore_default_data boolean NOT NULL DEFAULT false + , ignore_default_data boolean NOT NULL DEFAULT true + , default_table boolean DEFAULT true + , date_trunc_interval text , CONSTRAINT part_config_parent_table_pkey PRIMARY KEY (parent_table) , CONSTRAINT positive_premake_check CHECK (premake > 0) - , CONSTRAINT publications_no_empty_set_chk CHECK (publications <> '{}') ); + CREATE INDEX part_config_type_idx ON @extschema@.part_config (partition_type); -SELECT pg_catalog.pg_extension_config_dump('part_config', ''); +SELECT pg_catalog.pg_extension_config_dump('@extschema@.part_config'::regclass, ''); -- FK set deferrable because create_parent() & create_sub_parent() inserts to this table before part_config CREATE TABLE @extschema@.part_config_sub ( - sub_parent text - , sub_partition_type text NOT NULL + sub_parent text , sub_control text NOT NULL , sub_partition_interval text NOT NULL - , sub_constraint_cols text[] + , sub_partition_type text NOT NULL , sub_premake int NOT NULL DEFAULT 4 - , sub_optimize_trigger int NOT NULL DEFAULT 4 - , sub_optimize_constraint int NOT NULL DEFAULT 30 - , sub_epoch text NOT NULL DEFAULT 'none' - , sub_inherit_fk boolean NOT NULL DEFAULT true + , sub_automatic_maintenance text NOT NULL DEFAULT 'on' + , sub_template_table text , sub_retention text , sub_retention_schema text - , sub_retention_keep_table boolean NOT NULL DEFAULT true , sub_retention_keep_index boolean NOT NULL DEFAULT true + , sub_retention_keep_table boolean NOT NULL DEFAULT true + , sub_epoch text NOT NULL DEFAULT 'none' + , sub_constraint_cols text[] + , sub_optimize_constraint int NOT NULL DEFAULT 30 , sub_infinite_time_partitions boolean NOT NULL DEFAULT false - , sub_automatic_maintenance text NOT NULL DEFAULT 'on' , sub_jobmon boolean NOT NULL DEFAULT true - , sub_trigger_exception_handling BOOLEAN DEFAULT false - , sub_upsert TEXT NOT NULL DEFAULT '' - , sub_trigger_return_null boolean NOT NULL DEFAULT true - , sub_template_table text , sub_inherit_privileges boolean DEFAULT false , sub_constraint_valid boolean DEFAULT true NOT NULL - , sub_subscription_refresh text + , sub_ignore_default_data boolean NOT NULL DEFAULT true + , sub_default_table boolean default true , sub_date_trunc_interval TEXT - , sub_ignore_default_data boolean NOT NULL DEFAULT false , CONSTRAINT part_config_sub_pkey PRIMARY KEY (sub_parent) , CONSTRAINT part_config_sub_sub_parent_fkey FOREIGN KEY (sub_parent) REFERENCES @extschema@.part_config (parent_table) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED , CONSTRAINT positive_premake_check CHECK (sub_premake > 0) ); -SELECT pg_catalog.pg_extension_config_dump('part_config_sub', ''); +SELECT pg_catalog.pg_extension_config_dump('@extschema@.part_config_sub'::regclass, ''); --- Ensure the control column cannot be one of the additional constraint columns. +-- Ensure the control column cannot be one of the additional constraint columns. ALTER TABLE @extschema@.part_config ADD CONSTRAINT control_constraint_col_chk CHECK ((constraint_cols @> ARRAY[control]) <> true); ALTER TABLE @extschema@.part_config_sub ADD CONSTRAINT control_constraint_col_chk CHECK ((sub_constraint_cols @> ARRAY[sub_control]) <> true); ALTER TABLE @extschema@.part_config ADD CONSTRAINT retention_schema_not_empty_chk CHECK (retention_schema <> ''); ALTER TABLE @extschema@.part_config_sub ADD CONSTRAINT retention_schema_not_empty_chk CHECK (sub_retention_schema <> ''); -CREATE TABLE @extschema@.custom_time_partitions ( - parent_table text NOT NULL - , child_table text NOT NULL - , partition_range tstzrange NOT NULL - , PRIMARY KEY (parent_table, child_table)); -CREATE INDEX custom_time_partitions_partition_range_idx ON @extschema@.custom_time_partitions USING gist (partition_range); -SELECT pg_catalog.pg_extension_config_dump('custom_time_partitions', ''); - /* - * Custom view to help improve privilege lookups for pg_partman. + * Custom view to help improve privilege lookups for pg_partman. * information_schema is a performance bottleneck since indexes aren't being used properly. */ CREATE VIEW @extschema@.table_privs AS @@ -118,12 +99,12 @@ CREATE VIEW @extschema@.table_privs AS -- Put constraint functions & definitions here because having them in a separate file makes the ordering of their creation harder to control. Some require the above tables to exist first. -/* +/* * Check for valid config values for automatic maintenance * (not boolean to allow future values) */ CREATE FUNCTION @extschema@.check_automatic_maintenance_value (p_automatic_maintenance text) RETURNS boolean - LANGUAGE plpgsql IMMUTABLE + LANGUAGE plpgsql IMMUTABLE AS $$ DECLARE v_result boolean; @@ -159,36 +140,34 @@ $$; ALTER TABLE @extschema@.part_config -ADD CONSTRAINT part_config_epoch_check +ADD CONSTRAINT part_config_epoch_check CHECK (@extschema@.check_epoch_type(epoch)); ALTER TABLE @extschema@.part_config_sub -ADD CONSTRAINT part_config_sub_epoch_check +ADD CONSTRAINT part_config_sub_epoch_check CHECK (@extschema@.check_epoch_type(sub_epoch)); /* * Check for valid config table partition types */ -CREATE FUNCTION @extschema@.check_partition_type (p_type text) RETURNS boolean +-- Allow list/hash in future update +CREATE OR REPLACE FUNCTION @extschema@.check_partition_type (p_type text) RETURNS boolean LANGUAGE plpgsql IMMUTABLE SECURITY DEFINER SET search_path TO pg_catalog, pg_temp AS $$ DECLARE v_result boolean; BEGIN - SELECT p_type IN ('partman', 'time-custom', 'native') INTO v_result; + SELECT p_type IN ('range') INTO v_result; RETURN v_result; END $$; - ALTER TABLE @extschema@.part_config -ADD CONSTRAINT part_config_type_check +ADD CONSTRAINT part_config_type_check CHECK (@extschema@.check_partition_type(partition_type)); ALTER TABLE @extschema@.part_config_sub ADD CONSTRAINT part_config_sub_type_check CHECK (@extschema@.check_partition_type(sub_partition_type)); - - diff --git a/src/pg_partman_bgw.c b/src/pg_partman_bgw.c index d48759b3..6237a5f5 100644 --- a/src/pg_partman_bgw.c +++ b/src/pg_partman_bgw.c @@ -186,6 +186,7 @@ _PG_init(void) // Start BGW when database starts sprintf(worker.bgw_name, "pg_partman master background worker"); + sprintf(worker.bgw_type, "pg_partman background worker"); worker.bgw_flags = BGWORKER_SHMEM_ACCESS | BGWORKER_BACKEND_DATABASE_CONNECTION; worker.bgw_start_time = BgWorkerStart_RecoveryFinished; @@ -254,13 +255,13 @@ void pg_partman_bgw_main(Datum main_arg) { return; } - // Use method of shared_preload_libraries to split the pg_partman_bgw_dbname string found in src/backend/utils/init/miscinit.c - // Need a modifiable copy of string + // Use method of shared_preload_libraries to split the pg_partman_bgw_dbname string found in src/backend/utils/init/miscinit.c + // Need a modifiable copy of string if (pg_partman_bgw_dbname != NULL) { rawstring = pstrdup(pg_partman_bgw_dbname); - // Parse string into list of identifiers + // Parse string into list of identifiers if (!(*split_function_ptr)(rawstring, ',', &elemlist)) { - // syntax error in list + // syntax error in list pfree(rawstring); list_free(elemlist); ereport(LOG, @@ -268,12 +269,12 @@ void pg_partman_bgw_main(Datum main_arg) { errmsg("invalid list syntax in parameter \"pg_partman_bgw.dbname\" in postgresql.conf"))); return; } - + dbcounter = 0; foreach(l, elemlist) { char *dbname = (char *) lfirst(l); - + elog(DEBUG1, "Dynamic bgw launch begun for %s (%d)", dbname, dbcounter); worker.bgw_flags = BGWORKER_SHMEM_ACCESS | BGWORKER_BACKEND_DATABASE_CONNECTION; @@ -289,6 +290,7 @@ void pg_partman_bgw_main(Datum main_arg) { memcpy(worker.bgw_name + sizeof(worker.bgw_name) - sizeof(truncated_mark), truncated_mark, sizeof(truncated_mark)); } + sprintf(worker.bgw_type, "pg_partman background worker"); worker.bgw_main_arg = Int32GetDatum(dbcounter); worker.bgw_notify_pid = MyProcPid; @@ -318,7 +320,7 @@ void pg_partman_bgw_main(Datum main_arg) { } Assert(status == BGWH_STARTED); - // Shutdown wait function introduced in 9.5. The latch problems this wait fixes are only encountered in + // Shutdown wait function introduced in 9.5. The latch problems this wait fixes are only encountered in // 9.6 and later. elog(DEBUG1, "Waiting for BGW shutdown..."); status = WaitForBackgroundWorkerShutdown(handle); @@ -352,7 +354,7 @@ void pg_partman_bgw_main(Datum main_arg) { /* * Unable to pass the database name as a string argument (not sure why yet) - * Instead, the GUC is parsed both in the main function and below and a counter integer + * Instead, the GUC is parsed both in the main function and below and a counter integer * is passed to determine which database the BGW will run in. */ void pg_partman_bgw_run_maint(Datum arg) { @@ -380,9 +382,9 @@ void pg_partman_bgw_run_maint(Datum arg) { elog(DEBUG1, "Before parsing dbname GUC in dynamic main func: %s", pg_partman_bgw_dbname); rawstring = pstrdup(pg_partman_bgw_dbname); elog(DEBUG1, "GUC rawstring copy: %s", rawstring); - // Parse string into list of identifiers + // Parse string into list of identifiers if (!(*split_function_ptr)(rawstring, ',', &elemlist)) { - // syntax error in list + // syntax error in list pfree(rawstring); list_free(elemlist); ereport(LOG, @@ -393,7 +395,7 @@ void pg_partman_bgw_run_maint(Datum arg) { dbname = list_nth(elemlist, db_main_counter); elog(DEBUG1, "Parsing dbname list: %s (%d)", dbname, db_main_counter); - + if (strcmp(dbname, "template1") == 0) { elog(DEBUG1, "Default database name found in dbname local variable (\"template1\")."); } @@ -401,7 +403,7 @@ void pg_partman_bgw_run_maint(Datum arg) { elog(DEBUG1, "Before run_maint initialize connection for db %s", dbname); BackgroundWorkerInitializeConnection(dbname, pg_partman_bgw_role, 0); - + elog(DEBUG1, "After run_maint initialize connection for db %s", dbname); initStringInfo(&buf); @@ -444,7 +446,7 @@ void pg_partman_bgw_run_maint(Datum arg) { return; } - // If so then actually log that it's started for that database. + // If so then actually log that it's started for that database. elog(LOG, "%s dynamic background worker initialized with role %s on database %s" , MyBgworkerEntry->bgw_name , pg_partman_bgw_role @@ -529,4 +531,3 @@ void pg_partman_bgw_run_maint(Datum arg) { return; } - diff --git a/test/test-dump-definition.sql b/test/test-dump-definition.sql index 268ca302..2bca2dfe 100644 --- a/test/test-dump-definition.sql +++ b/test/test-dump-definition.sql @@ -36,7 +36,6 @@ SELECT bag_eq( 'undo_in_progress', 'inherit_privileges', 'constraint_valid', - 'subscription_refresh', 'ignore_default_data', 'default_table', 'date_trunc_interval' @@ -89,7 +88,6 @@ UPDATE partman.part_config SET sub_partition_set_full = ''f'', inherit_privileges = ''t'', constraint_valid = ''f'', - subscription_refresh = NULL, ignore_default_data = ''t'' WHERE parent_table = ''public.declarative_objects'';' ); diff --git a/test/test-time-30sec.sql b/test/test-time-30sec.sql index 11dbff5f..a801bbe9 100644 --- a/test/test-time-30sec.sql +++ b/test/test-time-30sec.sql @@ -2,7 +2,7 @@ -- Additional tests: Inherit privileges -- May fail when run in the first or second 30 seconds of the minute due to rounding down to the nearest minute. -- If it does, wait until the next block of 30 seconds starts and try again. --- If it is failing no matter when it is run, please create an issue on Github with a log of your result when running with "pg_prove -ovf" +-- If it is failing no matter when it is run, please create an issue on Github with a log of your result when running with "pg_prove -ovf" \set ON_ERROR_ROLLBACK 1 @@ -24,7 +24,6 @@ CREATE TABLE partman_test.time_taptest_table ( , col3 timestamptz NOT NULL DEFAULT now()) PARTITION BY RANGE (col3); CREATE TABLE partman_test.time_taptest_table_template (LIKE partman_test.time_taptest_table INCLUDING ALL); --- TODO change back to parent table when fixed in future pg version ALTER TABLE partman_test.time_taptest_table_template ADD PRIMARY KEY (col1); CREATE TABLE partman_test.undo_taptest (LIKE partman_test.time_taptest_table INCLUDING ALL); @@ -34,7 +33,7 @@ GRANT SELECT,INSERT,UPDATE ON partman_test.time_taptest_table TO partman_basic; GRANT ALL ON partman_test.time_taptest_table TO partman_revoke; SELECT create_parent('partman_test.time_taptest_table', 'col3', '30 seconds', p_template_table := 'partman_test.time_taptest_table_template'); --- Must run_maintenance because when interval time is between 1 hour and 1 minute since the first partition name done by above is always the nearest hour rounded down +-- Must run_maintenance because when interval time is between 1 hour and 1 minute since the first partition name done by above is always the nearest hour rounded down SELECT run_maintenance(); UPDATE part_config SET inherit_privileges = TRUE; SELECT reapply_privileges('partman_test.time_taptest_table'); @@ -42,190 +41,190 @@ SELECT reapply_privileges('partman_test.time_taptest_table'); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(1,10), CURRENT_TIMESTAMP); -SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYYMMDD_HH24MISS'), +SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYYMMDD_HH24MISS'), 'Check time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP), 'YYYYMMDD_HH24MISS')||' exists'); -SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 secs'::interval, 'YYYYMMDD_HH24MISS'), - 'Check time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 secs'::interval, 'YYYYMMDD_HH24MISS'), + 'Check time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 secs'::interval, 'YYYYMMDD_HH24MISS')||' exists'); -SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 secs'::interval, 'YYYYMMDD_HH24MISS'), - 'Check time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 secs'::interval, 'YYYYMMDD_HH24MISS'), + 'Check time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 secs'::interval, 'YYYYMMDD_HH24MISS')||' exists'); -SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 secs'::interval, 'YYYYMMDD_HH24MISS'), - 'Check time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 secs'::interval, 'YYYYMMDD_HH24MISS'), + 'Check time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 secs'::interval, 'YYYYMMDD_HH24MISS')||' exists'); -SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 secs'::interval, 'YYYYMMDD_HH24MISS'), - 'Check time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 secs'::interval, 'YYYYMMDD_HH24MISS'), + 'Check time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 secs'::interval, 'YYYYMMDD_HH24MISS')||' exists'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 secs'::interval, 'YYYYMMDD_HH24MISS'), - 'Check time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 secs'::interval, 'YYYYMMDD_HH24MISS'), + 'Check time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 secs'::interval, 'YYYYMMDD_HH24MISS')||' does not exist'); -SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'30 secs'::interval, 'YYYYMMDD_HH24MISS'), - 'Check time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'30 secs'::interval, 'YYYYMMDD_HH24MISS'), + 'Check time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'30 secs'::interval, 'YYYYMMDD_HH24MISS')||' exists'); -SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'60 secs'::interval, 'YYYYMMDD_HH24MISS'), - 'Check time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'60 secs'::interval, 'YYYYMMDD_HH24MISS'), + 'Check time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'60 secs'::interval, 'YYYYMMDD_HH24MISS')||' exists'); -SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'90 secs'::interval, 'YYYYMMDD_HH24MISS'), - 'Check time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'90 secs'::interval, 'YYYYMMDD_HH24MISS'), + 'Check time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'90 secs'::interval, 'YYYYMMDD_HH24MISS')||' exists'); -SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'120 secs'::interval, 'YYYYMMDD_HH24MISS'), - 'Check time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'120 secs'::interval, 'YYYYMMDD_HH24MISS'), + 'Check time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'120 secs'::interval, 'YYYYMMDD_HH24MISS')||' exists'); -/* extra previous tables may exist due to new rounding down of the hour. Test left here for manual checking -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'150 secs'::interval, 'YYYYMMDD_HH24MISS'), - 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +/* extra previous tables may exist due to new rounding down of the hour. Test left here for manual checking +SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'150 secs'::interval, 'YYYYMMDD_HH24MISS'), + 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'150 secs'::interval, 'YYYYMMDD_HH24MISS')||' does not exist'); */ -SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYYMMDD_HH24MISS'), ARRAY['col1'], - 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYYMMDD_HH24MISS'), ARRAY['col1'], + 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYYMMDD_HH24MISS')); -SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 secs'::interval, 'YYYYMMDD_HH24MISS'), ARRAY['col1'], - 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 secs'::interval, 'YYYYMMDD_HH24MISS'), ARRAY['col1'], + 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 secs'::interval, 'YYYYMMDD_HH24MISS'), ARRAY['col1'], - 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 secs'::interval, 'YYYYMMDD_HH24MISS'), ARRAY['col1'], + 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 secs'::interval, 'YYYYMMDD_HH24MISS'), ARRAY['col1'], - 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 secs'::interval, 'YYYYMMDD_HH24MISS'), ARRAY['col1'], + 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 secs'::interval, 'YYYYMMDD_HH24MISS'), ARRAY['col1'], - 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 secs'::interval, 'YYYYMMDD_HH24MISS'), ARRAY['col1'], + 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYYMMDD_HH24MISS'), ARRAY['col1'], - 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYYMMDD_HH24MISS'), ARRAY['col1'], + 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYYMMDD_HH24MISS')); -SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'30 secs'::interval, 'YYYYMMDD_HH24MISS'), ARRAY['col1'], - 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'30 secs'::interval, 'YYYYMMDD_HH24MISS'), ARRAY['col1'], + 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'30 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'60 secs'::interval, 'YYYYMMDD_HH24MISS'), ARRAY['col1'], - 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'60 secs'::interval, 'YYYYMMDD_HH24MISS'), ARRAY['col1'], + 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'60 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'90 secs'::interval, 'YYYYMMDD_HH24MISS'), ARRAY['col1'], - 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'90 secs'::interval, 'YYYYMMDD_HH24MISS'), ARRAY['col1'], + 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'90 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'120 secs'::interval, 'YYYYMMDD_HH24MISS'), ARRAY['col1'], - 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'120 secs'::interval, 'YYYYMMDD_HH24MISS'), ARRAY['col1'], + 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'120 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYYMMDD_HH24MISS'), 'partman_basic', - ARRAY['SELECT','INSERT','UPDATE'], - 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYYMMDD_HH24MISS'), 'partman_basic', + ARRAY['SELECT','INSERT','UPDATE'], + 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', - ARRAY['SELECT','INSERT','UPDATE'], - 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', + ARRAY['SELECT','INSERT','UPDATE'], + 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', - ARRAY['SELECT','INSERT','UPDATE'], - 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', + ARRAY['SELECT','INSERT','UPDATE'], + 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', - ARRAY['SELECT','INSERT','UPDATE'], - 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', + ARRAY['SELECT','INSERT','UPDATE'], + 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', - ARRAY['SELECT','INSERT','UPDATE'], - 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', + ARRAY['SELECT','INSERT','UPDATE'], + 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'30 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', - ARRAY['SELECT','INSERT','UPDATE'], - 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'30 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', + ARRAY['SELECT','INSERT','UPDATE'], + 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'30 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'60 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', - ARRAY['SELECT','INSERT','UPDATE'], - 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'60 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', + ARRAY['SELECT','INSERT','UPDATE'], + 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'60 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'90 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', - ARRAY['SELECT','INSERT','UPDATE'], - 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'90 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', + ARRAY['SELECT','INSERT','UPDATE'], + 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'90 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'120 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', - ARRAY['SELECT','INSERT','UPDATE'], - 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'120 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', + ARRAY['SELECT','INSERT','UPDATE'], + 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'120 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYYMMDD_HH24MISS'), 'partman_revoke', - ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], - 'Check partman_revoke privileges of time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYYMMDD_HH24MISS'), 'partman_revoke', + ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], + 'Check partman_revoke privileges of time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', - ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], - 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', + ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], + 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', - ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], - 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', + ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], + 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', - ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], - 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', + ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], + 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', - ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], - 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', + ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], + 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'30 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', - ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], - 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'30 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', + ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], + 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'30 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'60 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', - ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], - 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'60 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', + ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], + 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'60 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'90 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', - ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], - 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'90 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', + ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], + 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'90 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'120 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', - ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], - 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'120 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', + ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], + 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'120 secs'::interval, 'YYYYMMDD_HH24MISS')); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_default', 'Check that default table has no data'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table', ARRAY[10], 'Check count from parent table'); -SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYYMMDD_HH24MISS'), - ARRAY[10], 'Check count from time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYYMMDD_HH24MISS'), + ARRAY[10], 'Check count from time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYYMMDD_HH24MISS')); REVOKE INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER ON partman_test.time_taptest_table FROM partman_revoke; @@ -239,140 +238,140 @@ INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(86,100), CURRENT_TIMESTAMP - '120 secs'::interval); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_default', 'Check that default table has no data'); -SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 secs'::interval, 'YYYYMMDD_HH24MISS'), - ARRAY[10], 'Check count from time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 secs'::interval, 'YYYYMMDD_HH24MISS'), + ARRAY[10], 'Check count from time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 secs'::interval, 'YYYYMMDD_HH24MISS'), - ARRAY[5], 'Check count from time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 secs'::interval, 'YYYYMMDD_HH24MISS'), + ARRAY[5], 'Check count from time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 secs'::interval, 'YYYYMMDD_HH24MISS'), - ARRAY[5], 'Check count from time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 secs'::interval, 'YYYYMMDD_HH24MISS'), + ARRAY[5], 'Check count from time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 secs'::interval, 'YYYYMMDD_HH24MISS'), - ARRAY[7], 'Check count from time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 secs'::interval, 'YYYYMMDD_HH24MISS'), + ARRAY[7], 'Check count from time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'30 secs'::interval, 'YYYYMMDD_HH24MISS'), - ARRAY[10], 'Check count from time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'30 secs'::interval, 'YYYYMMDD_HH24MISS'), + ARRAY[10], 'Check count from time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'30 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'60 secs'::interval, 'YYYYMMDD_HH24MISS'), - ARRAY[21], 'Check count from time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'60 secs'::interval, 'YYYYMMDD_HH24MISS'), + ARRAY[21], 'Check count from time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'60 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'90 secs'::interval, 'YYYYMMDD_HH24MISS'), - ARRAY[15], 'Check count from time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'90 secs'::interval, 'YYYYMMDD_HH24MISS'), + ARRAY[15], 'Check count from time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'90 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'120 secs'::interval, 'YYYYMMDD_HH24MISS'), - ARRAY[15], 'Check count from time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'120 secs'::interval, 'YYYYMMDD_HH24MISS'), + ARRAY[15], 'Check count from time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'120 secs'::interval, 'YYYYMMDD_HH24MISS')); UPDATE part_config SET premake = 5 WHERE parent_table = 'partman_test.time_taptest_table'; SELECT run_maintenance(); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(101,122), CURRENT_TIMESTAMP + '150 secs'::interval); -SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 secs'::interval, 'YYYYMMDD_HH24MISS'), - 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 secs'::interval, 'YYYYMMDD_HH24MISS'), + 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 secs'::interval, 'YYYYMMDD_HH24MISS')||' exists'); -SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 secs'::interval, 'YYYYMMDD_HH24MISS'), - 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 secs'::interval, 'YYYYMMDD_HH24MISS'), + 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 secs'::interval, 'YYYYMMDD_HH24MISS')||' exists'); -SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 secs'::interval, 'YYYYMMDD_HH24MISS'), - 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 secs'::interval, 'YYYYMMDD_HH24MISS'), + 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 secs'::interval, 'YYYYMMDD_HH24MISS')||' exists'); -SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 secs'::interval, 'YYYYMMDD_HH24MISS'), - 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 secs'::interval, 'YYYYMMDD_HH24MISS'), + 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 secs'::interval, 'YYYYMMDD_HH24MISS')||' exists'); -SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'270 secs'::interval, 'YYYYMMDD_HH24MISS'), - 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'270 secs'::interval, 'YYYYMMDD_HH24MISS'), + 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'270 secs'::interval, 'YYYYMMDD_HH24MISS')||' exists'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 secs'::interval, 'YYYYMMDD_HH24MISS'), - 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 secs'::interval, 'YYYYMMDD_HH24MISS'), + 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 secs'::interval, 'YYYYMMDD_HH24MISS')||' exists'); -SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 secs'::interval, 'YYYYMMDD_HH24MISS'), ARRAY['col1'], - 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 secs'::interval, 'YYYYMMDD_HH24MISS'), ARRAY['col1'], + 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 secs'::interval, 'YYYYMMDD_HH24MISS'), ARRAY['col1'], - 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 secs'::interval, 'YYYYMMDD_HH24MISS'), ARRAY['col1'], + 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 secs'::interval, 'YYYYMMDD_HH24MISS'), ARRAY['col1'], - 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 secs'::interval, 'YYYYMMDD_HH24MISS'), ARRAY['col1'], + 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 secs'::interval, 'YYYYMMDD_HH24MISS'), ARRAY['col1'], - 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 secs'::interval, 'YYYYMMDD_HH24MISS'), ARRAY['col1'], + 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'270 secs'::interval, 'YYYYMMDD_HH24MISS'), ARRAY['col1'], - 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'270 secs'::interval, 'YYYYMMDD_HH24MISS'), ARRAY['col1'], + 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'270 secs'::interval, 'YYYYMMDD_HH24MISS')); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_default', 'Check that default table has no data'); -SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 secs'::interval, 'YYYYMMDD_HH24MISS'), - ARRAY[22], 'Check count from time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 secs'::interval, 'YYYYMMDD_HH24MISS'), + ARRAY[22], 'Check count from time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', - ARRAY['SELECT','INSERT','UPDATE'], - 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', + ARRAY['SELECT','INSERT','UPDATE'], + 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', - ARRAY['SELECT','INSERT','UPDATE'], - 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', + ARRAY['SELECT','INSERT','UPDATE'], + 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', - ARRAY['SELECT','INSERT','UPDATE'], - 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', + ARRAY['SELECT','INSERT','UPDATE'], + 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', - ARRAY['SELECT','INSERT','UPDATE'], - 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', + ARRAY['SELECT','INSERT','UPDATE'], + 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'270 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', - ARRAY['SELECT','INSERT','UPDATE'], - 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'270 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', + ARRAY['SELECT','INSERT','UPDATE'], + 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'270 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', - ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', + ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', - ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', + ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', - ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', + ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', - ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', + ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'270 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', - ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'270 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', + ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'270 secs'::interval, 'YYYYMMDD_HH24MISS')); GRANT DELETE ON partman_test.time_taptest_table TO partman_basic; @@ -385,334 +384,334 @@ INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_default', 'Check that default table has no data'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table', ARRAY[148], 'Check count from parent table'); -SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 secs'::interval, 'YYYYMMDD_HH24MISS'), - ARRAY[28], 'Check count from time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 secs'::interval, 'YYYYMMDD_HH24MISS'), + ARRAY[28], 'Check count from time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 secs'::interval, 'YYYYMMDD_HH24MISS'), - 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 secs'::interval, 'YYYYMMDD_HH24MISS'), + 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 secs'::interval, 'YYYYMMDD_HH24MISS')||' exists'); -SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'330 secs'::interval, 'YYYYMMDD_HH24MISS'), - 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'330 secs'::interval, 'YYYYMMDD_HH24MISS'), + 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'330 secs'::interval, 'YYYYMMDD_HH24MISS')||' exists'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'360 mins'::interval, 'YYYYMMDD_HH24MISS'), - 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'360 mins'::interval, 'YYYYMMDD_HH24MISS'), + 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'360 mins'::interval, 'YYYYMMDD_HH24MISS')||' exists'); -SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 secs'::interval, 'YYYYMMDD_HH24MISS'), ARRAY['col1'], - 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 secs'::interval, 'YYYYMMDD_HH24MISS'), ARRAY['col1'], + 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'330 secs'::interval, 'YYYYMMDD_HH24MISS'), ARRAY['col1'], - 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'330 secs'::interval, 'YYYYMMDD_HH24MISS'), ARRAY['col1'], + 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'330 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', - ARRAY['SELECT','INSERT','UPDATE','DELETE'], - 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', + ARRAY['SELECT','INSERT','UPDATE','DELETE'], + 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'330 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', - ARRAY['SELECT','INSERT','UPDATE','DELETE'], - 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'330 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', + ARRAY['SELECT','INSERT','UPDATE','DELETE'], + 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'330 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', - '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', + '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'330 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', - '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'330 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', + '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'330 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_owner', - 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_owner', + 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'330 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_owner', - 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'330 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_owner', + 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'330 secs'::interval, 'YYYYMMDD_HH24MISS')); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(200,210), CURRENT_TIMESTAMP + '600 mins'::interval); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table_default', ARRAY[11], 'Check that data outside scope goes to default'); SELECT reapply_privileges('partman_test.time_taptest_table'); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYYMMDD_HH24MISS'), 'partman_basic', - ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], - 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYYMMDD_HH24MISS'), 'partman_basic', + ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], + 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'30 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', - ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], - 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'30 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', + ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], + 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'30 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'60 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', - ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], - 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'60 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', + ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], + 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'60 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'90 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', - ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], - 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'90 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', + ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], + 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'90 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'120 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', - ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], - 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'120 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', + ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], + 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'120 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', - ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], - 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', + ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], + 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', - ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], - 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', + ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], + 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', - ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], - 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', + ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], + 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', - ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], - 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', + ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], + 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', - ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], - 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', + ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], + 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', - ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], - 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', + ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], + 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', - ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], - 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', + ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], + 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', - ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], - 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', + ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], + 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'270 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', - ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], - 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'270 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', + ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], + 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'270 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', - ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], - 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', + ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], + 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'330 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', - ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], - 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'330 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_basic', + ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], + 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'330 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYYMMDD_HH24MISS'), 'partman_revoke', - '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYYMMDD_HH24MISS'), 'partman_revoke', + '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', - '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', + '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', - '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', + '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', - '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', + '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', - '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', + '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', - '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', + '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', - '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', + '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', - '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', + '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', - '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', + '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'270 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', - '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'270 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', + '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'270 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', - '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', + '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'330 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', - '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'330 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_revoke', + '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'330 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYYMMDD_HH24MISS'), 'partman_owner', - 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYYMMDD_HH24MISS'), 'partman_owner', + 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYYMMDD_HH24MISS')); -SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'30 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_owner', - 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'30 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_owner', + 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'30 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'60 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_owner', - 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'60 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_owner', + 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'60 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'90 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_owner', - 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'90 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_owner', + 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'90 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'120 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_owner', - 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'120 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_owner', + 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'120 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_owner', - 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_owner', + 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_owner', - 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_owner', + 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_owner', - 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_owner', + 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_owner', - 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_owner', + 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_owner', - 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_owner', + 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_owner', - 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_owner', + 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_owner', - 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_owner', + 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_owner', - 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_owner', + 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'270 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_owner', - 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'270 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_owner', + 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'270 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_owner', - 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_owner', + 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 secs'::interval, 'YYYYMMDD_HH24MISS')); -SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'330 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_owner', - 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'330 secs'::interval, 'YYYYMMDD_HH24MISS'), 'partman_owner', + 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'330 secs'::interval, 'YYYYMMDD_HH24MISS')); SELECT drop_partition_time('partman_test.time_taptest_table', '90 secs', p_keep_table := false); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'120 secs'::interval, 'YYYYMMDD_HH24MISS'), - 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'120 secs'::interval, 'YYYYMMDD_HH24MISS'), + 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'120 secs'::interval, 'YYYYMMDD_HH24MISS')||' does not exist'); UPDATE part_config SET retention = '60 secs'::interval WHERE parent_table = 'partman_test.time_taptest_table'; SELECT drop_partition_time('partman_test.time_taptest_table', p_retention_schema := 'partman_retention_test'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'90 secs'::interval, 'YYYYMMDD_HH24MISS'), - 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'90 secs'::interval, 'YYYYMMDD_HH24MISS'), + 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'90 secs'::interval, 'YYYYMMDD_HH24MISS')||' does not exist'); -SELECT has_table('partman_retention_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'90 secs'::interval, 'YYYYMMDD_HH24MISS'), - 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT has_table('partman_retention_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'90 secs'::interval, 'YYYYMMDD_HH24MISS'), + 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'90 secs'::interval, 'YYYYMMDD_HH24MISS')||' got moved to new schema'); SELECT undo_partition('partman_test.time_taptest_table', p_loop_count => 20, p_target_table := 'partman_test.undo_taptest', p_keep_table := false); SELECT results_eq('SELECT count(*)::int FROM partman_test.undo_taptest', ARRAY[129], 'Check count from target table after undo'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYYMMDD_HH24MISS'), - 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYYMMDD_HH24MISS'), + 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYYMMDD_HH24MISS')||' does not exist'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'30 secs'::interval, 'YYYYMMDD_HH24MISS'), - 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'30 secs'::interval, 'YYYYMMDD_HH24MISS'), + 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'30 secs'::interval, 'YYYYMMDD_HH24MISS')||' does not exist'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'60 secs'::interval, 'YYYYMMDD_HH24MISS'), - 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'60 secs'::interval, 'YYYYMMDD_HH24MISS'), + 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'60 secs'::interval, 'YYYYMMDD_HH24MISS')||' does not exist'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 secs'::interval, 'YYYYMMDD_HH24MISS'), - 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 secs'::interval, 'YYYYMMDD_HH24MISS'), + 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 secs'::interval, 'YYYYMMDD_HH24MISS')||' does not exist'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 secs'::interval, 'YYYYMMDD_HH24MISS'), - 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 secs'::interval, 'YYYYMMDD_HH24MISS'), + 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 secs'::interval, 'YYYYMMDD_HH24MISS')||' does not exist'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 secs'::interval, 'YYYYMMDD_HH24MISS'), - 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 secs'::interval, 'YYYYMMDD_HH24MISS'), + 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 secs'::interval, 'YYYYMMDD_HH24MISS')||' does not exist'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 secs'::interval, 'YYYYMMDD_HH24MISS'), - 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 secs'::interval, 'YYYYMMDD_HH24MISS'), + 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 secs'::interval, 'YYYYMMDD_HH24MISS')||' does not exist'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 secs'::interval, 'YYYYMMDD_HH24MISS'), - 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 secs'::interval, 'YYYYMMDD_HH24MISS'), + 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 secs'::interval, 'YYYYMMDD_HH24MISS')||' does not exist'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 secs'::interval, 'YYYYMMDD_HH24MISS'), - 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 secs'::interval, 'YYYYMMDD_HH24MISS'), + 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 secs'::interval, 'YYYYMMDD_HH24MISS')||' does not exist'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 secs'::interval, 'YYYYMMDD_HH24MISS'), - 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 secs'::interval, 'YYYYMMDD_HH24MISS'), + 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 secs'::interval, 'YYYYMMDD_HH24MISS')||' does not exist'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 secs'::interval, 'YYYYMMDD_HH24MISS'), - 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 secs'::interval, 'YYYYMMDD_HH24MISS'), + 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 secs'::interval, 'YYYYMMDD_HH24MISS')||' does not exist'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'270 secs'::interval, 'YYYYMMDD_HH24MISS'), - 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'270 secs'::interval, 'YYYYMMDD_HH24MISS'), + 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'270 secs'::interval, 'YYYYMMDD_HH24MISS')||' does not exist'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 secs'::interval, 'YYYYMMDD_HH24MISS'), - 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 secs'::interval, 'YYYYMMDD_HH24MISS'), + 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 secs'::interval, 'YYYYMMDD_HH24MISS')||' does not exist'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + - '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'330 secs'::interval, 'YYYYMMDD_HH24MISS'), - 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + +SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'330 secs'::interval, 'YYYYMMDD_HH24MISS'), + 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'330 secs'::interval, 'YYYYMMDD_HH24MISS')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_template', 'Check that template table was dropped'); diff --git a/test/test-time-epoch-milliseconds-weekly.sql b/test/test-time-epoch-milliseconds-weekly.sql index da13e15b..b5e12c5f 100644 --- a/test/test-time-epoch-milliseconds-weekly.sql +++ b/test/test-time-epoch-milliseconds-weekly.sql @@ -1,5 +1,5 @@ -- ########## TIME WEEKLY EPOCH TESTS ########## --- Other tests: +-- Other tests: -- combination of start_partition & constraint_cols/optimize_constraint. Requires manually running apply_constraint to set other old partitions -- Test millisecond epoch time partitioning @@ -16,107 +16,106 @@ CREATE SCHEMA partman_retention_test; CREATE TABLE partman_test.time_taptest_table ( col1 bigint , col2 text - , col3 bigint NOT NULL DEFAULT (extract('epoch' from CURRENT_TIMESTAMP)::bigint)*1000) + , col3 bigint NOT NULL DEFAULT (extract('epoch' from date_trunc('week',CURRENT_TIMESTAMP))::bigint)*1000) PARTITION BY RANGE (col3); 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); CREATE TABLE partman_test.undo_taptest (LIKE partman_test.time_taptest_table INCLUDING ALL); --- TODO See if there's a way to force this to always start on a sunday SELECT create_parent('partman_test.time_taptest_table' , 'col3' , '1 week' , p_constraint_cols => '{"col1"}' - , p_epoch => 'milliseconds' + , p_epoch => 'milliseconds' , p_premake => 2 - , p_start_partition => to_char(CURRENT_TIMESTAMP-'8 weeks'::interval, 'YYYY-MM-DD HH24:MI:SS') + , p_start_partition => to_char(date_trunc('week',CURRENT_TIMESTAMP)-'8 weeks'::interval, 'YYYY-MM-DD HH24:MI:SS') , p_template_table => 'partman_test.template_time_taptest_table' ); -INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(1,10), (extract('epoch' from CURRENT_TIMESTAMP - '8 weeks'::interval)::bigint)*1000); - -SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYYMMDD'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP, 'YYYYMMDD')||' exists'); -SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'YYYYMMDD')||' exists (+1 weeks)'); -SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'YYYYMMDD')||' exists (+2 weeks)'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'YYYYMMDD')||' does not exist (+3 weeks)'); -SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'YYYYMMDD')||' exists (-1 weeks)'); -SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'YYYYMMDD')||' exists (-2 weeks)'); -SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'YYYYMMDD')||' exists (-3 weeks)'); -SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'YYYYMMDD')||' exists (-4 weeks)'); -SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'YYYYMMDD')||' exists (-5 weeks)'); -SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'YYYYMMDD')||' exists (-6 weeks)'); -SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'YYYYMMDD')||' exists (-7 weeks)'); -SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'8 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'8 weeks'::interval, 'YYYYMMDD')||' exists (-8 weeks)'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'9 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'9 weeks'::interval, 'YYYYMMDD')||' does not exist (-9 weeks)'); - -SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYYMMDD'), ARRAY['col1'], - 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYYMMDD')); -SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'YYYYMMDD'), ARRAY['col1'], - 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'YYYYMMDD')||' (+1 weeks)'); -SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'YYYYMMDD'), ARRAY['col1'], - 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'YYYYMMDD')||' (+2 weeks)'); -SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'YYYYMMDD'), ARRAY['col1'], - 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'YYYYMMDD')||' (-1 weeks)'); -SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'YYYYMMDD'), ARRAY['col1'], - 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'YYYYMMDD')||' (-2 weeks)'); -SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'YYYYMMDD'), ARRAY['col1'], - 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'YYYYMMDD')||' (-3 weeks)'); -SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'YYYYMMDD'), ARRAY['col1'], - 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'YYYYMMDD')||' (-4 weeks)'); -SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'YYYYMMDD'), ARRAY['col1'], - 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'YYYYMMDD')||' (-5 weeks)'); -SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'YYYYMMDD'), ARRAY['col1'], - 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'YYYYMMDD')||' (-6 weeks)'); -SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'YYYYMMDD'), ARRAY['col1'], - 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'YYYYMMDD')||' (-7 weeks)'); -SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'8 weeks'::interval, 'YYYYMMDD'), ARRAY['col1'], - 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'8 weeks'::interval, 'YYYYMMDD')||' (-8 weeks)'); +INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(1,10), (extract('epoch' from date_trunc('week',CURRENT_TIMESTAMP) - '8 weeks'::interval)::bigint)*1000); + +SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP), 'YYYYMMDD'), 'Check time_taptest_table_'||to_char(date_trunc('week',CURRENT_TIMESTAMP), 'YYYYMMDD')||' exists'); +SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)+'1 week'::interval, 'YYYYMMDD'), + 'Check time_taptest_table_'||to_char(date_trunc('week',CURRENT_TIMESTAMP)+'1 week'::interval, 'YYYYMMDD')||' exists (+1 weeks)'); +SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)+'2 weeks'::interval, 'YYYYMMDD'), + 'Check time_taptest_table_'||to_char(date_trunc('week',CURRENT_TIMESTAMP)+'2 weeks'::interval, 'YYYYMMDD')||' exists (+2 weeks)'); +SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)+'3 weeks'::interval, 'YYYYMMDD'), + 'Check time_taptest_table_'||to_char(date_trunc('week',CURRENT_TIMESTAMP)+'5 weeks'::interval, 'YYYYMMDD')||' does not exist (+3 weeks)'); +SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'1 week'::interval, 'YYYYMMDD'), + 'Check time_taptest_table_'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'1 week'::interval, 'YYYYMMDD')||' exists (-1 weeks)'); +SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'2 weeks'::interval, 'YYYYMMDD'), + 'Check time_taptest_table_'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'2 weeks'::interval, 'YYYYMMDD')||' exists (-2 weeks)'); +SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'3 weeks'::interval, 'YYYYMMDD'), + 'Check time_taptest_table_'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'3 weeks'::interval, 'YYYYMMDD')||' exists (-3 weeks)'); +SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'4 weeks'::interval, 'YYYYMMDD'), + 'Check time_taptest_table_'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'4 weeks'::interval, 'YYYYMMDD')||' exists (-4 weeks)'); +SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'5 weeks'::interval, 'YYYYMMDD'), + 'Check time_taptest_table_'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'5 weeks'::interval, 'YYYYMMDD')||' exists (-5 weeks)'); +SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'6 weeks'::interval, 'YYYYMMDD'), + 'Check time_taptest_table_'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'6 weeks'::interval, 'YYYYMMDD')||' exists (-6 weeks)'); +SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'7 weeks'::interval, 'YYYYMMDD'), + 'Check time_taptest_table_'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'7 weeks'::interval, 'YYYYMMDD')||' exists (-7 weeks)'); +SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'8 weeks'::interval, 'YYYYMMDD'), + 'Check time_taptest_table_'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'8 weeks'::interval, 'YYYYMMDD')||' exists (-8 weeks)'); +SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'9 weeks'::interval, 'YYYYMMDD'), + 'Check time_taptest_table_'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'9 weeks'::interval, 'YYYYMMDD')||' does not exist (-9 weeks)'); + +SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP), 'YYYYMMDD'), ARRAY['col1'], + 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP), 'YYYYMMDD')); +SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)+'1 week'::interval, 'YYYYMMDD'), ARRAY['col1'], + 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)+'1 week'::interval, 'YYYYMMDD')||' (+1 weeks)'); +SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)+'2 weeks'::interval, 'YYYYMMDD'), ARRAY['col1'], + 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)+'2 weeks'::interval, 'YYYYMMDD')||' (+2 weeks)'); +SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'1 week'::interval, 'YYYYMMDD'), ARRAY['col1'], + 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'1 week'::interval, 'YYYYMMDD')||' (-1 weeks)'); +SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'2 weeks'::interval, 'YYYYMMDD'), ARRAY['col1'], + 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'2 weeks'::interval, 'YYYYMMDD')||' (-2 weeks)'); +SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'3 weeks'::interval, 'YYYYMMDD'), ARRAY['col1'], + 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'3 weeks'::interval, 'YYYYMMDD')||' (-3 weeks)'); +SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'4 weeks'::interval, 'YYYYMMDD'), ARRAY['col1'], + 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'4 weeks'::interval, 'YYYYMMDD')||' (-4 weeks)'); +SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'5 weeks'::interval, 'YYYYMMDD'), ARRAY['col1'], + 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'5 weeks'::interval, 'YYYYMMDD')||' (-5 weeks)'); +SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'6 weeks'::interval, 'YYYYMMDD'), ARRAY['col1'], + 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'6 weeks'::interval, 'YYYYMMDD')||' (-6 weeks)'); +SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'7 weeks'::interval, 'YYYYMMDD'), ARRAY['col1'], + 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'7 weeks'::interval, 'YYYYMMDD')||' (-7 weeks)'); +SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'8 weeks'::interval, 'YYYYMMDD'), ARRAY['col1'], + 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'8 weeks'::interval, 'YYYYMMDD')||' (-8 weeks)'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_default', 'Check that default table no data'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table', ARRAY[10], 'Check count from parent table'); -SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP - '8 weeks'::interval, 'YYYYMMDD'), - ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'8 weeks'::interval, 'YYYYMMDD')||' (-8 weeks)'); - -INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(11,20), (extract('epoch' from CURRENT_TIMESTAMP - '7 weeks'::interval)::bigint)*1000); -INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(21,25), (extract('epoch' from CURRENT_TIMESTAMP - '6 weeks'::interval)::bigint)*1000); -INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(26,30), (extract('epoch' from CURRENT_TIMESTAMP - '5 weeks'::interval)::bigint)*1000); -INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(31,37), (extract('epoch' from CURRENT_TIMESTAMP - '4 week'::interval)::bigint)*1000); -INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(38,49), (extract('epoch' from CURRENT_TIMESTAMP - '3 week'::interval)::bigint)*1000); -INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(50,70), (extract('epoch' from CURRENT_TIMESTAMP - '2 weeks'::interval)::bigint)*1000); -INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(71,85), (extract('epoch' from CURRENT_TIMESTAMP - '1 week'::interval)::bigint)*1000); -INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(86,100), (extract('epoch' from CURRENT_TIMESTAMP + '1 week'::interval)::bigint)*1000); -INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(101,110), (extract('epoch' from CURRENT_TIMESTAMP + '2 weeks'::interval)::bigint)*1000); +SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP) - '8 weeks'::interval, 'YYYYMMDD'), + ARRAY[10], 'Check count from time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'8 weeks'::interval, 'YYYYMMDD')||' (-8 weeks)'); + +INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(11,20), (extract('epoch' from date_trunc('week',CURRENT_TIMESTAMP) - '7 weeks'::interval)::bigint)*1000); +INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(21,25), (extract('epoch' from date_trunc('week',CURRENT_TIMESTAMP) - '6 weeks'::interval)::bigint)*1000); +INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(26,30), (extract('epoch' from date_trunc('week',CURRENT_TIMESTAMP) - '5 weeks'::interval)::bigint)*1000); +INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(31,37), (extract('epoch' from date_trunc('week',CURRENT_TIMESTAMP) - '4 week'::interval)::bigint)*1000); +INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(38,49), (extract('epoch' from date_trunc('week',CURRENT_TIMESTAMP) - '3 week'::interval)::bigint)*1000); +INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(50,70), (extract('epoch' from date_trunc('week',CURRENT_TIMESTAMP) - '2 weeks'::interval)::bigint)*1000); +INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(71,85), (extract('epoch' from date_trunc('week',CURRENT_TIMESTAMP) - '1 week'::interval)::bigint)*1000); +INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(86,100), (extract('epoch' from date_trunc('week',CURRENT_TIMESTAMP) + '1 week'::interval)::bigint)*1000); +INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(101,110), (extract('epoch' from date_trunc('week',CURRENT_TIMESTAMP) + '2 weeks'::interval)::bigint)*1000); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table', 'Check that parent table has had no data inserted to it'); -SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'YYYYMMDD'), - ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'YYYYMMDD')||' (-7 weeks)'); -SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'YYYYMMDD'), - ARRAY[5], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'YYYYMMDD')||' (-6 weeks)'); -SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'YYYYMMDD'), - ARRAY[5], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'YYYYMMDD')||' (-5 weeks)'); -SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'YYYYMMDD'), - ARRAY[7], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'YYYYMMDD')||' (-4 weeks)'); -SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'YYYYMMDD'), - ARRAY[12], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'YYYYMMDD')||' (-3 weeks)'); -SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'YYYYMMDD'), - ARRAY[21], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'YYYYMMDD')||' (-2 weeks)'); -SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'YYYYMMDD'), - ARRAY[15], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'YYYYMMDD')||' (-1 weeks)'); -SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'YYYYMMDD'), - ARRAY[15], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'YYYYMMDD')||' (+1 weeks)'); +SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'7 weeks'::interval, 'YYYYMMDD'), + ARRAY[10], 'Check count from time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'7 weeks'::interval, 'YYYYMMDD')||' (-7 weeks)'); +SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'6 weeks'::interval, 'YYYYMMDD'), + ARRAY[5], 'Check count from time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'6 weeks'::interval, 'YYYYMMDD')||' (-6 weeks)'); +SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'5 weeks'::interval, 'YYYYMMDD'), + ARRAY[5], 'Check count from time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'5 weeks'::interval, 'YYYYMMDD')||' (-5 weeks)'); +SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'4 weeks'::interval, 'YYYYMMDD'), + ARRAY[7], 'Check count from time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'4 weeks'::interval, 'YYYYMMDD')||' (-4 weeks)'); +SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'3 weeks'::interval, 'YYYYMMDD'), + ARRAY[12], 'Check count from time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'3 weeks'::interval, 'YYYYMMDD')||' (-3 weeks)'); +SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'2 weeks'::interval, 'YYYYMMDD'), + ARRAY[21], 'Check count from time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'2 weeks'::interval, 'YYYYMMDD')||' (-2 weeks)'); +SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'1 week'::interval, 'YYYYMMDD'), + ARRAY[15], 'Check count from time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'1 week'::interval, 'YYYYMMDD')||' (-1 weeks)'); +SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)+'1 week'::interval, 'YYYYMMDD'), + ARRAY[15], 'Check count from time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)+'1 week'::interval, 'YYYYMMDD')||' (+1 weeks)'); -- Default optimize_constraint is 30, so set it to a value that will trigger it to work for given conditions of this partition set UPDATE part_config SET premake = 3, optimize_constraint = 5 WHERE parent_table = 'partman_test.time_taptest_table'; @@ -125,91 +124,90 @@ SELECT run_maintenance(); -- Automatic run of apply_constraints due to run_maintenance will put constraint on "now() - 4 weeks" partition with an optimize_constraint value of 5 -- This is due to values "now() + 2 weeks" being inserted above. -- Ex: Current week is 39. +2 weeks is 41. Going back 5 weeks is 36, but constraint is applied to the table OLDER than that value. Hence constraint will be on week 35 -SELECT col_has_check('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'YYYYMMDD'), 'col1' - , 'Check for additional constraint on col1 on time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'YYYYMMDD')||' (-4 weeks)'); +SELECT col_has_check('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'4 weeks'::interval, 'YYYYMMDD'), 'col1' + , 'Check for additional constraint on col1 on time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'4 weeks'::interval, 'YYYYMMDD')||' (-4 weeks)'); -- Must run apply_constraints() to manually set the other older constraints -SELECT apply_constraints('partman_test.time_taptest_table', 'partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'8 weeks'::interval, 'YYYYMMDD')); -SELECT apply_constraints('partman_test.time_taptest_table', 'partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'YYYYMMDD')); -SELECT apply_constraints('partman_test.time_taptest_table', 'partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'YYYYMMDD')); -SELECT apply_constraints('partman_test.time_taptest_table', 'partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'YYYYMMDD')); -SELECT col_has_check('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'8 weeks'::interval, 'YYYYMMDD'), 'col1' - , 'Check for additional constraint on col1 on time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'8 weeks'::interval, 'YYYYMMDD')); -SELECT col_has_check('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'YYYYMMDD'), 'col1' - , 'Check for additional constraint on col1 on time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'YYYYMMDD')); -SELECT col_has_check('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'YYYYMMDD'), 'col1' - , 'Check for additional constraint on col1 on time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'YYYYMMDD')); -SELECT col_has_check('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'YYYYMMDD'), 'col1' - , 'Check for additional constraint on col1 on time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'YYYYMMDD')); - -INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(111,120), (extract('epoch' from CURRENT_TIMESTAMP + '3 weeks'::interval)::bigint)*1000); - -SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'YYYYMMDD')||' exists (+3 weeks'); +SELECT apply_constraints('partman_test.time_taptest_table', 'partman_test.time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'8 weeks'::interval, 'YYYYMMDD')); +SELECT apply_constraints('partman_test.time_taptest_table', 'partman_test.time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'7 weeks'::interval, 'YYYYMMDD')); +SELECT apply_constraints('partman_test.time_taptest_table', 'partman_test.time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'6 weeks'::interval, 'YYYYMMDD')); +SELECT apply_constraints('partman_test.time_taptest_table', 'partman_test.time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'5 weeks'::interval, 'YYYYMMDD')); +SELECT col_has_check('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'8 weeks'::interval, 'YYYYMMDD'), 'col1' + , 'Check for additional constraint on col1 on time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'8 weeks'::interval, 'YYYYMMDD')); +SELECT col_has_check('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'7 weeks'::interval, 'YYYYMMDD'), 'col1' + , 'Check for additional constraint on col1 on time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'7 weeks'::interval, 'YYYYMMDD')); +SELECT col_has_check('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'6 weeks'::interval, 'YYYYMMDD'), 'col1' + , 'Check for additional constraint on col1 on time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'6 weeks'::interval, 'YYYYMMDD')); +SELECT col_has_check('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'5 weeks'::interval, 'YYYYMMDD'), 'col1' + , 'Check for additional constraint on col1 on time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'5 weeks'::interval, 'YYYYMMDD')); + +INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(111,120), (extract('epoch' from date_trunc('week',CURRENT_TIMESTAMP) + '3 weeks'::interval)::bigint)*1000); + +SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)+'3 weeks'::interval, 'YYYYMMDD'), + 'Check time_taptest_table_'||to_char(date_trunc('week',CURRENT_TIMESTAMP)+'3 weeks'::interval, 'YYYYMMDD')||' exists (+3 weeks'); -- Cannot test for next week not existing. Different lengths of months will sometimes cause an extra partition. -SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'YYYYMMDD'), ARRAY['col1'], - 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'YYYYMMDD')||' (+3 weeks)'); +SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)+'3 weeks'::interval, 'YYYYMMDD'), ARRAY['col1'], + 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)+'3 weeks'::interval, 'YYYYMMDD')||' (+3 weeks)'); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table', 'Check that parent table has had no data inserted to it'); -SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'YYYYMMDD'), - ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'YYYYMMDD')||' (+3 weeks)'); +SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)+'3 weeks'::interval, 'YYYYMMDD'), + ARRAY[10], 'Check count from time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)+'3 weeks'::interval, 'YYYYMMDD')||' (+3 weeks)'); UPDATE part_config SET premake = 4 WHERE parent_table = 'partman_test.time_taptest_table'; SELECT run_maintenance(); -INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(121,130), (extract('epoch' from CURRENT_TIMESTAMP + '4 weeks'::interval)::bigint)*1000); +INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(121,130), (extract('epoch' from date_trunc('week',CURRENT_TIMESTAMP) + '4 weeks'::interval)::bigint)*1000); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_default', 'Check that default table has had no data inserted to it'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table', ARRAY[130], 'Check count from parent table'); -SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'YYYYMMDD'), - ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'YYYYMMDD')||' (+4 weeks)'); -SELECT col_has_check('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'YYYYMMDD'), 'col1' - , 'Check for additional constraint on col1 on time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'YYYYMMDD')); +SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)+'4 weeks'::interval, 'YYYYMMDD'), + ARRAY[10], 'Check count from time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)+'4 weeks'::interval, 'YYYYMMDD')||' (+4 weeks)'); +SELECT col_has_check('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'3 weeks'::interval, 'YYYYMMDD'), 'col1' + , 'Check for additional constraint on col1 on time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'3 weeks'::interval, 'YYYYMMDD')); -SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'YYYYMMDD')||' exists (+4 weeks)'); -SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'YYYYMMDD'), ARRAY['col1'], - 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'YYYYMMDD')||' (+4 weeks)'); +SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)+'4 weeks'::interval, 'YYYYMMDD'), + 'Check time_taptest_table_'||to_char(date_trunc('week',CURRENT_TIMESTAMP)+'4 weeks'::interval, 'YYYYMMDD')||' exists (+4 weeks)'); +SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)+'4 weeks'::interval, 'YYYYMMDD'), ARRAY['col1'], + 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)+'4 weeks'::interval, 'YYYYMMDD')||' (+4 weeks)'); -INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(200,210), (extract('epoch' from CURRENT_TIMESTAMP + '20 weeks'::interval)::bigint)*1000); +INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(200,210), (extract('epoch' from date_trunc('week',CURRENT_TIMESTAMP) + '20 weeks'::interval)::bigint)*1000); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_default', ARRAY[11], 'Check that data outside scope goes to default'); SELECT drop_partition_time('partman_test.time_taptest_table', '3 weeks', p_keep_table := false); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'YYYYMMDD')||' does not exist (-4 weeks)'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'YYYYMMDD')||' does not exist (-5 weeks)'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'YYYYMMDD')||' does not exist (-6 weeks)'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'YYYYMMDD')||' does not exist (-7 weeks)'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'8 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'8 weeks'::interval, 'YYYYMMDD')||' does not exist (-8 weeks)'); +SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'4 weeks'::interval, 'YYYYMMDD'), + 'Check time_taptest_table_'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'4 weeks'::interval, 'YYYYMMDD')||' does not exist (-4 weeks)'); +SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'5 weeks'::interval, 'YYYYMMDD'), + 'Check time_taptest_table_'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'5 weeks'::interval, 'YYYYMMDD')||' does not exist (-5 weeks)'); +SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'6 weeks'::interval, 'YYYYMMDD'), + 'Check time_taptest_table_'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'6 weeks'::interval, 'YYYYMMDD')||' does not exist (-6 weeks)'); +SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'7 weeks'::interval, 'YYYYMMDD'), + 'Check time_taptest_table_'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'7 weeks'::interval, 'YYYYMMDD')||' does not exist (-7 weeks)'); +SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'8 weeks'::interval, 'YYYYMMDD'), + 'Check time_taptest_table_'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'8 weeks'::interval, 'YYYYMMDD')||' does not exist (-8 weeks)'); UPDATE part_config SET retention = '2 weeks'::interval WHERE parent_table = 'partman_test.time_taptest_table'; SELECT drop_partition_time('partman_test.time_taptest_table', p_retention_schema := 'partman_retention_test'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'YYYYMMDD')||' does not exist (-3 weeks)'); -SELECT has_table('partman_retention_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'YYYYMMDD')||' got moved to new schema (-3 weeks)'); +SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'3 weeks'::interval, 'YYYYMMDD'), + 'Check time_taptest_table_'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'3 weeks'::interval, 'YYYYMMDD')||' does not exist (-3 weeks)'); +SELECT has_table('partman_retention_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'3 weeks'::interval, 'YYYYMMDD'), + 'Check time_taptest_table_'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'3 weeks'::interval, 'YYYYMMDD')||' got moved to new schema (-3 weeks)'); SELECT undo_partition('partman_test.time_taptest_table', p_loop_count => 20, p_keep_table => false, p_target_table => 'partman_test.undo_taptest'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.undo_taptest', ARRAY[92], 'Check count from undo table after undo'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table', 'Check that parent table no data'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP, 'YYYYMMDD')||' does not exist (now)'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'YYYYMMDD')||' does not exist (+1 weeks)'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'YYYYMMDD')||' does not exist (+2 weeks)'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'YYYYMMDD')||' does not exist (+3 weeks)'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'YYYYMMDD')||' does not exist (+4 weeks)'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'YYYYMMDD')||' does not exist (-1 weeks)'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'YYYYMMDD')||' does not exist (-2 weeks)'); +SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP), 'YYYYMMDD'), + 'Check time_taptest_table_'||to_char(date_trunc('week',CURRENT_TIMESTAMP), 'YYYYMMDD')||' does not exist (now)'); +SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)+'1 week'::interval, 'YYYYMMDD'), + 'Check time_taptest_table_'||to_char(date_trunc('week',CURRENT_TIMESTAMP)+'1 week'::interval, 'YYYYMMDD')||' does not exist (+1 weeks)'); +SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)+'2 weeks'::interval, 'YYYYMMDD'), + 'Check time_taptest_table_'||to_char(date_trunc('week',CURRENT_TIMESTAMP)+'2 weeks'::interval, 'YYYYMMDD')||' does not exist (+2 weeks)'); +SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)+'3 weeks'::interval, 'YYYYMMDD'), + 'Check time_taptest_table_'||to_char(date_trunc('week',CURRENT_TIMESTAMP)+'3 weeks'::interval, 'YYYYMMDD')||' does not exist (+3 weeks)'); +SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)+'4 weeks'::interval, 'YYYYMMDD'), + 'Check time_taptest_table_'||to_char(date_trunc('week',CURRENT_TIMESTAMP)+'4 weeks'::interval, 'YYYYMMDD')||' does not exist (+4 weeks)'); +SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'1 week'::interval, 'YYYYMMDD'), + 'Check time_taptest_table_'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'1 week'::interval, 'YYYYMMDD')||' does not exist (-1 weeks)'); +SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'2 weeks'::interval, 'YYYYMMDD'), + 'Check time_taptest_table_'||to_char(date_trunc('week',CURRENT_TIMESTAMP)-'2 weeks'::interval, 'YYYYMMDD')||' does not exist (-2 weeks)'); SELECT * FROM finish(); ROLLBACK; - diff --git a/test/test-time-epoch-weekly-native.sql b/test/test-time-epoch-weekly-native.sql deleted file mode 100644 index 509aeb0a..00000000 --- a/test/test-time-epoch-weekly-native.sql +++ /dev/null @@ -1,199 +0,0 @@ --- ########## NATIVE TIME WEEKLY EPOCH TESTS ########## --- Other tests: combination of start_partition & constraint_cols/optimize_constraint. Requires manually running apply_constraint to set other old partitions - -\set ON_ERROR_ROLLBACK 1 -\set ON_ERROR_STOP true - -BEGIN; -SELECT set_config('search_path','partman, public',false); - -SELECT plan(58); -CREATE SCHEMA partman_test; -CREATE SCHEMA partman_retention_test; - -CREATE TABLE partman_test.time_taptest_table ( - col1 int, - col2 text, - col3 int NOT NULL 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); - -SELECT create_parent( - 'partman_test.time_taptest_table' - , 'col3' - , '1 week' - , p_constraint_cols => '{"col1"}' - , p_epoch := 'seconds' - , p_premake := 2 - , p_start_partition := to_char(CURRENT_TIMESTAMP-'8 weeks'::interval, 'YYYY-MM-DD HH24:MI:SS') -); - -SELECT is_partitioned('partman_test', 'time_taptest_table', 'Check that time_taptest_table is natively partitioned'); - -INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(1,10), extract('epoch' from CURRENT_TIMESTAMP - '8 weeks'::interval)::int); - -SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYYMMDD'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP, 'YYYYMMDD')||' exists'); -SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'YYYYMMDD')||' exists (+1 weeks)'); -SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'YYYYMMDD')||' exists (+2 weeks)'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'YYYYMMDD')||' does not exist (+3 weeks)'); -SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'YYYYMMDD')||' exists (-1 weeks)'); -SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'YYYYMMDD')||' exists (-2 weeks)'); -SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'YYYYMMDD')||' exists (-3 weeks)'); -SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'YYYYMMDD')||' exists (-4 weeks)'); -SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'YYYYMMDD')||' exists (-5 weeks)'); -SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'YYYYMMDD')||' exists (-6 weeks)'); -SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'YYYYMMDD')||' exists (-7 weeks)'); -SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'8 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'8 weeks'::interval, 'YYYYMMDD')||' exists (-8 weeks)'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'9 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'9 weeks'::interval, 'YYYYMMDD')||' does not exist (-9 weeks)'); - -SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_default', 'Check that default table has no data'); -SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table', ARRAY[10], 'Check count from parent table'); -SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP - '8 weeks'::interval, 'YYYYMMDD'), - ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'8 weeks'::interval, 'YYYYMMDD')||' (-8 weeks)'); - -INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(11,20), extract('epoch' from CURRENT_TIMESTAMP - '7 weeks'::interval)::int); -INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(21,25), extract('epoch' from CURRENT_TIMESTAMP - '6 weeks'::interval)::int); -INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(26,30), extract('epoch' from CURRENT_TIMESTAMP - '5 weeks'::interval)::int); -INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(31,37), extract('epoch' from CURRENT_TIMESTAMP - '4 week'::interval)::int); -INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(38,49), extract('epoch' from CURRENT_TIMESTAMP - '3 week'::interval)::int); -INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(50,70), extract('epoch' from CURRENT_TIMESTAMP - '2 weeks'::interval)::int); -INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(71,85), extract('epoch' from CURRENT_TIMESTAMP - '1 week'::interval)::int); -INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(86,100), extract('epoch' from CURRENT_TIMESTAMP + '1 week'::interval)::int); -INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(101,110), extract('epoch' from CURRENT_TIMESTAMP + '2 weeks'::interval)::int); - -SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_default', 'Check that default table has had no data inserted to it'); -SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'YYYYMMDD'), - ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'YYYYMMDD')||' (-7 weeks)'); -SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'YYYYMMDD'), - ARRAY[5], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'YYYYMMDD')||' (-6 weeks)'); -SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'YYYYMMDD'), - ARRAY[5], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'YYYYMMDD')||' (-5 weeks)'); -SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'YYYYMMDD'), - ARRAY[7], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'YYYYMMDD')||' (-4 weeks)'); -SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'YYYYMMDD'), - ARRAY[12], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'YYYYMMDD')||' (-3 weeks)'); -SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'YYYYMMDD'), - ARRAY[21], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'YYYYMMDD')||' (-2 weeks)'); -SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'YYYYMMDD'), - ARRAY[15], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'YYYYMMDD')||' (-1 weeks)'); -SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'YYYYMMDD'), - ARRAY[15], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'YYYYMMDD')||' (+1 weeks)'); - --- Default optimize_constraint is 30, so set it to a value that will trigger it to work for given conditions of this partition set -UPDATE part_config SET premake = 3, optimize_constraint = 5 WHERE parent_table = 'partman_test.time_taptest_table'; -SELECT run_maintenance(); - --- Automatic run of apply_constraints due to run_maintenance will put constraint on "now() - 4 weeks" partition with an optimize_constraint value of 5 --- This is due to values "now() + 2 weeks" being inserted above. --- Ex: Current week is 39. +2 weeks is 41. Going back 5 weeks is 36, but constraint is applied to the table OLDER than that value. Hence constraint will be on week 35 -SELECT col_has_check('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'YYYYMMDD'), 'col1' - , 'Check for additional constraint on col1 on time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'YYYYMMDD')||' (-4 weeks)'); --- Must run apply_constraints() to manually set the other older constraints - -SELECT apply_constraints('partman_test.time_taptest_table', 'partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'8 weeks'::interval, 'YYYYMMDD')); -SELECT apply_constraints('partman_test.time_taptest_table', 'partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'YYYYMMDD')); -SELECT apply_constraints('partman_test.time_taptest_table', 'partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'YYYYMMDD')); -SELECT apply_constraints('partman_test.time_taptest_table', 'partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'YYYYMMDD')); -SELECT col_has_check('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'8 weeks'::interval, 'YYYYMMDD'), 'col1' - , 'Check for additional constraint on col1 on time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'8 weeks'::interval, 'YYYYMMDD')); -SELECT col_has_check('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'YYYYMMDD'), 'col1' - , 'Check for additional constraint on col1 on time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'YYYYMMDD')); -SELECT col_has_check('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'YYYYMMDD'), 'col1' - , 'Check for additional constraint on col1 on time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'YYYYMMDD')); -SELECT col_has_check('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'YYYYMMDD'), 'col1' - , 'Check for additional constraint on col1 on time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'YYYYMMDD')); - -INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(111,120), extract('epoch' from CURRENT_TIMESTAMP + '3 weeks'::interval)::int); - -SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'YYYYMMDD')||' exists (+3 weeks'); --- TODO test if this is still true --- Cannot test for next week not existing. Different lengths of months will sometimes cause an extra partition. - - -SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_default', 'Check that default table has had no data inserted to it'); -SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'YYYYMMDD'), - ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'YYYYMMDD')||' (+3 weeks)'); - -UPDATE part_config SET premake = 4 WHERE parent_table = 'partman_test.time_taptest_table'; -SELECT run_maintenance(); -INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(121,130), extract('epoch' from CURRENT_TIMESTAMP + '4 weeks'::interval)::int); - -SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table_default', 'Check that default table has had no data inserted to it'); -SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table', ARRAY[130], 'Check count from parent table'); -SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'YYYYMMDD'), - ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'YYYYMMDD')||' (+4 weeks)'); -SELECT col_has_check('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'YYYYMMDD'), 'col1' - , 'Check for additional constraint on col1 on time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'YYYYMMDD')); - -SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'YYYYMMDD')||' exists (+4 weeks)'); --- TODO test if this is still true --- Cannot test for next week not existing. Different lengths of months will sometimes cause an extra partition. - -INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(200,210), extract('epoch' from CURRENT_TIMESTAMP + '20 weeks'::interval)::int); -SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_default', ARRAY[11], 'Check that data outside trigger scope goes to parent'); - -SELECT drop_partition_time('partman_test.time_taptest_table', '3 weeks', p_keep_table := false); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'YYYYMMDD')||' does not exist (-4 weeks)'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'YYYYMMDD')||' does not exist (-5 weeks)'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'YYYYMMDD')||' does not exist (-6 weeks)'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'YYYYMMDD')||' does not exist (-7 weeks)'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'8 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'8 weeks'::interval, 'YYYYMMDD')||' does not exist (-8 weeks)'); - -UPDATE part_config SET retention = '2 weeks'::interval WHERE parent_table = 'partman_test.time_taptest_table'; -SELECT drop_partition_time('partman_test.time_taptest_table', p_retention_schema := 'partman_retention_test'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'YYYYMMDD')||' does not exist (-3 weeks)'); -SELECT has_table('partman_retention_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'YYYYMMDD')||' got moved to new schema (-3 weeks)'); - -UPDATE part_config SET retention = '10 days'::interval WHERE parent_table = 'partman_test.time_taptest_table'; -SELECT child_start_time AS two_week_old_partition_start_time FROM show_partition_info('partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'YYYYMMDD')) -\gset -SELECT drop_partition_time('partman_test.time_taptest_table', p_retention_schema := 'partman_retention_test', p_reference_timestamp := (:'two_week_old_partition_start_time')::timestamp+'17 days'::interval); -SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'YYYYMMDD')||' exists after 17 days (retention + partition size)'); -SELECT drop_partition_time('partman_test.time_taptest_table', p_retention_schema := 'partman_retention_test', p_reference_timestamp := (:'two_week_old_partition_start_time')::timestamp+'18 days'::interval); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'YYYYMMDD')||' does not exist after 18 days (1 day past retention + partition size)'); -SELECT has_table('partman_retention_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'YYYYMMDD')||' got moved to new schema after 18 days (1 day past retention + partition size)'); - -SELECT undo_partition('partman_test.time_taptest_table', p_loop_count => 20, p_target_table := 'partman_test.undo_taptest', p_keep_table := false); -SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.undo_taptest', ARRAY[71], 'Check count from target table after undo'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP, 'YYYYMMDD')||' does not exist (now)'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'YYYYMMDD')||' does not exist (+1 weeks)'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'YYYYMMDD')||' does not exist (+2 weeks)'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'YYYYMMDD')||' does not exist (+3 weeks)'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'YYYYMMDD')||' does not exist (+4 weeks)'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'YYYYMMDD')||' does not exist (-1 weeks)'); -SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'YYYYMMDD'), - 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'YYYYMMDD')||' does not exist (-2 weeks)'); - -SELECT * FROM finish(); -ROLLBACK; - diff --git a/updates/pg_partman--4.7.4--5.0.0-beta.sql b/updates/pg_partman--4.7.4--5.0.0.sql similarity index 99% rename from updates/pg_partman--4.7.4--5.0.0-beta.sql rename to updates/pg_partman--4.7.4--5.0.0.sql index d122f3e2..66d3adb2 100644 --- a/updates/pg_partman--4.7.4--5.0.0-beta.sql +++ b/updates/pg_partman--4.7.4--5.0.0.sql @@ -181,7 +181,6 @@ CREATE TABLE @extschema@.part_config ( , undo_in_progress boolean NOT NULL DEFAULT false , inherit_privileges boolean DEFAULT false , constraint_valid boolean DEFAULT true NOT NULL - , subscription_refresh text , ignore_default_data boolean NOT NULL DEFAULT true , default_table boolean DEFAULT true , date_trunc_interval text @@ -214,7 +213,6 @@ INSERT INTO @extschema@.part_config ( , undo_in_progress , inherit_privileges , constraint_valid - , subscription_refresh , ignore_default_data ) SELECT @@ -239,7 +237,6 @@ SELECT , undo_in_progress , inherit_privileges , constraint_valid - , subscription_refresh , ignore_default_data FROM @extschema@.part_config_pre_500_data; @@ -263,7 +260,6 @@ CREATE TABLE @extschema@.part_config_sub ( , sub_jobmon boolean NOT NULL DEFAULT true , sub_inherit_privileges boolean DEFAULT false , sub_constraint_valid boolean DEFAULT true NOT NULL - , sub_subscription_refresh text , sub_ignore_default_data boolean NOT NULL DEFAULT true , sub_default_table boolean default true , sub_date_trunc_interval TEXT @@ -292,7 +288,6 @@ INSERT INTO @extschema@.part_config_sub ( , sub_jobmon , sub_inherit_privileges , sub_constraint_valid - , sub_subscription_refresh , sub_date_trunc_interval , sub_ignore_default_data ) @@ -315,7 +310,6 @@ SELECT , sub_jobmon , sub_inherit_privileges , sub_constraint_valid - , sub_subscription_refresh , sub_date_trunc_interval , sub_ignore_default_data FROM @extschema@.part_config_sub_pre_500_data; @@ -843,7 +837,6 @@ CREATE FUNCTION @extschema@.check_subpart_sameconfig(p_parent_table text) , sub_jobmon boolean , sub_inherit_privileges boolean , sub_constraint_valid boolean - , sub_subscription_refresh text , sub_date_trunc_interval text , sub_ignore_default_data boolean , sub_default_table boolean @@ -894,7 +887,6 @@ AS $$ , a.sub_jobmon , a.sub_inherit_privileges , a.sub_constraint_valid - , a.sub_subscription_refresh , a.sub_date_trunc_interval , a.sub_ignore_default_data , a.sub_default_table @@ -1158,7 +1150,6 @@ FOR v_row IN , a.sub_jobmon , a.sub_inherit_privileges , a.sub_constraint_valid - , a.sub_subscription_refresh , a.sub_date_trunc_interval , a.sub_ignore_default_data , a.sub_default_table @@ -1184,7 +1175,6 @@ LOOP , sub_template_table , sub_inherit_privileges , sub_constraint_valid - , sub_subscription_refresh , sub_date_trunc_interval , sub_ignore_default_data) VALUES ( @@ -1206,7 +1196,6 @@ LOOP , v_row.sub_template_table , v_row.sub_inherit_privileges , v_row.sub_constraint_valid - , v_row.sub_subscription_refresh , v_row.sub_date_trunc_interval , v_row.sub_ignore_default_data); @@ -1742,7 +1731,6 @@ FOREACH v_id IN ARRAY p_partition_ids LOOP , sub_jobmon , sub_inherit_privileges , sub_constraint_valid - , sub_subscription_refresh , sub_date_trunc_interval , sub_ignore_default_data , sub_default_table @@ -1789,7 +1777,6 @@ FOREACH v_id IN ARRAY p_partition_ids LOOP , infinite_time_partitions = v_row.sub_infinite_time_partitions , inherit_privileges = v_row.sub_inherit_privileges , constraint_valid = v_row.sub_constraint_valid - , subscription_refresh = v_row.sub_subscription_refresh , ignore_default_data = v_row.sub_ignore_default_data WHERE parent_table = v_parent_schema||'.'||v_partition_name; @@ -2139,7 +2126,6 @@ FOREACH v_time IN ARRAY p_partition_times LOOP , sub_jobmon , sub_inherit_privileges , sub_constraint_valid - , sub_subscription_refresh , sub_date_trunc_interval , sub_ignore_default_data , sub_default_table @@ -2187,7 +2173,6 @@ FOREACH v_time IN ARRAY p_partition_times LOOP , infinite_time_partitions = v_row.sub_infinite_time_partitions , inherit_privileges = v_row.sub_inherit_privileges , constraint_valid = v_row.sub_constraint_valid - , subscription_refresh = v_row.sub_subscription_refresh , ignore_default_data = v_row.sub_ignore_default_data WHERE parent_table = v_parent_schema||'.'||v_partition_name; @@ -3083,7 +3068,6 @@ DECLARE v_template_table text; v_inherit_privileges boolean; -- DEFAULT false v_constraint_valid boolean; -- DEFAULT true NOT NULL - v_subscription_refresh TEXT; v_ignore_default_data boolean; -- DEFAULT false NOT NULL v_date_trunc_interval text; v_default_table boolean ; @@ -3109,7 +3093,6 @@ BEGIN pc.template_table, pc.inherit_privileges, pc.constraint_valid, - pc.subscription_refresh, pc.ignore_default_data, pc.date_trunc_interval, pc.default_table @@ -3134,7 +3117,6 @@ BEGIN v_template_table, v_inherit_privileges, v_constraint_valid, - v_subscription_refresh, v_ignore_default_data, v_date_trunc_interval, v_default_table @@ -3190,7 +3172,6 @@ E'UPDATE @extschema@.part_config SET \tsub_partition_set_full = %L, \tinherit_privileges = %L, \tconstraint_valid = %L, -\tsubscription_refresh = %L, \tignore_default_data = %L WHERE parent_table = %L;', v_optimize_constraint, @@ -3203,7 +3184,6 @@ WHERE parent_table = %L;', v_sub_partition_set_full, v_inherit_privileges, v_constraint_valid, - v_subscription_refresh, v_ignore_default_data, v_parent_table ); @@ -3990,7 +3970,7 @@ $$; CREATE FUNCTION @extschema@.run_maintenance( p_parent_table text DEFAULT NULL - -- If these default changed reflect them in `run_maintenance_proc`! + -- If these defaults change reflect them in `run_maintenance_proc`! , p_analyze boolean DEFAULT false , p_jobmon boolean DEFAULT true ) @@ -4041,7 +4021,6 @@ v_sub_id_max bigint; v_sub_id_max_suffix bigint; v_sub_id_min bigint; v_sub_parent text; -v_sub_refresh_done text[]; v_sub_timestamp_max timestamptz; v_sub_timestamp_max_suffix timestamptz; v_sub_timestamp_min timestamptz; @@ -4091,7 +4070,6 @@ v_tables_list_sql := 'SELECT parent_table , epoch , infinite_time_partitions , retention - , subscription_refresh , ignore_default_data , datetime_string FROM @extschema@.part_config @@ -4344,20 +4322,6 @@ LOOP END IF; -- end main IF check for time or id - -- Refresh subscriptions in order to catch new tables that may have been created in the publication - -- Keep track of which ones have been refreshed so it doesn't needlessly run more than once - -- in a single maintenance run - IF v_row.subscription_refresh IS NOT NULL THEN - IF v_sub_refresh_done @> ARRAY[v_row.subscription_refresh] THEN - CONTINUE; - ELSE - v_sql := format('ALTER SUBSCRIPTION %I REFRESH PUBLICATION', v_row.subscription_refresh); - RAISE DEBUG '%', v_sql; - EXECUTE v_sql; - v_sub_refresh_done := array_append(v_sub_refresh_done, v_row.subscription_refresh); - END IF; - END IF; - IF v_analyze AND p_analyze THEN IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, format('Analyzing partition set: %s', v_row.parent_table));