pax_global_header00006660000000000000000000000064126214662170014521gustar00rootroot0000000000000052 comment=c4578a9f24f8be45f1c6fd8a4cc2c8ced0ad91a1 pg_partman-2.2.2/000077500000000000000000000000001262146621700136545ustar00rootroot00000000000000pg_partman-2.2.2/.gitignore000066400000000000000000000000461262146621700156440ustar00rootroot00000000000000ignore/* *.swp *.swo .deps/* *.o *.so pg_partman-2.2.2/CHANGELOG.txt000066400000000000000000001512161262146621700157120ustar00rootroot000000000000002.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 partiton 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 partitons 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 partiton 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 partitoning 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. pg_partman-2.2.2/META.json000066400000000000000000000025431262146621700153010ustar00rootroot00000000000000{ "name": "pg_partman", "abstract": "Extension to manage partitioned tables by time or ID", "version": "2.2.2", "maintainer": [ "Keith Fiske " ], "license": "postgresql", "generated_by": "Keith Fiske", "release_status": "stable", "prereqs": { "runtime": { "requires": { "PostgreSQL": "9.4.0" }, "recommends": { "pg_jobmon": "1.3.0" } } }, "provides": { "pg_partman": { "file": "sql/pg_partman--2.2.2.sql", "docfile": "doc/pg_partman.md", "version": "2.2.2", "abstract": "Extension to manage partitioned tables by time or ID" } }, "resources": { "bugtracker": { "web": "https://github.com/keithf4/pg_partman/issues" }, "repository": { "url": "git://github.com/keithf4/pg_partman.git" , "web": "https://github.com/keithf4/pg_partman", "type": "git" } }, "meta-spec": { "version": "1.0.0", "url": "http://pgxn.org/meta/spec.txt" }, "tags": [ "partition", "partitions", "partitioning", "table", "tables", "bgw", "background worker", "custom background worker" ] } pg_partman-2.2.2/Makefile000066400000000000000000000016001262146621700153110ustar00rootroot00000000000000EXTENSION = pg_partman EXTVERSION = $(shell grep default_version $(EXTENSION).control | \ sed -e "s/default_version[[:space:]]*=[[:space:]]*'\([^']*\)'/\1/") PG_CONFIG = pg_config PG94 = $(shell $(PG_CONFIG) --version | egrep " 8\.| 9\.0| 9\.1| 9\.2| 9\.3" > /dev/null && echo no || echo yes) ifeq ($(PG94),yes) DOCS = $(wildcard doc/*.md) SCRIPTS = bin/*.py MODULES = src/pg_partman_bgw # If user does not want the background worker, run: make NO_BGW=1 ifneq ($(NO_BGW),) MODULES= endif all: sql/$(EXTENSION)--$(EXTVERSION).sql sql/$(EXTENSION)--$(EXTVERSION).sql: sql/types/*.sql sql/tables/*.sql sql/functions/*.sql cat $^ > $@ DATA = $(wildcard updates/*--*.sql) sql/$(EXTENSION)--$(EXTVERSION).sql EXTRA_CLEAN = sql/$(EXTENSION)--$(EXTVERSION).sql else $(error Minimum version of PostgreSQL required is 9.4.0) endif PGXS := $(shell $(PG_CONFIG) --pgxs) include $(PGXS) pg_partman-2.2.2/README.md000066400000000000000000000157601262146621700151440ustar00rootroot00000000000000[![PGXN version](https://badge.fury.io/pg/pg_partman.svg)](https://badge.fury.io/pg/pg_partman) PG Partition Manager ==================== pg_partman is an extension to create and manage both time-based and serial-based table partition sets. Sub-partitoning is also supported. Child table & trigger function creation is all managed by the extension itself. Tables with existing data can also have their data partitioned in easily managed smaller batches. Optional retention policy can automatically drop partitions no longer needed. A background worker (BGW) process is included to automatically run partition maintenance without the need of an external scheduler (cron, etc) in most cases. All bug reports, feature requests and general questions can be directed to the Issues section on Github. Please feel free to post here no matter how minor you may feel your issue or question may be. - https://github.com/keithf4/pg_partman/issues If you're looking for a partitioning system that handles any range type beyond just time & serial, check out https://github.com/moat/range_partitioning. Note that if you are doing time/serial, the methods used in pg_partman are much more efficient and will provide better performance. But if you need greater flexibility, the range_partitioning extension should well work for you. INSTALLATION ------------ Requirement: PostgreSQL 9.4 or greater Recommended: pg_jobmon (>=v1.3.0). PG Job Monitor will automatically be used if it is installed and setup properly. https://github.com/omniti-labs/pg_jobmon In the directory where you downloaded pg_partman, run make install If you do not want the background worker compiled and just want the plain PL/PGSQL functions, you can run this instead: make NO_BGW=1 install The background worker must be loaded on database start by adding the library to shared_preload_libraries in postgresql.conf shared_preload_libraries = 'pg_partman_bgw' # (change requires restart) You can also set other control variables for the BGW in postgresql.conf. "dbname" is required at a minimum for maintenance to run on the given database(s). These can be added/changed at anytime with a simple reload. See the documentation for more details. An example with some of them: pg_partman_bgw.interval = 3600 pg_partman_bgw.role = 'keith' pg_partman_bgw.dbname = 'keith' Log into PostgreSQL and run the following commands. Schema is optional (but recommended) and can be whatever you wish, but it cannot be changed after installation. If you're using the BGW, the database cluster can be safely started without having the extension first created in the configured database(s). You can create the extension at any time and the BGW will automatically pick up that it exists without restarting the cluster (as long as shared_preload_libraries was set) and begin running maintenance as configured. CREATE SCHEMA partman; CREATE EXTENSION pg_partman SCHEMA partman; Functions must either be run as a superuser or you can set the ownership of the extension functions to a superuser role and they will also work (SECURITY DEFINER is set). The 1.8.x branch is still available on github if you have PostgreSQL versions 9.1 - 9.3. You will have to install the 1.8.7 tagged release located here: https://github.com/keithf4/pg_partman/releases/tag/v1.8.7 then check the "updates" folder in the latest 2.x.x release to see if there have been any updates to the 1.8.x series since then and apply them. Only bug fixes are being applied to the 1.8.x series and only while versions of PostgreSQL prior to 9.4 are still officially supported themselves. All new development is being done on 2.x.x, so it's advised that you update your PostgreSQL cluster to make managing this extension easier. UPGRADE ------- Run "make install" same as above to put the script files and libraries in place. Then run the following in PostgreSQL itself: ALTER EXTENSION pg_partman UPDATE TO ''; If upgrading from 1.x to 2.x, please see the CHANGELOG or the notes in the update script itself for additional instructions for updating your trigger functions to the newer version and other important considerations for the update. EXAMPLE ------- First create a parent table with an appropriate column type for the partitioning type you will do. Apply all defaults, indexes, constraints, privileges & ownership to the parent table and they will be inherited to newly created child tables automatically (not already existing partitions, see docs for how to fix that). Here's one with columns that can be used for either CREATE schema test; CREATE TABLE test.part_test (col1 serial, col2 text, col3 timestamptz NOT NULL DEFAULT now()); Then just run the create_parent() function with the appropriate parameters SELECT partman.create_parent('test.part_test', 'col3', 'time', 'daily'); or SELECT partman.create_parent('test.part_test', 'col1', 'id', '100000'); This will turn your table into a parent table and premake 4 future partitions and also make 4 past partitions. To make new partitions for time-based partitioning, use the run_maintenance() function. Ideally, you'd run this as a cronjob to keep new partitions premade in preparation of new data. Serial based partitioning does not always require run_maintenance() (see doc file below). This should be enough to get you started. Please see the [pg_partman.md file](doc/pg_partman.md) in the doc folder for more information on the types of partitioning supported and what the parameters in the create_parent() function mean. TESTING ------- This extension can use the pgTAP unit testing suite to evalutate if it is working properly (http://www.pgtap.org). WARNING: You MUST increase max_locks_per_transaction above the default value of 64. For me, 128 has worked well so far. This is due to the sub-partitioning tests that create/destroy several hundred tables in a single transaction. If you don't do this, you risk a cluster crash when running subpartitioning tests. LICENSE AND COPYRIGHT --------------------- PG Partition Manager (pg_partman) is released under the PostgreSQL License, a liberal Open Source license, similar to the BSD or MIT licenses. Copyright (c) 2015 OmniTI, Inc. Permission to use, copy, modify, and distribute this software and its documentation for any purpose, without fee, and without a written agreement is hereby granted, provided that the above copyright notice and this paragraph and the following two paragraphs appear in all copies. IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE AUTHOR HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. pg_partman-2.2.2/bin/000077500000000000000000000000001262146621700144245ustar00rootroot00000000000000pg_partman-2.2.2/bin/check_unique_constraint.py000077500000000000000000000104541262146621700217140ustar00rootroot00000000000000#!/usr/bin/env python import argparse, collections, psycopg2, os, subprocess, sys, tempfile partman_version = "2.0.0" parser = argparse.ArgumentParser(description="This script is used to check that all rows in a partition set are unique for the given columns. Since unique constraints are not applied across partition sets, this cannot be enforced within the database. This script can be used as a monitor to ensure uniquness. If any unique violations are found, the values, along with a count of each, are output.") parser.add_argument('-p', '--parent', help="Parent table of the partition set to be checked") parser.add_argument('-l', '--column_list', help="Comma separated list of columns that make up the unique constraint to be checked") parser.add_argument('-c','--connection', default="host=", help="""Connection string for use by psycopg. Defaults to "host=" (local socket).""") parser.add_argument('-t', '--temp', help="Path to a writable folder that can be used for temp working files. Defaults system temp folder.") parser.add_argument('--psql', help="Full path to psql binary if not in current PATH") parser.add_argument('--simple', action="store_true", help="Output a single integer value with the total duplicate count. Use this for monitoring software that requires a simple value to be checked for.") parser.add_argument('--index_scan', action="store_true", help="By default index scans are disabled to force the script to check the actual table data with sequential scans. Set this option if you want the script to allow index scans to be used (does not guarentee that they will be used).") parser.add_argument('-q', '--quiet', action="store_true", help="Suppress all output unless there is a constraint violation found.") parser.add_argument('--version', action="store_true", help="Print out the minimum version of pg_partman this script is meant to work with. The version of pg_partman installed may be greater than this.") args = parser.parse_args() if args.version: print(partman_version) sys.exit() if args.parent == None: print("-p/--parent option is required") sys.exit(2) if args.column_list == None: print("-l/--column_list option is required") sys.exit(2) if args.temp == None: tmp_copy_file = tempfile.NamedTemporaryFile(prefix="partman_constraint") else: tmp_copy_file = tempfile.NamedTemporaryFile(prefix="partman_constraint", dir=args.temp) fh = open(tmp_copy_file.name, 'w') conn = psycopg2.connect(args.connection) conn.set_session(isolation_level="REPEATABLE READ", readonly=True) cur = conn.cursor() # Get separate object names so they can be properly quoted sql = "SELECT schemaname, tablename FROM pg_catalog.pg_tables WHERE schemaname||'.'||tablename = %s" cur.execute(sql, [args.parent]) result = cur.fetchone() if result == None: print("Given parent table ("+args.parent+") does not exist") sys.exit(2) quoted_parent_table = "\"" + result[0] + "\".\"" + result[1] + "\"" #print(quoted_parent_table) # Recreated column list with double quotes around each cols_array = args.column_list.split(",") quoted_col_list = "\"" + "\",\"".join(cols_array) + "\"" #print(quoted_col_list) if args.index_scan == False: sql = """set enable_bitmapscan = false; set enable_indexonlyscan = false; set enable_indexscan = false; set enable_seqscan = true;""" else: sql = """set enable_bitmapscan = true; set enable_indexonlyscan = true; set enable_indexscan = true; set enable_seqscan = false;""" cur.execute(sql) cur.close() cur = conn.cursor() if not args.quiet: print("Dumping out column data to temp file...") copy_statement = "COPY (SELECT " + quoted_col_list + " FROM " + quoted_parent_table + ") TO STDOUT WITH DELIMITER ','" #print(copy_statement) cur.copy_expert(copy_statement, fh) conn.rollback() conn.close() fh.close() total_count = 0 if not args.quiet: print("Checking for dupes...") with open(tmp_copy_file.name) as infile: counts = collections.Counter(l.strip() for l in infile) for line, count in counts.most_common(): if count > 1: if not args.simple: print(str(line) + ": " + str(count)) total_count += count if args.simple: if total_count > 0: print(total_count) elif not args.quiet: print(total_count) else: if total_count == 0 and not args.quiet: print("No constraint violations found") pg_partman-2.2.2/bin/dump_partition.py000077500000000000000000000132051262146621700200400ustar00rootroot00000000000000#!/usr/bin/env python import argparse, hashlib, os, os.path, psycopg2, subprocess, sys partman_version = "2.0.0" parser = argparse.ArgumentParser(description="This script will dump out and then drop all tables contained in the designated schema using pg_dump. Each table will be in its own separate file along with a SHA-512 hash of the dump file. Tables are not dropped from the database if pg_dump does not return successfully. All dump_* option defaults are the same as they would be for pg_dump if they are not given.", epilog="NOTE: The connection options for psyocpg and pg_dump were separated out due to distinct differences in their requirements depending on your database connection configuration.") parser.add_argument('-n','--schema', help="(Required) The schema that contains the tables that will be dumped.") parser.add_argument('-c','--connection', default="host=", help="""Connection string for use by psycopg. Role used must be able to select pg_catalog.pg_tables in the relevant database and drop all tables in the given schema. Defaults to "host=" (local socket). Note this is distinct from the parameters sent to pg_dump.""") parser.add_argument('-o','--output', default=os.getcwd(), help="Path to dump file output location. Default is where the script is run from.") parser.add_argument('-d','--dump_database', help="Used for pg_dump, same as its --dbname (-d) option or final database name parameter.") parser.add_argument('--dump_host', help="Used for pg_dump, same as its --host (-h) option.") parser.add_argument('--dump_username', help="Used for pg_dump, same as its --username (-U) option.") parser.add_argument('--dump_port', help="Used for pg_dump, same as its --port (-p) option.") parser.add_argument('--pg_dump_path', help="Path to pg_dump binary location. Must set if not in current PATH.") parser.add_argument('--Fp', action="store_true", help="Dump using pg_dump plain text format. Default is binary custom (-Fc).") parser.add_argument('--nohashfile', action="store_true", help="Do NOT create a separate file with the SHA-512 hash of the dump. If dump files are very large, hash generation can possibly take a long time.") parser.add_argument('--nodrop', action="store_true", help="Do NOT drop the tables from the given schema after dumping/hashing.") parser.add_argument('-v','--verbose', action="store_true", help="Provide more verbose output.") parser.add_argument('--version', action="store_true", help="Print out the minimum version of pg_partman this script is meant to work with. The version of pg_partman installed may be greater than this.") args = parser.parse_args() def create_hash(table_name): output_file = os.path.join(args.output, args.schema + "." + table_name + ".pgdump") try: with open(output_file, "rb") as fh: shash = hashlib.sha512() while True: data = fh.read(8192) if not data: break shash.update(data) except IOError as e: print("Cannot access dump file for hash creation: " + e.strerror) sys.exit(2) hash_file = os.path.join(args.output, args.schema + "." + table_name + ".hash") if args.verbose: print("hash_file: " + hash_file) try: with open(hash_file, "w") as fh: fh.write(shash.hexdigest() + " " + os.path.basename(output_file)) except IOError as e: print("Unable to write to hash file: " + e.strerror) sys.exit(2) def drop_table(table_name): conn = psycopg2.connect(args.connection) cur = conn.cursor() sql = "DROP TABLE IF EXISTS \"" + args.schema + "\".\"" + table_name + "\""; print(sql) cur.execute(sql) conn.commit() cur.close() conn.close() def get_tables(): conn = psycopg2.connect(args.connection) cur = conn.cursor() sql = "SELECT tablename FROM pg_catalog.pg_tables WHERE schemaname = %s"; cur.execute(sql, [args.schema]) result = cur.fetchall() cur.close() conn.close() return result def perform_dump(result): table_name = result.pop()[0] processcmd = [] if args.pg_dump_path != None: processcmd.append(args.pg_dump_path) else: processcmd.append("pg_dump") if args.dump_host != None: processcmd.append("--host=" + args.dump_host) if args.dump_port != None: processcmd.append("--port=" + args.dump_port) if args.dump_username != None: processcmd.append("--username=" + args.dump_username) if args.Fp: processcmd.append("--format=plain") else: processcmd.append("--format=custom") processcmd.append("--table=\"" + args.schema + "\".\"" + table_name + "\"") output_file = os.path.join(args.output, args.schema + "." + table_name + ".pgdump") processcmd.append("--file=" + output_file) if args.dump_database != None: processcmd.append(args.dump_database) if args.verbose: print(processcmd) try: subprocess.check_call(processcmd) except subprocess.CalledProcessError as e: print("Error in pg_dump command: " + str(e.cmd)) sys.exit(2) return table_name def print_version(): print(partman_version) sys.exit() if __name__ == "__main__": if args.version: print_version() if args.schema == None: print("-n/--schema option is required") sys.exit(2) if not os.path.exists(args.output): print("Path given by --output (-o) does not exist: " + str(args.output)) sys.exit(2) result = get_tables() while len(result) > 0: table_name = perform_dump(result) if not args.nohashfile: create_hash(table_name) if not args.nodrop: drop_table(table_name) pg_partman-2.2.2/bin/partition_data.py000077500000000000000000000212751262146621700200120ustar00rootroot00000000000000#!/usr/bin/env python import argparse, psycopg2, time, signal, sys partman_version = "2.0.0" parser = argparse.ArgumentParser(description="This script calls either partition_data_time() or partition_data_id() depending on the value given for --type. A commit is done at the end of each --interval and/or fully created partition. Returns the total number of rows moved to partitions. Automatically stops when parent is empty. See docs for examples.", epilog="NOTE: To help avoid heavy load and contention during partitioning, autovacuum is turned off for the parent table when this script is run. When partitioning is complete, autovacuum is set back to its default value and the parent table is vacuumed when it is emptied.") parser.add_argument('-p','--parent', help="Parent table of an already created partition set. (Required)") parser.add_argument('-t','--type', choices=["time","id",], help="""Type of partitioning. Valid values are "time" and "id". (Required)""") parser.add_argument('-c','--connection', default="host=", help="""Connection string for use by psycopg. Defaults to "host=" (local socket).""") parser.add_argument('-i','--interval', help="Value that is passed on to the partitioning function as p_batch_interval argument. Use this to set an interval smaller than the partition interval to commit data in smaller batches. Defaults to the partition interval if not given.") parser.add_argument('-b','--batch', default=0, type=int, help="""How many times to loop through the value given for --interval. If --interval not set, will use default partition interval and make at most -b partition(s). Script commits at the end of each individual batch. (NOT passed as p_batch_count to partitioning function). If not set, all data in the parent table will be partitioned in a single run of the script.""") parser.add_argument('-w','--wait', default=0, type=float, help="Cause the script to pause for a given number of seconds between commits (batches) to reduce write load") parser.add_argument('-o', '--order', choices=["ASC", "DESC"], default="ASC", help="Allows you to specify the order that data is migrated from the parent to the children, either ascending (ASC) or descending (DESC). Default is ASC.") parser.add_argument('-l','--lockwait', default=0, type=float, help="Have a lock timeout of this many seconds on the data move. If a lock is not obtained, that batch will be tried again.") parser.add_argument('--lockwait_tries', default=10, type=int, help="Number of times to allow a lockwait to time out before giving up on the partitioning. Defaults to 10") parser.add_argument('--autovacuum_on', action="store_true", help="Turning autovacuum off requires a brief lock to ALTER the table property. Set this option to leave autovacuum on and avoid the lock attempt.") parser.add_argument('-q','--quiet', action="store_true", help="Switch setting to stop all output during and after partitioning for use in cron jobs") parser.add_argument('--version', action="store_true", help="Print out the minimum version of pg_partman this script is meant to work with. The version of pg_partman installed may be greater than this.") parser.add_argument('--debug', action="store_true", help="Show additional debugging output") args = parser.parse_args() def close_conn(conn): conn.close() def create_conn(): conn = psycopg2.connect(args.connection) conn.autocommit = True return conn def get_partman_schema(conn): cur = conn.cursor() sql = "SELECT nspname FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_partman' AND e.extnamespace = n.oid" cur.execute(sql) partman_schema = "\"" + cur.fetchone()[0] + "\"" cur.close() return partman_schema def get_quoted_parent_table(conn): cur = conn.cursor() sql = "SELECT schemaname, tablename FROM pg_catalog.pg_tables WHERE schemaname||'.'||tablename = %s" cur.execute(sql, [args.parent]) result = cur.fetchone() if result == None: print("Given parent table ("+args.parent+") does not exist") sys.exit(2) quoted_parent_table = "\"" + result[0] + "\".\"" + result[1] + "\"" cur.close() return quoted_parent_table def partition_data(conn, partman_schema): batch_count = 0 total = 0 lockwait_count = 0 cur = conn.cursor() sql = "SELECT " + partman_schema + ".partition_data_" + args.type + "(%s" if args.interval != "": sql += ", p_batch_interval := %s" sql += ", p_lock_wait := %s" sql += ", p_order := %s)" while True: if args.interval != "": li = [args.parent, args.interval, args.lockwait, args.order] else: li = [args.parent, args.lockwait, args.order] if args.debug: print(cur.mogrify(sql, li)) cur.execute(sql, li) result = cur.fetchone() if not args.quiet: if result[0] > 0: print("Rows moved: " + str(result[0])) elif result[0] == -1: print("Unable to obtain lock, trying again") print(conn.notices[-1]) # if lock wait timeout, do not increment the counter if result[0] != -1: batch_count += 1 total += result[0] lockwait_count = 0 else: lockwait_count += 1 if lockwait_count > args.lockwait_tries: print("Quitting due to inability to get lock on next rows to be moved") break # If no rows left or given batch argument limit is reached if (result[0] == 0) or (args.batch > 0 and batch_count >= int(args.batch)): break time.sleep(args.wait) return total def print_version(): print(partman_version) sys.exit() def reset_autovacuum(conn, partman_schema, quoted_parent_table): cur = conn.cursor() sql = "ALTER TABLE " + quoted_parent_table + " RESET (autovacuum_enabled, toast.autovacuum_enabled)" if not args.quiet: print("Attempting to reset autovacuum for old parent table and all child tables...") if args.debug: print(cur.mogrify(sql)) cur.execute(sql) sql = "SELECT partition_schemaname, partition_tablename FROM " + partman_schema + ".show_partitions(%s)" if args.debug: print(cur.mogrify(sql, [args.parent])) cur.execute(sql, [args.parent]) result = cur.fetchall() for r in result: sql = "ALTER TABLE \"" + r[0] + "\".\"" + r[1] + "\" RESET (autovacuum_enabled, toast.autovacuum_enabled)" if args.debug: print(cur.mogrify(sql)) cur.execute(sql) print("\t... Success!") cur.close() def sigint_handler(signum, frame): if is_autovac_off == True: reset_autovacuum(conn, partman_schema) sys.exit(2) def turn_off_autovacuum(conn, partman_schema, quoted_parent_table): cur = conn.cursor() sql = "ALTER TABLE " + quoted_parent_table + " SET (autovacuum_enabled = false, toast.autovacuum_enabled = false)" if not args.quiet: print("Attempting to turn off autovacuum for partition set...") if args.debug: print(cur.mogrify(sql)) cur.execute(sql) sql = "SELECT partition_schemaname, partition_tablename FROM " + partman_schema + ".show_partitions(%s)" if args.debug: print(cur.mogrify(sql, [args.parent])) cur.execute(sql, [args.parent]) result = cur.fetchall() for r in result: sql = "ALTER TABLE \"" + r[0] + "\".\"" + r[1] + "\" SET (autovacuum_enabled = false, toast.autovacuum_enabled = false)" if args.debug: print(cur.mogrify(sql)) cur.execute(sql) print("\t... Success!") cur.close() def vacuum_parent(conn, quoted_parent_table): cur = conn.cursor() sql = "VACUUM ANALYZE " + quoted_parent_table if args.debug: print(cur.mogrify(sql)) if not args.quiet: print("Running vacuum analyze on parent table...") cur.execute(sql) cur.close() if __name__ == "__main__": if args.version: print_version() if args.parent == None: print("-p/--parent option is required") sys.exit(2) if args.type == None: print("-t/--type option is required") sys.exit(2) is_autovac_off = False signal.signal(signal.SIGINT, sigint_handler) conn = create_conn() partman_schema = get_partman_schema(conn) quoted_parent_table = get_quoted_parent_table(conn) if not args.autovacuum_on: turn_off_autovacuum(conn, partman_schema, quoted_parent_table) is_autovac_off = True total = partition_data(conn, partman_schema) if not args.quiet: print("Total rows moved: %d" % total) vacuum_parent(conn, quoted_parent_table) if not args.autovacuum_on: reset_autovacuum(conn, partman_schema, quoted_parent_table) is_autovac_off = False close_conn(conn) pg_partman-2.2.2/bin/reapply_constraints.py000077500000000000000000000157661262146621700211230ustar00rootroot00000000000000#!/usr/bin/env python import argparse, psycopg2, sys, time from multiprocessing import Process partman_version = "2.2.0" parser = argparse.ArgumentParser(description="Script for reapplying additional constraints managed by pg_partman on child tables. See docs for additional info on this special constraint management. Script runs in two distinct modes: 1) Drop all constraints 2) Apply all constraints. Typical usage would be to run the drop mode, edit the data, then run apply mode to re-create all constraints on a partition set.") parser.add_argument('-p', '--parent', help="Parent table of an already created partition set. (Required)") parser.add_argument('-c','--connection', default="host=", help="""Connection string for use by psycopg. Defaults to "host=" (local socket).""") parser.add_argument('-d', '--drop_constraints', action="store_true", help="Drop all constraints managed by pg_partman. Drops constraints on all child tables including current & future.") parser.add_argument('-a', '--add_constraints', action="store_true", help="Apply configured constraints to all child tables older than the optimize_constraint value.") parser.add_argument('-j', '--jobs', type=int, default=0, help="Use the python multiprocessing library to recreate indexes in parallel. Value for -j is number of simultaneous jobs to run. Note that this is per table, not per index. Be very careful setting this option if load is a concern on your systems.") parser.add_argument('-w', '--wait', type=float, default=0, help="Wait the given number of seconds after a table has had its constraints dropped or applied before moving on to the next. When used with -j, this will set the pause between the batches of parallel jobs instead.") parser.add_argument('--dryrun', action="store_true", help="Show what the script will do without actually running it against the database. Highly recommend reviewing this before running.") parser.add_argument('-q', '--quiet', action="store_true", help="Turn off all output.") parser.add_argument('--version', action="store_true", help="Print out the minimum version of pg_partman this script is meant to work with. The version of pg_partman installed may be greater than this.") args = parser.parse_args() def apply_proc(child_table, partman_schema): conn = create_conn() conn.autocommit = True cur = conn.cursor() sql = "SELECT " + partman_schema + ".apply_constraints(%s, %s, %s, %s, %s)" debug = False; if not args.quiet: debug = True print(cur.mogrify(sql, [args.parent, child_table, False, None, debug])) if not args.dryrun: cur.execute(sql, [args.parent, child_table, False, None, debug]) cur.close() close_conn(conn) def create_conn(): conn = psycopg2.connect(args.connection) return conn def close_conn(conn): conn.close() def drop_proc(child_table, partman_schema): conn = create_conn() conn.autocommit = True cur = conn.cursor() sql = "SELECT " + partman_schema + ".drop_constraints(%s, %s, %s)" debug = False; if not args.quiet: debug = True print(cur.mogrify(sql, [args.parent, child_table, debug])) if not args.dryrun: cur.execute(sql, [args.parent, child_table, debug]) cur.close() close_conn(conn) def get_children(conn, partman_schema): cur = conn.cursor() sql = "SELECT partition_schemaname||'.'||partition_tablename FROM " + partman_schema + ".show_partitions(%s, %s)" cur.execute(sql, [args.parent, 'ASC']) child_list = cur.fetchall() cur.close() return child_list def get_partman_schema(conn): cur = conn.cursor() sql = "SELECT nspname FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_partman' AND e.extnamespace = n.oid" cur.execute(sql) partman_schema = "\"" + cur.fetchone()[0] + "\"" cur.close() return partman_schema def get_optimize_value(conn, partman_schema): cur = conn.cursor() sql = "SELECT optimize_constraint FROM " + partman_schema + ".part_config WHERE parent_table = %s" cur.execute(sql, [args.parent]) optimize_constraint = int(cur.fetchone()[0]) cur.close() return optimize_constraint def get_quoted_parent_table(conn): cur = conn.cursor() sql = "SELECT schemaname, tablename FROM pg_catalog.pg_tables WHERE schemaname||'.'||tablename = %s" cur.execute(sql, [args.parent]) result = cur.fetchone() if result == None: print("Given parent table ("+args.parent+") does not exist") sys.exit(2) quoted_parent_table = "\"" + result[0] + "\".\"" + result[1] + "\"" cur.close() return quoted_parent_table def print_version(): print(partman_version) sys.exit() if __name__ == "__main__": if args.version: print_version() if args.parent == None: print("-p/--parent option is required") sys.exit(2) if args.parent.find(".") < 0: print("Parent table must be schema qualified") sys.exit(2) if args.drop_constraints and args.add_constraints: print("Can only set one or the other of --drop_constraints (-d) and --add_constraints (-a)") sys.exit(2) if (args.drop_constraints == False) and (args.add_constraints == False): print("Must set one of --drop_constraints (-d) or --add_constraints (-a)") sys.exit(2) main_conn = create_conn() partman_schema = get_partman_schema(main_conn) quoted_parent_table = get_quoted_parent_table(main_conn) child_list = get_children(main_conn, partman_schema) optimize_constraint = get_optimize_value(main_conn, partman_schema) if args.add_constraints: # Remove tables from the list of child tables that shouldn't have constraints yet for x in range((optimize_constraint * 2) + 1): child_list.pop() if args.jobs == 0: for c in child_list: if args.drop_constraints: drop_proc(c[0], partman_schema) if args.add_constraints: apply_proc(c[0], partman_schema) if args.wait > 0: time.sleep(args.wait) else: child_list.reverse() while len(child_list) > 0: if not args.quiet: print("Jobs left in queue: " + str(len(child_list))) if len(child_list) < args.jobs: args.jobs = len(child_list) processlist = [] for num in range(0, args.jobs): c = child_list.pop() if args.drop_constraints: p = Process(target=drop_proc, args=(c[0], partman_schema)) if args.add_constraints: p = Process(target=apply_proc, args=(c[0], partman_schema)) p.start() processlist.append(p) for j in processlist: j.join() if args.wait > 0: time.sleep(args.wait) sql = 'ANALYZE ' + quoted_parent_table main_cur = main_conn.cursor() if not args.quiet: print(main_cur.mogrify(sql)) if not args.dryrun: main_cur.execute(sql) close_conn(main_conn) pg_partman-2.2.2/bin/reapply_foreign_keys.py000077500000000000000000000126131262146621700212240ustar00rootroot00000000000000#!/usr/bin/env python import argparse, psycopg2, sys, time partman_version = "2.0.0" parser = argparse.ArgumentParser(description="This script will reapply the foreign keys on a parent table to all child tables in an inheritance set. Any existing foreign keys on child tables will be dropped in order to match the parent. A commit is done after each foreign key application to avoid excessive contention. Note that this script can work on any inheritance set, not just partition sets managed by pg_partman.") parser.add_argument('-p','--parent', help="Parent table of an already created partition set. (Required)") parser.add_argument('-c','--connection', default="host=", help="""Connection string for use by psycopg. Defaults to "host=" (local socket).""") parser.add_argument('-q', '--quiet', action="store_true", help="Switch setting to stop all output during and after partitioning undo.") parser.add_argument('--dryrun', action="store_true", help="Show what the script will do without actually running it against the database. Highly recommend reviewing this before running.") parser.add_argument('--nonpartman', action="store_true", help="If the partition set you are running this on is not managed by pg_partman, set this flag. Otherwise internal pg_partman functions are used and this script may not work. When this is set the order that the tables are rekeyed is alphabetical instead of logical.") parser.add_argument('--debug', action="store_true", help="Show additional debugging output") parser.add_argument('--version', action="store_true", help="Print out the minimum version of pg_partman this script is meant to work with. The version of pg_partman installed may be greater than this.") args = parser.parse_args() def apply_foreign_keys(conn, child_tables): if not args.quiet: print("Applying foreign keys to child tables...") cur = conn.cursor() for c in child_tables: sql = """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 JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid WHERE n.nspname ||'.'|| c.relname = %s AND contype = 'f'"""; if args.debug: print(cur.mogrify(sql, [args.parent])) cur.execute(sql, [args.parent]) parent_fkeys = cur.fetchall() for pfk in parent_fkeys: alter_sql = "ALTER TABLE \"" + c[0] + "\".\"" + c[1] + "\" ADD " + pfk[0] if not args.quiet: print(alter_sql) if not args.dryrun: cur.execute(alter_sql) def create_conn(): conn = psycopg2.connect(args.connection) conn.autocommit = True return conn def close_conn(conn): conn.close() def drop_foreign_keys(conn, child_tables): if not args.quiet: print("Dropping current foreign keys on child tables...") cur = conn.cursor() for c in child_tables: sql = """SELECT constraint_name FROM information_schema.table_constraints WHERE table_schema = %s AND table_name = %s AND constraint_type = 'FOREIGN KEY'""" if args.debug: print(cur.mogrify(sql, [ c[0], c[1] ])) cur.execute(sql, [ c[0], c[1] ]) child_fkeys = cur.fetchall() for cfk in child_fkeys: alter_sql = "ALTER TABLE \"" + c[0] + "\".\"" + c[1] + "\" DROP CONSTRAINT \"" + cfk[0] + "\"" if not args.quiet: print(alter_sql) if not args.dryrun: cur.execute(alter_sql) def get_child_tables(conn, partman_schema): if not args.quiet: print("Getting list of child tables...") cur = conn.cursor() if args.nonpartman == False: sql = "SELECT partition_schemaname, partition_tablename FROM " + partman_schema + ".show_partitions(%s)" else: sql = """ WITH parent_info AS ( SELECT c1.oid FROM pg_catalog.pg_class c1 JOIN pg_catalog.pg_namespace n1 ON c1.relnamespace = n1.oid WHERE n1.nspname ||'.'|| c1.relname = %s ) SELECT n.nspname::text, 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 JOIN parent_info pi ON h.inhparent = pi.oid ORDER BY 1,2""" if args.debug: print(cur.mogrify(sql, [args.parent])) cur.execute(sql, [args.parent]) result = cur.fetchall() return result def get_partman_schema(conn): cur = conn.cursor() sql = "SELECT nspname FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_partman' AND e.extnamespace = n.oid" cur.execute(sql) partman_schema = "\"" + cur.fetchone()[0] + "\"" cur.close() return partman_schema def print_version(): print(partman_version) sys.exit() if __name__ == "__main__": if args.version: print_version() if args.parent == None: print("-p/--parent option is required") sys.exit(2) conn = create_conn() partman_schema = get_partman_schema(conn) child_tables = get_child_tables(conn, partman_schema) drop_foreign_keys(conn, child_tables) apply_foreign_keys(conn, child_tables) if not args.quiet: print("Done!") close_conn(conn) pg_partman-2.2.2/bin/reapply_indexes.py000077500000000000000000000401131262146621700201730ustar00rootroot00000000000000#!/usr/bin/env python import argparse, psycopg2, re, sys, time from multiprocessing import Process partman_version = "2.0.0" parser = argparse.ArgumentParser(description="Script for reapplying indexes on child tables in a partition set to match the parent table. Any indexes that currently exist on the children and match the definition on the parent will be left as is. There is an option to recreate matching as well indexes if desired, as well as the primary key. Indexes that do not exist on the parent will be dropped. Commits are done after each index is dropped/created to help prevent long running transactions & locks.", epilog="NOTE: New index names are made based off the child table name & columns used, so their naming may differ from the name given on the parent. This is done to allow the tool to account for long or duplicate index names. If an index name would be duplicated, an incremental counter is added on to the end of the index name to allow it to be created. Use the --dryrun option first to see what it will do and which names may cause dupes to be handled like this.") parser.add_argument('-p', '--parent', help="Parent table of an already created partition set. (Required)") parser.add_argument('-c','--connection', default="host=", help="""Connection string for use by psycopg. Defaults to "host=" (local socket).""") parser.add_argument('--concurrent', action="store_true", help="Create indexes with the CONCURRENTLY option. Note this does not work on primary keys when --primary is given.") parser.add_argument('--drop_concurrent', action="store_true", help="If an index is dropped (because it doesn't exist on the parent or because you set them to be recreated), do it concurrently (PostgreSQL >= v9.2 only). Note this does not work on primary keys when --primary is given.") parser.add_argument('-R', '--recreate_all', action="store_true", help="By default, if an index exists on a child and matches the parent, it will not be touched. Setting this option will force all child indexes to be dropped & recreated. Will obey the --concurrent & --drop_concurrent options if given. Will not recreate primary keys unless --primary option is also given.") parser.add_argument('--primary', action="store_true", help="By default the primary key is not recreated. Set this option if that is needed. Note this will cause an exclusive lock on the child table.") parser.add_argument('-j', '--jobs', type=int, default=0, help="Use the python multiprocessing library to recreate indexes in parallel. Value for -j is number of simultaneous jobs to run. Note that this is per table, not per index. Be very careful setting this option if load is a concern on your systems.") parser.add_argument('-w', '--wait', type=float, default=0, help="Wait the given number of seconds after indexes have finished being created on a table before moving on to the next. When used with -j, this will set the pause between the batches of parallel jobs instead.") parser.add_argument('--dryrun', action="store_true", help="Show what the script will do without actually running it against the database. Highly recommend reviewing this before running. Note that if multiple indexes would get the same default name, the duplicated name will show in the dryrun (because the index doesn't exist in the catalog to check for it). When the real thing is run, the duplicated names will be handled as stated in NOTE at the end of --help.") parser.add_argument('-q', '--quiet', action="store_true", help="Turn off all output.") parser.add_argument('--nonpartman', action="store_true", help="If the partition set you are running this on is not managed by pg_partman, set this flag otherwise this script may not work. Note that the pg_partman extension is still required to be installed for this to work since it uses certain internal functions. When this is set the order that the tables are reindexed is alphabetical instead of logical.") parser.add_argument('--version', action="store_true", help="Print out the minimum version of pg_partman this script is meant to work with. The version of pg_partman installed may be greater than this.") args = parser.parse_args() # Add any checks for version specific features to this function def check_version(conn, partman_schema): cur = conn.cursor() if args.drop_concurrent: sql = "SELECT " + partman_schema + ".check_version('9.2.0')" cur.execute(sql) if cur.fetchone()[0] == False: print("ERROR: --drop_concurrent option requires PostgreSQL minimum version 9.2.0") sys.exit(2) cur.close() def create_conn(): conn = psycopg2.connect(args.connection) return conn def create_index(conn, partman_schema, child_schemaname, child_tablename, child_index_list, parent_index_list): cur = conn.cursor() sql = """SELECT schemaname, tablename FROM pg_catalog.pg_tables WHERE schemaname||'.'||tablename = %s""" cur.execute(sql, [args.parent]) result = cur.fetchone() parent_schemaname = result[0] parent_tablename = result[1] cur.close() parent_match_regex = re.compile(r" ON \"?%s\"?\.\"?%s\"? | ON \"?%s\"?" % (parent_schemaname, parent_tablename, parent_tablename)) index_match_regex = re.compile(r'(?PUSING .*)') for i in parent_index_list: # if there is already a child index that matches the parent index don't try to create it unless --recreate_all set if args.recreate_all != True: child_found = False statement = None parinddef = index_match_regex.search(i[0]) for c in child_index_list: chinddef = index_match_regex.search(c[0]) if chinddef.group('index_def') == parinddef.group('index_def'): child_found = True break if child_found: continue if i[1] == True and args.primary: index_name = child_tablename + "_" + "_".join(i[2].split(",")) sql = "SELECT " + partman_schema + ".check_name_length('" + index_name + "', p_suffix := '_pk')" cur = conn.cursor() cur.execute(sql) index_name = cur.fetchone()[0] cur.close() quoted_column_names = "\"" + "\",\"".join(i[2].split(",")) + "\"" statement = "ALTER TABLE \"" + child_schemaname + "\".\"" + child_tablename + "\" ADD CONSTRAINT \"" + index_name + "\" PRIMARY KEY (" + quoted_column_names + ")" elif i[1] == False: index_name = child_tablename if i[2] != None: index_name += "_" index_name += "_".join(i[2].split(",")) sql = "SELECT " + partman_schema + ".check_name_length('" + index_name + "', p_suffix := '_idx')" cur = conn.cursor() cur.execute(sql) index_name = cur.fetchone()[0] name_counter = 1 while True: sql = "SELECT count(*) FROM pg_class c JOIN pg_namespace n ON c.relnamespace = n.oid WHERE n.nspname = %s AND c.relname = %s" cur = conn.cursor() cur.execute(sql, [parent_schemaname, index_name]) index_exists = cur.fetchone()[0] if index_exists != None and index_exists > 0: index_name = child_tablename if i[2] != None: index_name += "_" index_name += "_".join(i[2].split(",")) suffix = "_idx" + str(name_counter) sql = "SELECT " + partman_schema + ".check_name_length('" + index_name + "', p_suffix := '" + suffix + "')" cur = conn.cursor() cur.execute(sql) index_name = cur.fetchone()[0] name_counter += 1 else: break cur.close() statement = i[0] statement = statement.replace(i[3], index_name) # replace parent index name with child index name if args.concurrent: statement = statement.replace("CREATE INDEX ", "CREATE INDEX CONCURRENTLY ") statement = parent_match_regex.sub(" ON \"" + child_schemaname + "\".\"" + child_tablename + "\" ", statement) cur = conn.cursor() if statement != None: if not args.quiet: print(cur.mogrify(statement)) if not args.dryrun: cur.execute(statement) cur.close() def close_conn(conn): conn.close() def drop_index(conn, child_schemaname, child_tablename, child_index_list, parent_index_list): cur = conn.cursor() drop_list = [] for d in child_index_list: if d[1] == True and args.primary: statement = "ALTER TABLE \"" + child_schemaname + "\".\"" + child_tablename + "\" DROP CONSTRAINT \"" + d[4] + "\"" if not args.quiet: print(cur.mogrify(statement)) if not args.dryrun: cur.execute(statement) elif d[1] == False: if args.drop_concurrent: statement = "DROP INDEX CONCURRENTLY \"" + d[2] + "\".\"" + d[3] + "\"" else: statement = "DROP INDEX \"" + d[2] + "\".\"" + d[3] + "\"" if args.recreate_all != True: pat = re.compile(r'(?PUSING .*)') # if there is a parent index that matches the child index # don't try to drop it - we'd just have to recreate it chinddef = pat.search(d[0]) for p in parent_index_list: parent_found = False parinddef = pat.search(p[0]) if chinddef.group('index_def') == parinddef.group('index_def'): parent_found = True break if not parent_found: if not args.quiet: print(cur.mogrify(statement)) if not args.dryrun: cur.execute(statement) else: if not args.quiet: print(cur.mogrify(statement)) if not args.dryrun: cur.execute(statement) def get_children(conn, partman_schema): cur = conn.cursor() if args.nonpartman == False: sql = "SELECT partition_schemaname, partition_tablename FROM " + partman_schema + ".show_partitions(%s)" else: sql = """ WITH parent_info AS ( SELECT c1.oid FROM pg_catalog.pg_class c1 JOIN pg_catalog.pg_namespace n1 ON c1.relnamespace = n1.oid WHERE n1.nspname ||'.'|| c1.relname = %s ) SELECT n.nspname::text, 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 JOIN parent_info pi ON h.inhparent = pi.oid ORDER BY 1,2""" cur.execute(sql, [args.parent]) child_list = cur.fetchall() cur.close() return child_list def get_child_index_list(conn, child_schemaname, child_tablename): cur = conn.cursor() sql = """ 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 n1.nspname = %s AND c1.relname = %s ) SELECT pg_get_indexdef(indexrelid) AS statement , i.indisprimary , n.nspname AS index_schemaname , c.relname AS index_name , t.conname FROM pg_catalog.pg_index i JOIN pg_catalog.pg_class c ON i.indexrelid = c.oid JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid LEFT JOIN pg_catalog.pg_constraint t ON c.oid = t.conindid JOIN child_info ci ON i.indrelid = ci.oid """ cur.execute(sql, [child_schemaname, child_tablename]) child_index_list = cur.fetchall() cur.close() return child_index_list def get_parent_index_list(conn): cur = conn.cursor() sql = """ WITH parent_info AS ( SELECT c1.oid FROM pg_catalog.pg_class c1 JOIN pg_catalog.pg_namespace n1 ON c1.relnamespace = n1.oid WHERE n1.nspname||'.'||c1.relname = %s ) SELECT pg_get_indexdef(indexrelid) AS statement , i.indisprimary , ( SELECT array_to_string(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 FROM pg_catalog.pg_index i JOIN pg_catalog.pg_class c ON i.indexrelid = c.oid JOIN parent_info pi ON i.indrelid = pi.oid WHERE i.indisvalid ORDER BY 1""" cur.execute(sql, [args.parent]) parent_index_list = cur.fetchall() return parent_index_list def get_partman_schema(conn): cur = conn.cursor() sql = "SELECT nspname FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_partman' AND e.extnamespace = n.oid" cur.execute(sql) partman_schema = "\"" + cur.fetchone()[0] + "\"" cur.close() return partman_schema def get_quoted_parent_table(conn): cur = conn.cursor() sql = "SELECT schemaname, tablename FROM pg_catalog.pg_tables WHERE schemaname||'.'||tablename = %s" cur.execute(sql, [args.parent]) result = cur.fetchone() if result == None: print("Given parent table ("+args.parent+") does not exist") sys.exit(2) quoted_parent_table = "\"" + result[0] + "\".\"" + result[1] + "\"" cur.close() return quoted_parent_table def print_version(): print(partman_version) sys.exit() def reindex_proc(child_schemaname, child_tablename, parent_index_list, partman_schema): conn = create_conn() conn.autocommit = True # must be turned on to support CONCURRENTLY cur = conn.cursor() child_index_list = get_child_index_list(conn, child_schemaname, child_tablename) drop_index(conn, child_schemaname, child_tablename, child_index_list, parent_index_list) create_index(conn, partman_schema, child_schemaname, child_tablename, child_index_list, parent_index_list) sql = "ANALYZE \"" + child_schemaname + "\".\"" + child_tablename + "\"" if not args.quiet: print(cur.mogrify(sql)) if not args.dryrun: cur.execute(sql) cur.close() close_conn(conn) if __name__ == "__main__": if args.version: print_version() if args.parent == None: print("-p/--parent option is required") sys.exit(2) if args.parent.find(".") < 0: print("ERROR: Parent table must be schema qualified") sys.exit(2) conn = create_conn() cur = conn.cursor() partman_schema = get_partman_schema(conn) # Script now states it only works for pg_partman 2.x and hence postgres 9.4+. Leaving this here in case future version checks are needed # check_version(conn, partman_schema) quoted_parent_table = get_quoted_parent_table(conn) parent_index_list = get_parent_index_list(conn) child_list = get_children(conn, partman_schema) close_conn(conn) if args.jobs == 0: for c in child_list: reindex_proc(c[0], c[1], parent_index_list, partman_schema) if args.wait > 0: time.sleep(args.wait) else: child_list.reverse() while len(child_list) > 0: if not args.quiet: print("Jobs left in queue: " + str(len(child_list))) if len(child_list) < args.jobs: args.jobs = len(child_list) processlist = [] for num in range(0, args.jobs): c = child_list.pop() p = Process(target=reindex_proc, args=(c[0], c[1],parent_index_list,partman_schema)) p.start() processlist.append(p) for j in processlist: j.join() if args.wait > 0: time.sleep(args.wait) pg_partman-2.2.2/bin/undo_partition.py000077500000000000000000000245741262146621700200530ustar00rootroot00000000000000#!/usr/bin/env python import argparse, psycopg2, signal, sys, time partman_version = "2.0.0" parser = argparse.ArgumentParser(description="This script calls either undo_partition(), undo_partition_time() or undo_partition_id depending on the value given for --type. A commit is done at the end of each --interval and/or emptied partition. Returns the total number of rows put into the parent. Automatically stops when last child table is empty.", epilog="NOTE: To help avoid heavy load and contention during the undo, autovacuum is turned off for the parent table and all child tables when this script is run. When the undo is complete, autovacuum is set back to its default for the parent. Any child tables left behind will still have it turned off.") parser.add_argument('-p','--parent', help="Parent table of the partition set. (Required)") parser.add_argument('-t','--type', choices=["time","id",], help="""Type of partitioning. Valid values are "time" and "id". Not setting this argument will use undo_partition() and work on any parent/child table set.""") parser.add_argument('-c','--connection', default="host=", help="""Connection string for use by psycopg. Defaults to "host=" (local socket).""") parser.add_argument('-i','--interval', help="Value that is passed on to the undo partitioning function as p_batch_interval. Use this to set an interval smaller than the partition interval to commit data in smaller batches. Defaults to the partition interval if not given. If -t value is not set, interval cannot be smaller than the partition interval and an entire partition is copied each batch.") parser.add_argument('-b','--batch', type=int, default=0, help="How many times to loop through the value given for --interval. If --interval not set, will use default partition interval and undo at most -b partition(s). Script commits at the end of each individual batch. (NOT passed as p_batch_count to undo function). If not set, all data will be moved to the parent table in a single run of the script.") parser.add_argument('-d', '--droptable', action="store_true", help="Switch setting for whether to drop child tables when they are empty. Do not set to just uninherit.") parser.add_argument('-w','--wait', type=float, default=0, help="Cause the script to pause for a given number of seconds between commits (batches).") parser.add_argument('-l','--lockwait', default=0, type=float, help="Have a lock timeout of this many seconds on the data move. If a lock is not obtained, that batch will be tried again.") parser.add_argument('--lockwait_tries', default=10, type=int, help="Number of times to allow a lockwait to time out before giving up on the partitioning. Defaults to 10") parser.add_argument('--autovacuum_on', action="store_true", help="Turning autovacuum off requires a brief lock to ALTER the table property. Set this option to leave autovacuum on and avoid the lock attempt.") parser.add_argument('-q', '--quiet', action="store_true", help="Switch setting to stop all output during and after partitioning undo.") parser.add_argument('--version', action="store_true", help="Print out the minimum version of pg_partman this script is meant to work with. The version of pg_partman installed may be greater than this.") parser.add_argument('--debug', action="store_true", help="Show additional debugging output") args = parser.parse_args() def close_conn(conn): conn.close() def create_conn(): conn = psycopg2.connect(args.connection) conn.autocommit = True return conn def get_partman_schema(conn): cur = conn.cursor() sql = "SELECT nspname FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_partman' AND e.extnamespace = n.oid" cur.execute(sql) partman_schema = "\"" + cur.fetchone()[0] + "\"" cur.close() return partman_schema def get_quoted_parent_table(conn): cur = conn.cursor() sql = "SELECT schemaname, tablename FROM pg_catalog.pg_tables WHERE schemaname||'.'||tablename = %s" cur.execute(sql, [args.parent]) result = cur.fetchone() if result == None: print("Given parent table ("+args.parent+") does not exist") sys.exit(2) quoted_parent_table = "\"" + result[0] + "\".\"" + result[1] + "\"" cur.close() return quoted_parent_table def print_version(): print(partman_version) sys.exit() def reset_autovacuum(conn, partman_schema, quoted_parent_table): cur = conn.cursor() sql = "ALTER TABLE " + quoted_parent_table + " RESET (autovacuum_enabled, toast.autovacuum_enabled)" if not args.quiet: print("Attempting to reset autovacuum for old parent table...") if args.debug: print(cur.mogrify(sql)) cur.execute(sql) # Do not use show_partitions() so that this can work on non-pg_partman partition sets # sql = "SELECT partition_schemaname, partition_tablename FROM " + partman_schema + ".show_partitions(%s)" sql = """ WITH parent_info AS ( SELECT c1.oid FROM pg_catalog.pg_class c1 JOIN pg_catalog.pg_namespace n1 ON c1.relnamespace = n1.oid WHERE n1.nspname ||'.'|| c1.relname = %s ) SELECT n.nspname::text, 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 JOIN parent_info pi ON h.inhparent = pi.oid ORDER BY 1,2""" if args.debug: print(cur.mogrify(sql, [args.parent])) cur.execute(sql, [args.parent]) result = cur.fetchall() for r in result: sql = "ALTER TABLE \"" + r[0] + "\".\"" + r[1] + "\" RESET (autovacuum_enabled, toast.autovacuum_enabled)" if args.debug: print(cur.mogrify(sql)) cur.execute(sql) print("\t... Success!") cur.close() def sigint_handler(signum, frame): if is_autovac_off == True: reset_autovacuum(conn, partman_schema) sys.exit(2) def turn_off_autovacuum(conn, partman_schema, quoted_parent_table): cur = conn.cursor() sql = "ALTER TABLE " + quoted_parent_table + " SET (autovacuum_enabled = false, toast.autovacuum_enabled = false)" if not args.quiet: print("Attempting to turn off autovacuum for partition set...") if args.debug: print(cur.mogrify(sql)) cur.execute(sql) # Do not use show_partitions() so that this can work on non-pg_partman partition sets # sql = "SELECT partition_schemaname, partition_tablename FROM " + partman_schema + ".show_partitions(%s)" sql = """ WITH parent_info AS ( SELECT c1.oid FROM pg_catalog.pg_class c1 JOIN pg_catalog.pg_namespace n1 ON c1.relnamespace = n1.oid WHERE n1.nspname ||'.'|| c1.relname = %s ) SELECT n.nspname::text, 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 JOIN parent_info pi ON h.inhparent = pi.oid ORDER BY 1,2""" if args.debug: print(cur.mogrify(sql, [args.parent])) cur.execute(sql, [args.parent]) result = cur.fetchall() for r in result: sql = "ALTER TABLE \"" + r[0] + "\".\"" + r[1] + "\" SET (autovacuum_enabled = false, toast.autovacuum_enabled = false)" if args.debug: print(cur.mogrify(sql)) cur.execute(sql) print("\t... Success!") cur.close() def undo_partition_data(conn, partman_schema): batch_count = 0 total = 0 lockwait_count = 0 cur = conn.cursor() sql = "SELECT " + partman_schema + ".undo_partition" if args.type != None: sql += "_" + args.type sql += "(%s, p_keep_table := %s" if args.interval != None: sql += ", p_batch_interval := %s" sql += ", p_lock_wait := %s" sql += ")" # Actual undo sql functions do not drop by default, so fix argument value to match that default if args.droptable: keep_table = False else: keep_table = True while True: if args.interval != None: li = [args.parent, keep_table, args.interval, args.lockwait] else: li = [args.parent, keep_table, args.lockwait] if args.debug: print(cur.mogrify(sql, li)) cur.execute(sql, li) result = cur.fetchone() conn.commit() if not args.quiet: if result[0] > 0: print("Rows moved into parent: " + str(result[0])) elif result[0] == -1: print("Unable to obtain lock, trying again (" + str(lockwait_count+1) + ")") print(conn.notices[-1]) # if lock wait timeout, do not increment the counter if result[0] != -1: batch_count += 1 total += result[0] lockwait_count = 0 else: lockwait_count += 1 if lockwait_count > args.lockwait_tries: print("Quitting due to inability to get lock on table/rows for migration to parent") break # If no rows left or given batch argument limit is reached if (result[0] == 0) or (args.batch > 0 and batch_count >= int(args.batch)): break if args.wait > 0: time.sleep(args.wait) return total def vacuum_parent(conn, quoted_parent_table): cur = conn.cursor() sql = "VACUUM ANALYZE " + quoted_parent_table if args.debug: print(cur.mogrify(sql)) if not args.quiet: print("Running vacuum analyze on parent table...") cur.execute(sql) cur.close() if __name__ == "__main__": if args.version: print_version() if args.parent == None: print("-p/--parent option is required") sys.exit(2) if args.parent.find(".") < 0: print("ERROR: Parent table must be schema qualified") sys.exit(2) is_autovac_off = False signal.signal(signal.SIGINT, sigint_handler) conn = create_conn() partman_schema = get_partman_schema(conn) quoted_parent_table = get_quoted_parent_table(conn) if not args.autovacuum_on: turn_off_autovacuum(conn, partman_schema, quoted_parent_table) is_autovac_off = True total = undo_partition_data(conn, partman_schema) if not args.quiet: print("Total rows moved: %d" % total) vacuum_parent(conn, quoted_parent_table) if not args.autovacuum_on: reset_autovacuum(conn, partman_schema, quoted_parent_table) is_autovac_off = False close_conn(conn) pg_partman-2.2.2/doc/000077500000000000000000000000001262146621700144215ustar00rootroot00000000000000pg_partman-2.2.2/doc/pg_partman.md000066400000000000000000002174121262146621700171020ustar00rootroot00000000000000PostgreSQL Partition Manager Extension (`pg_partman`) ===================================================== About ----- PostgreSQL Partition Manager is an extension to help make managing time or serial id based table partitioning easier. It has many options, but usually only a few are needed, so it's much easier to use than it may seem (and definitely easier than implementing it yourself). Currenly the trigger functions only handle inserts to the parent table. Updates that would move a value from one partition to another are not yet supported. Some features of this extension have been expanded upon in the author's blog - http://www.keithf4.com/tag/pg_partman If you attempt to insert data into a partition set that contains data for a partition that does not exist, that data will be placed into the set's parent table. This is preferred over automatically creating new partitions to match that data since a mistake that is causing non-partitioned data to be inserted could cause a lot of unwanted child tables to be made. The `check_parent()` function provides monitoring for any data getting inserted into parents and the `partition_data_`* set of functions can easily partition that data for you if it is valid data. That is much easier than having to clean up potentially hundreds or thousands of unwanted partitions. And also better than throwing an error and losing the data! Note that future child table creation is based on the data currently in the partition set for both time & serial partitioning. This means that if you put "future" data into a partition set, newly created tables will be based off that value. This may cause intervening data to go to the parent as stated above if no child table exists. It is recommended that you set the `premake` value high enough to encompass your expected data range being inserted and the `optimize_trigger` value to efficiently handle your most frequent data range. See below for further explanations on these configuration values. ### Child Table Property Inheritance For this extension, most of the attributes of the child partitions are all obtained from the original parent. This includes defaults, indexes (primary keys, unique, etc), foreign keys (optional), tablespace, constraints, privileges & ownership. This also includes the OID and UNLOGGED table properties. For managing privileges, whenever a new partition is created it will obtain its privilege & ownership information from what the parent has at that time. Previous partition privileges are not changed. If previous partitions require that their privileges be updated, a separate function is available. This is kept as a separate process due to being an expensive operation when the partition set grows larger. The defaults, indexes, tablespace & constraints on the parent are only applied to newly created partitions and are not retroactively set on ones that already existed. While you would not normally create indexes on the parent of a partition set, doing so makes it much easier to manage in this case. There will be no data in the parent table (if everything is working right), so they will not take up any space or have any impact on system performance. Using the parent table as a control to the details of the child tables like this gives a better place to manage things that's a little more natural than a configuration table or using setup functions. ### Sub-partitioning Sub-partitioning with multiple levels is supported. You can do time->time, id->id, time->id and id->time. There is no set limit on the level of subpartitioning you can do, but be sensible and keep in mind performance considerations on managing many tables in a single inheritance set. Also, if the number of tables in a single partition set gets very high, you may have to adjust the `max_locks_per_transaction` postgresql.conf setting above the default of 64. Otherwise you may run into shared memory issues or even crash the cluster. By default all subpartition sets require `run_maintenance()` for the creation of new partitions. Single level time-based partition sets already do this, but single level serial sets do not. If you have contention issues when `run_maintenance()` is called for general maintenance of all partition sets, you can set the **`use_run_maintenance`** column in the **`part_config`** table to false if you do not want that general call to manage your subpartition set. But you must then call `run_maintenance(parent_table)` directly, and often enough, to have to future partitions made. See the `create_parent_sub()` & `run_maintenance()` functions below for more information. ### Retention If you don't need to keep data in older partitions, a retention system is available to automatically drop unneeded child partitions. By default, they are only uninherited not actually dropped, but that can be configured if desired. If the old partitions are kept, dropping their indexes can also be configured to recover disk space. Note that this will also remove any primary key or unique constraints in order to allow the indexes to be dropped. There is also a method available to dump the tables out if they don't need to be in the database anymore but still need to be kept. To set the retention policy, enter either an interval or integer value into the **retention** column of the **part_config** table. For time-based partitioning, the interval value will set that any partitions containing only data older than that will be dropped. For id-based partitioning, the integer value will set that any partitions with an id value less than the current maximum id value minus the retention value will be dropped. For example, if the current max id is 100 and the retention value is 30, any partitions with id values less than 70 will be dropped. The current maximum id value at the time the drop function is run is always used. Keep in mind that for subpartition sets, when a parent table has a child dropped, if that child table is in turn partitioned, the drop is a CASCADE and ALL child tables down the entire inheritance tree will be dropped. ### Constraint Exclusion One of the big advantages of partitioning is a feature called **constraint exclusion** (see docs for explanation of functionality and examples http://www.postgresql.org/docs/current/static/ddl-partitioning.html#DDL-PARTITIONING-CONSTRAINT-EXCLUSION). The problem with most partitioning setups however, is that this will only be used on the partitioning control column. If you use a WHERE condition on any other column in the partition set, a scan across all child tables will occur unless there are also constraints on those columns. And predicting what a column's values will be to precreate constraints can be very hard or impossible. `pg_partman` has a feature to apply constraints on older tables in a partition set that may no longer have any edits done to them ("old" being defined as older than the `optimize_constraint` config value). It checks the current min/max values in the given columns and then applies a constraint to that child table. This can allow the constraint exclusion feature to potentially eliminate scanning older child tables when other columns are used in WHERE conditions. Be aware that this limits being able to edit those columns, but for the situations where it is applicable it can have a tremendous affect on query performance for very large partition sets. So if you are only inserting new data this can be very useful, but if data is regularly being inserted throughout the entire partition set, this is of limited use. Functions for easily recreating constraints are also available if data does end up having to be edited in those older partitions. Note that constraints managed by PG Partman SHOULD NOT be renamed in order to allow the extension to manage them properly for you. For a better example of how this works, please see this blog post: http://www.keithf4.com/managing-constraint-exclusion-in-table-partitioning NOTE: This may not work with sub-partitioning. It will work on the first level of partitioning, but is not guarenteed to work properly on further sub-partition sets depending on the interval combinations and the optimize_constraint value. Ex: Weekly -> Daily with a daily optimize_constraint of 7 won't work as expected. Weekly constraints will get created but daily sub-partition ones likely will not. ### Custom Time Interval Considerations 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). When first running `create_parent()` to create a partition set, intervals less than a day round down when determining what the first partition to create will be. Intervals less than 24 hours but greater than 1 minute use the nearest hour rounded down. Intervals less than 1 minute use the nearest minute rounded down. However, enough partitions will be made to support up to what the real current time is. This means that when `create_parent()` is run, more previous partitions may be made than expected and all future partitions may not be made. The first run of `run_maintenance()` will fix the missing future partitions. This happens due to the nature of being able to support custom time intervals. Any intervals greater than or equal to 24 hours should set things up as would be expected. Keep in mind that for intervals equal to or greater than 100 years, the extension will use the real start of the century or millennium to determine the partition name & constraint rules. For example, the 21st century and 3rd millennium started January 1, 2001 (not 2000). This also means there is no year "0". It's much too difficult to try to work around this and make nice "even" partition names & rules to handle all possible time periods people may need. Blame the Gregorian creators. ### Naming Length Limits PostgreSQL has an object naming length limit of 63 characters. If you try and create an object with a longer name, it truncates off any characters at the end to fit that limit. This can cause obvious issues with partition names that rely on having a specifically named suffix. PG Partman automatically handles this for all child tables, trigger functions and triggers. It will truncate off the existing parent table name to fit the required suffix. Be aware that if you have tables with very long, similar names, you may run into naming conflicts if they are part of separate partition sets. With serial based partitioning, be aware that over time the table name will be truncated more and more to fit a longer partition suffix. So while the extention will try and handle this edge case for you, it is recommended to keep table names that will be partitioned as short as possible. ### Unique Constraints Table inheritance in PostgreSQL does not allow a primary key or unique index/constraint on the parent to apply to all child tables. The constraint is applied to each individual table, but not on the entire partition set as a whole. For example, this means a careless application can cause a primary key value to be duplicated in a partition set. This is one of the "big issues" that causes performance issues with partitoning on other database systems and one of the reasons for the delay in getting partitioning built in to PostgreSQL. In the mean time, a python script is included with `pg_partman` that can provide monitoring to help ensure the lack of this feature doesn't cause long term harm. See **`check_unique_constraint.py`** in the **Scripts** section. ### Logging/Monitoring The PG Jobmon extension (https://github.com/omniti-labs/pg_jobmon) is optional and allows auditing and monitoring of partition maintenance. If jobmon is installed and configured properly, it will automatically be used by partman with no additional setup needed. Jobmon can also be turned on or off individually for each partition set by using the `jobmon` column in the **`part_config`** table or with the option to `create_parent()` during initial setup. Note that if you try to partition `pg_jobmon`'s tables you **MUST** set the option in `create_parent()` to false, otherwise it will be put into a permanent lockwait since `pg_jobmon` will be trying to write to the table it's trying to partition. By default, any function that fails to run successfully 3 consecutive times will cause jobmon to raise an alert. This is why the default pre-make value is set to 4 so that an alert will be raised in time for intervention with no additional configuration of jobmon needed. You can of course configure jobmon to alert before (or later) than 3 failures if needed. If you're running partman in a production environment it is HIGHLY recommended to have jobmon installed and some sort of 3rd-party monitoring configured with it to alert when partitioning fails (Nagios, Circonus, etc). Background Worker ----------------- With PostgreSQL 9.4, the ability to create custom background workers and dynamically load them during runtime was introduced. `pg_partman`'s BGW is basically just a scheduler that runs the `run_maintenance()` function for you so that you don't have to use an external scheduler (cron, etc). Right now it doesn't do anything differently than calling `run_maintenance()` directly, but that may change in the future. See the README.md file for installation instructions. If you need to call `run_maintenance()` directly on any specific partition sets, you will still need to do so manually using an outside scheduler. This only maintains partition sets that have `use_run_maintenance` in `**part_config**` set to true. LOG messages are output to the normal PostgreSQL log file to indicate when the BGW runs. Additional logging messages are available if *log_min_messages* is set to "DEBUG1". The following configuration options are available to add into postgresql.conf to control the BGW process: - `pg_partman_bgw.dbname` - Required. The database(s) that `run_maintenance()` will run on. If more than one, use a comma separated list. If not set, BGW will do nothing. - `pg_partman_bgw.interval` - Number of seconds between calls to `run_maintenance()`. Default is 3600 (1 hour). - See further documenation below on suggested values for this based on partition types & intervals used. - `pg_partman_bgw.role` - The role that `run_maintenance()` will run as. Default is "postgres". Only a single role name is allowed. - `pg_partman_bgw.analyze` - Same purpose as the p_analyze argument to `run_maintenance()`. See below for more detail. Set to 'on' for TRUE. Set to 'off' for FALSE. Default is 'on'. - `pg_partman_bgw.jobmon` - Same purpose as the p_jobmon argument to `run_maintenance()`. See below for more detail. Set to 'on' for TRUE. Set to 'off' for FALSE. Default is 'on'. Extension Objects ----------------- A superuser must be used to run all these functions in order to set privileges & ownership properly in all cases. All are set with SECURITY DEFINER, so if you cannot have a superuser running them just assign a superuser role as the owner. As a note for people that were not aware, you can name arguments in function calls to make calling them easier and avoid confusion when there are many possible arguments. If a value has a default listed, it is not required to pass a value to that argument. As an example: `SELECT create_parent('schema.table', 'col1', 'time', 'daily', p_start_partition := '2015-10-20');` ### Creation Functions *`create_parent(p_parent_table text, p_control text, p_type text, p_interval text, p_constraint_cols text[] DEFAULT NULL, p_premake int DEFAULT 4, p_use_run_maintenance boolean DEFAULT NULL, p_start_partition text DEFAULT NULL, p_inherit_fk boolean DEFAULT true, p_epoch boolean DEFAULT false, p_jobmon boolean DEFAULT true, p_debug boolean DEFAULT false) RETURNS boolean`* * Main function to create a partition set with one parent table and inherited children. Parent table must already exist. Please apply all defaults, indexes, constraints, privileges & ownership to parent table so they will propagate to children. * An ACCESS EXCLUSIVE lock is taken on the parent table during the running of this function. No data is moved when running this function, so lock should be brief. * p_parent_table - the existing parent table. MUST be schema qualified, even if in public schema. * p_control - the column that the partitioning will be based on. Must be a time or integer based column. * p_type - one of the following values to set the partitioning type that will be used: + **time** - Create a time-based partition set using a predefined interval below. - The number of partitions most efficiently managed behind and ahead of the current one is determined by the **optimize_trigger** config value in the part_config table (default of 4 means data for 4 previous and 4 future partitions are handled best). - *Beware setting the optimize_trigger value too high as that will lessen the efficiency boost*. - Inserts to the parent table outside the optimize_trigger window will go to the proper child table if it exists, but performance will be degraded due to the higher overhead of handling that condition. - If the child table does not exist for that time value, the row will go to the parent. - Child table creation & trigger function is kept up to date by `run_maintenance()` function. + **time-custom** - Allows use of any time interval instead of the predefined ones below. Works the same as "time". - Note this method uses a lookup table as well as a dynamic insert statement, so performance will not be as good as the predefined intervals. So, while it is more flexible, it sacrifices speed. - Child table creation is kept up to date by `run_maintenance()` function. + **id** - Create a serial/id-based partition set. Same functionality & use of optimize_trigger value as the "time" method. - By default, when the id value reaches 50% of the max value for that partition, it will automatically create the next partition in sequence if it doesn't yet exist. This can be changed to use `run_maintenance()` instead. See the notes for this function below. - Note that the 50% rule is NOT true if the id set is sub-partitioned. Then `run_maintenance()` must be used. - Only supports id values greater than or equal to zero. * `p_interval` - the time or numeric range interval for each partition. No matter the partitioning type, value must be given as text. The generic intervals of "yearly -> quarter-hour" are for the "time" type and allow better performance than using an arbitrary time interval (time-custom). + *yearly* - One partition per year + *quarterly* - One partition per yearly quarter. Partitions are named as YYYYqQ (ex: 2012q4) + *monthly* - One partition per month + *weekly* - One partition per week. Follows ISO week date format (http://en.wikipedia.org/wiki/ISO_week_date). Partitions are named as IYYYwIW (ex: 2012w36) + *daily* - One partition per day + *hourly* - One partition per hour + *half-hour* - One partition per 30 minute interval on the half-hour (1200, 1230) + *quarter-hour* - One partition per 15 minute interval on the quarter-hour (1200, 1215, 1230, 1245) + *\* - For the time-custom partitioning type, this can be any interval value that is valid for the PostgreSQL interval data type. Do not type cast the parameter value, just leave as text. + *\* - For ID based partitions, the integer value range of the ID that should be set per partition. Enter this as an integer in text format ('100' not 100). Must be greater than or equal to 10. * `p_constraint_cols` - an optional array parameter to set the columns that will have additional constraints set. See the **About** section above for more information on how this works and the **apply_constraints()** function for how this is used. * `p_premake` - is how many additional partitions to always stay ahead of the current partition. Default value is 4. This will keep at minimum 5 partitions made, including the current one. For example, if today was Sept 6th, and `premake` was set to 4 for a daily partition, then partitions would be made for the 6th as well as the 7th, 8th, 9th and 10th. Note some intervals may occasionally cause an extra partition to be premade or one to be missed due to leap years, differing month lengths, daylight savings (on non-UTC systems), etc. This won't hurt anything and will self-correct. If partitioning ever falls behind the `premake` value, normal running of `run_maintenance()` and data insertion to id-based tables should automatically catch things up. * `p_use_run_maintenance` - Used to tell partman whether you'd like to override the default way that child partitions are created. Set this value to TRUE to allow you to use the `run_maintenance()` function, without any table paramter, to create new child tables for serial partitioning instead of using 50% method mentioned above. Time based partitining MUST use `run_maintenance()`, so either leave this value true or call the `run_maintenance()` function directly on a partition set by passing the parent table as a parameter. See **run_mainteanance** in Maintenance Functions section below for more info. * `p_start_partition` - allows the first partition of a set to be specified instead of it being automatically determined. 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. * `p_inherit_fk` - allows `pg_partman` to automatically manage inheriting any foreign keys that exist on the parent table to all its children. Defaults to TRUE. * `p_epoch` - tells `pg_partman` that the control column is an integer type, but actually represents and epoch time value. All triggers, constraints & table names will be time-based. * `p_jobmon` - allow `pg_partman` to use the `pg_jobmon` extension to monitor that partitioning is working correctly. Defaults to TRUE. * `p_debug` - turns on additional debugging information. *`create_sub_parent(p_top_parent text, p_control text, p_type text, p_interval text, 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 boolean DEFAULT false, p_jobmon boolean DEFAULT true, p_debug boolean DEFAULT false) RETURNS boolean;`* * Create a subpartition set of an already existing partitioned set. * `p_top_parent` - This parameter is the parent table of an already existing partition set. It tells `pg_partman` to turn all child tables of the given partition set into their own parent tables of their own partition sets using the rest of the parameters for this function. * All other parameters to this function have the same exact purpose as those of `create_parent()`, but instead are used to tell `pg_partman` how each child table shall itself be partitioned. * For example if you have an existing partition set done by year and you then want to partition each of the year partitions by day, you would use this function. * It is advised that you keep table names short for subpartition sets if you plan on relying on the table names for organization. The suffix added on to the end of a table name is always guarenteed to be there for whatever partition type is active for that set, but if the total length is longer than 63 chars, the original name will get truncated. Longer table names may cause the original parent table names to be truncated and possibly cut off the top level partitioning suffix. I cannot control this and made the requirement that the lowest level partitioning suffix survives. * Note that for the first level of subpartitions, the `p_parent_table` argument you originally gave to `create_parent()` would be the exact same value you give to `create_sub_parent()`. If you need further subpartitioning, you would then start giving `create_sub_parent()` a different value (the child tables of the top level partition set). * Note that for ID sub-partitioning, future partition maintenance must be done with run_maintenace() and does not use the 50% rule mentioned above. *`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') RETURNS bigint`* * This function is used to partition data that may have existed prior to setting up the parent table as a time-based partition set, or to fix data that accidentally gets inserted into the parent. * If the needed partition does not exist, it will automatically be created. If the needed partition already exists, the data will be moved there. * If you are trying to partition a large amount of data automatically, it is recommended to run this function with an external script and appropriate batch settings. This will help avoid transactional locks and prevent a failure from causing an extensive rollback. See **Scripts** section for an included python script that will do this for you. * For sub-partitioned sets, you must start partitioning data at the highest level and work your way down each level. All data will not automatically go to the lowest level when run from the top for a sub-partitioned set. * `p_parent_table` - the existing parent table. This is assumed to be where the unpartitioned data is located. MUST be schema qualified, even if in public schema. * `p_batch_interval` - optional argument, a time interval of how much of the data to move. This can be smaller than the partition interval, allowing for very large sized partitions to be broken up into smaller commit batches. Defaults to the configured partition interval if not given or if you give an interval larger than the partition interval. * `p_batch_count` - optional argument, how many times to run the `batch_interval` in a single call of this function. Default value is 1. * `p_lock_wait` - optional argument, sets how long in seconds to wait for a row to be unlocked before timing out. Default is to wait forever. * `p_order` - optional argument, by default data is migrated out of the parent in ascending order (ASC). Allows you to change to descending order (DESC). * Returns the number of rows that were moved from the parent table to partitions. Returns zero when parent table is empty and partitioning is complete. *`partition_data_id(p_parent_table text, p_batch_count int DEFAULT 1, p_batch_interval int DEFAULT NULL, p_lock_wait numeric DEFAULT 0, p_order text DEFAULT 'ASC') RETURNS bigint`* * This function is used to partition data that may have existed prior to setting up the parent table as a serial id partition set, or to fix data that accidentally gets inserted into the parent. * If the needed partition does not exist, it will automatically be created. If the needed partition already exists, the data will be moved there. * If you are trying to partition a large amount of data automatically, it is recommended to run this function with an external script and appropriate batch settings. This will help avoid transactional locks and prevent a failure from causing an extensive rollback. See **Scripts** section for an included python script that will do this for you. * For sub-partitioned sets, you must start partitioning data at the highest level and work your way down each level. All data will not automatically go to the lowest level when run from the top for a sub-partitioned set. * `p_parent_table` - the existing parent table. This is assumed to be where the unpartitioned data is located. MUST be schema qualified, even if in public schema. * `p_batch_interval` - optional argument, an integer amount representing an interval of how much of the data to move. This can be smaller than the partition interval, allowing for very large sized partitions to be broken up into smaller commit batches. Defaults to the configured partition interval if not given or if you give an interval larger than the partition interval. * `p_batch_count` - optional argument, how many times to run the `batch_interval` in a single call of this function. Default value is 1. * `p_lock_wait` - optional argument, sets how long in seconds to wait for a row to be unlocked before timing out. Default is to wait forever. * `p_order` - optional argument, by default data is migrated out of the parent in ascending order (ASC). Allows you to change to descending order (DESC). * Returns the number of rows that were moved from the parent table to partitions. Returns zero when parent table is empty and partitioning is complete. ### Maintenance Functions *`run_maintenance(p_parent_table text DEFAULT NULL, p_analyze boolean DEFAULT true, p_jobmon boolean DEFAULT true, p_debug boolean DEFAULT false) RETURNS void`* * Run this function as a scheduled job (cron, etc) to automatically create child tables for partition sets configured to use it. * You can also use the included background worker (BGW) to have this automatically run for you by PostgreSQL itself. Note that the `p_parent_table` parameter is not available with this method, so if you need to run it for a specific partition set, you must do that manually or scheduled as noted above. The other parameters have postgresql.conf values that can be set. See BGW section above. * This function also maintains the partition retention system for any partitions sets that have it turned on. * Every run checks for all tables listed in the **part_config** table with **use_run_maintenance** set to true and either creates new partitions for them or runs their retention policy. * By default, time-based partition sets and all sub-partition sets have use_run_maintenance set to true. This function is required to be run to maintain time-based partitioning & sub-partiton sets. * By default, serial-based partition sets have use_run_maintenance set to false (except if they are sub-partitioned) and don't require `run_maintenance()` for partition maintenance, but can be overridden to do so. By default serial partitioning creates new partitions when the current one reaches 50% of it's max capacity. This can cause contention on very high transaction tables. If configured to use `run_maintenance()` for serial partitioning, you must call it often enough to keep partition creation ahead of your insertion rate, otherwise data will go into the parent. * New partitions are only created if the number of child tables ahead of the current one is less than the premake value, so you can run this more often than needed without fear of needlessly creating more partitions. * Every run checks all tables of all types listed in the **part_config** table with a value in the **retention** column and drops tables as needed (see **About** and config table below). * Will automatically update the function for **time** partitioning (and **id** if configured) to keep the parent table pointing at the correct partitions. When using time, run this function more often than the partitioning interval to keep the trigger function running its most efficient. For example, if using quarter-hour, run every 5 minutes; if using daily, run at least twice a day, etc. * `p_parent_table` - an optional parameter that if passed will cause `run_maintenance()` to be run for ONLY that given table. This occurs no matter what `use_run_maintenance` in part_config is set to. High transcation rate tables can cause contention when maintenance is being run for many tables at the same time, so this allows finer control of when partition maintenance is run for specific tables. Note that this will also cause the retention system to only be run for the given table as well. * `p_analyze` - By default when a new child table is created, an analyze is run on the parent to ensure statistics are updated for constraint exclusion. However, for large partition sets, this analyze can take a while and if `run_maintenance()` is managing several partitions in a single run, this can cause contention while the analyze finishes. Set this to false to disable the analyze run and avoid this contention. Please note that you must then schedule an analyze of the parent table at some point for constraint exclusion to work properly on all child tables. * `p_jobmon` - an optional paramter to control whether `run_maintenance()` itself uses the `pg_jobmon` extension to log what it does. Whether the maintenance of a particular table uses `pg_jobmon` is controlled by the setting in the **part_config** table and this setting will have no affect on that. Defaults to true if not set. * `p_debug` - Output additional notices to help with debugging problems or to more closely examine what is being done during the run. *`show_partitions (p_parent_table text, p_order text DEFAULT 'ASC')`* * List all child tables of a given partition set. Each child table returned as a single row. * Tables are returned in the order that makes sense for the partition interval, not by the locale ordering of their names. * `p_order` - optional parameter to set the order the child tables are returned in. Defaults to ASCending. Set to 'DESC' to return in descending order. *`show_partition_name(p_parent_table text, p_value text, OUT partition_table text, OUT suffix_timestamp timestamp, OUT suffix_id bigint, OUT table_exists boolean)`* * Given a parent table managed by pg_partman (p_parent_table) and an appropriate value (time or id but given in text form for p_value), return the name of the child partition that that value would exist in. * If using epoch time partitioning, give the timestamp value, NOT the integer epoch value (use to_timestamp() to convert an epoch value). * Returns a child table name whether the child table actually exists or not * Also returns a raw value (suffix_timestamp or suffix_id) for the partition suffix for the given child table * Also returns a boolean value (table_exists) to say whether that child table actually exists *`check_parent()`* * Run this function to monitor that the parent tables of the partition sets that `pg_partman` manages do not get rows inserted to them. * Returns a row for each parent table along with the number of rows it contains. Returns zero rows if none found. * `partition_data_time()` & `partition_data_id()` can be used to move data from these parent tables into the proper children. *`apply_constraints(p_parent_table text, p_child_table text DEFAULT NULL, p_job_id bigint DEFAULT NULL, p_debug BOOLEAN DEFAULT FALSE)`* * Apply constraints to child tables in a given partition set for the columns that are configured (constraint names are all prefixed with "partmanconstr_"). * Note that this does not need to be called manually to maintain custom constraints. The creation of new partitions automatically manages adding constraints to old child tables. * Columns that are to have constraints are set in the **part_config** table **constraint_cols** array column or during creation with the parameter to `create_parent()`. * If the `pg_partman` constraints already exists on the child table, the function will cleanly skip over the ones that exist and not create duplicates. * If the column(s) given contain all NULL values, no constraint will be made. * If the child table parameter is given, only that child table will have constraints applied. * If the p_child_table parameter is not given, constraints are placed on the last child table older than the `optimize_constraint` value. For example, if the optimize_constraint value is 30, then constraints will be placed on the child table that is 31 back from the current partition (as long as partition pre-creation has been kept up to date). * If you need to apply constraints to all older child tables, use the included python script (reapply_constraint.py). This script has options to make constraint application easier with as little impact on performance as possible. * The p_job_id parameter is optional. It's for internal use and allows job logging to be consolidated into the original job that called this function if applicable. * The p_debug parameter will show you the constraint creation statement that was used. *`drop_constraints(p_parent_table text, p_child_table text, p_debug boolean DEFAULT false)`* * Drop constraints that have been created by `pg_partman` for the columns that are configured in *part_config*. This makes it easy to clean up constraints if old data needs to be edited and the constraints aren't allowing it. * Will only drop constraints that begin with `partmanconstr_*` for the given child table and configured columns. * If you need to drop constraints on all child tables, use the included python script (`reapply_constraint.py`). This script has options to make constraint removal easier with as little impact on performance as possible. * The debug parameter will show you the constraint drop statement that was used. *`reapply_privileges(p_parent_table text)`* * This function is used to reapply ownership & grants on all child tables based on what the parent table has set. * Privileges that the parent table has will be granted to all child tables and privilges that the parent does not have will be revoked (with CASCADE). * Privilges that are checked for are SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, & TRIGGER. * Be aware that for large partition sets, this can be a very long running operation and is why it was made into a separate function to run independently. Only privileges that are different between the parent & child are applied, but it still has to do system catalog lookups and comparisons for every single child partition and all individual privileges on each. * `p_parent_table` - parent table of the partition set. Must be schema qualified and match a parent table name already configured in `pg_partman`. *`apply_foreign_keys(p_parent_table text, p_child_table text DEFAULT NULL, p_job_id bigint DEFAULT NULL, p_debug boolean DEFAULT false)`* * Applies any foreign keys that exist on a parent table in a partition set to all the child tables. * This function is automatically called whenever a new child table is created, so there is no need to manually run it unless you need to fix an existing child table. * If you need to apply this to an entire partition set, see the **reapply_foreign_keys.py** python script. This will commit after every FK creation to avoid contention. * This function can be used on any table inheritance set, not just ones managed by `pg_partman`. * The p_job_id parameter is optional. It's for internal use and allows job logging to be consolidated into the original job that called this function if applicable. * The p_debug parameter will show you the constraint creation statement that was used. ### Destruction Functions *`undo_partition_time(p_parent_table text, p_batch_count int DEFAULT 1, p_batch_interval interval DEFAULT NULL, p_keep_table boolean DEFAULT true, p_lock_wait numeric DEFAULT 0) RETURNS bigint`* * Undo a time-based partition set created by `pg_partman`. This function MOVES the data from existing child partitions to the parent table. * When this function is run, the trigger on the parent table & the trigger function are immediately dropped (if they still exist). This means any further writes are done to the parent. * When this function is run, the **`undo_in_progress`** column in the configuration table is set to true. This causes all partition creation and retention management to stop. * If you are trying to un-partition a large amount of data automatically, it is recommended to run this function with an external script and appropriate batch settings. This will help avoid transactional locks and prevent a failure from causing an extensive rollback. See **Scripts** section for an included python script that will do this for you. * By default, partitions are not DROPPED, they are UNINHERITED. This leave previous child tables as empty, independent tables. * Without setting either batch argument manually, each run of the function will move all the data from a single partition into the parent. * Once all child tables have been uninherited/dropped, the configuration data is removed from `pg_partman` automatically. * For subpartitioned tables, you must start at the lowest level parent table and undo from there then work your way up. If you attempt to undo partitioning on a subpartition set, the function will stop with a warning to let you know. * `p_parent_table` - parent table of the partition set. Must be schema qualified and match a parent table name already configured in `pg_partman`. * `p_batch_count` - an optional argument, this sets how many times to move the amount of data equal to the `p_batch_interval` argument (or default partition interval if not set) in a single run of the function. Defaults to 1. * `p_batch_interval` - an optional argument, a time interval of how much of the data to move. This can be smaller than the partition interval, allowing for very large sized partitions to be broken up into smaller commit batches. Defaults to the configured partition interval if not given or if you give an interval larger than the partition interval. * `p_keep_table` - an optional argument, setting this to false will cause the old child table to be dropped instead of uninherited after all of its data has been moved. Note that it takes at least two batches to actually uninherit/drop a table from the set. * `p_lock_wait` - optional argument, sets how long in seconds to wait for either the table or a row to be unlocked before timing out. Default is to wait forever. * Returns the number of rows moved to the parent table. Returns zero when all child tables are empty. *`undo_partition_id(p_parent_table text, p_batch_count int DEFAULT 1, p_batch_interval bigint DEFAULT NULL, p_keep_table boolean DEFAULT true, p_lock_wait numeric DEFAULT 0) RETURNS bigint`* * Undo an id-based partition set created by `pg_partman`. This function MOVES the data from existing child partitions to the parent table. * When this function is run, the trigger on the parent table & the trigger function are immediately dropped (if they still exist). This means any further writes are done to the parent. * When this function is run, the **`undo_in_progress`** column in the configuration table is set to true. This causes all partition creation and retention management to stop. * If you are trying to un-partition a large amount of data automatically, it is recommended to run this function with an external script and appropriate batch settings. This will help avoid transactional locks and prevent a failure from causing an extensive rollback. See **Scripts** section for an included python script that will do this for you. * By default, partitions are not DROPPED, they are UNINHERITED. This leave previous child tables as empty, independent tables. * Without setting either batch argument manually, each run of the function will move all the data from a single partition into the parent. * Once all child tables have been uninherited/dropped, the configuration data is removed from `pg_partman` automatically. * For subpartitioned tables, you must start at the lowest level parent table and undo from there then work your way up. If you attempt to undo partitioning on a subpartition set, the function will stop with a warning to let you know. * `p_parent_table` - parent table of the partition set. Must be schema qualified and match a parent table name already configured in `pg_partman`. * `p_batch_count` - an optional argument, this sets how many times to move the amount of data equal to the `p_batch_interval` argument (or default partition interval if not set) in a single run of the function. Defaults to 1. * `p_batch_interval` - an optional argument, an integer amount representing an interval of how much of the data to move. This can be smaller than the partition interval, allowing for very large sized partitions to be broken up into smaller commit batches. Defaults to the configured partition interval if not given or if you give an interval larger than the partition interval. * `p_keep_table` - an optional argument, setting this to false will cause the old child table to be dropped instead of uninherited after all of it's data has been moved. Note that it takes at least two batches to actually uninherit/drop a table from the set (second batch sees it has no more data and drops it). * `p_lock_wait` - optional argument, sets how long in seconds to wait for either the table or a row to be unlocked before timing out. Default is to wait forever. * Returns the number of rows moved to the parent table. Returns zero when all child tables are empty. *`undo_partition(p_parent_table text, p_batch_count int DEFAULT 1, p_keep_table boolean DEFAULT true, p_jobmon boolean DEFAULT true, p_lock_wait numeric DEFAULT 0) RETURNS bigint`* * Undo the parent/child table inheritance of any partition set, not just ones managed by `pg_partman`. This function COPIES the data from existing child partitions to the parent table. * WARNING: If used on a sub-partitioned set not managed by `pg_partman`, results could be unpredictable. It is not recommended to do so. * If you need to keep the data in your child tables after it is put into the parent, use this function. * Unlike the other undo functions, data cannot be copied in batches smaller than the partition interval. Every run of the function copies an entire partition to the parent. * When this function is run, the **`undo_in_progress`** column in the configuration table is set to true if it was managed by pg_partman. This causes all partition creation and retention management to stop ONLY if it was managed by pg_partman. * If you are trying to un-partition a large amount of data automatically, it is recommended to run this function with an external script and appropriate batch settings. This will help avoid transactional locks and prevent a failure from causing an extensive rollback. See **Scripts** section for an included python script that will do this for you. * By default, partitions are not DROPPED, they are UNINHERITED. This leave previous child tables exactly as they were but no longer inherited from the parent. Does not work on multiple levels of inheritance (subpartitions) if dropping tables. * `p_parent_table` - parent table of the partition set. Must be schema qualified but does NOT have to be managed by `pg_partman`. * `p_batch_count` - an optional argument, this sets how many partitions to copy data from in a single run. Defaults to 1. * `p_keep_table` - an optional argument, setting this to false will cause the old child table to be dropped instead of uninherited. * `p_jobmon` - an optional paramter to stop undo_partition() from using the `pg_jobmon` extension to log what it does. Defaults to true if not set. * `p_lock_wait` - optional argument, sets how long in seconds to wait for either the table or a row to be unlocked before timing out. Default is to wait forever. * Returns the number of rows moved to the parent table. Returns zero when child tables are all empty. *`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) RETURNS int`* * This function is used to drop child tables from a time-based partition set. By default, the table is just uninherited and not actually dropped. For automatically dropping old tables, it is recommended to use the `run_maintenance()` function with retention configured instead of calling this directly. * `p_parent_table` - the existing parent table of a time-based partition set. MUST be schema qualified, even if in public schema. * `p_retention` - optional parameter to give a retention time interval and immediately drop tables containing only data older than the given interval. If you have a retention value set in the config table already, the function will use that, otherwise this will override it. If not, this parameter is required. See the **About** section above for more information on retention settings. * `p_keep_table` - optional parameter to tell partman whether to keep or drop the table in addition to uninheriting it. TRUE means the table will not actually be dropped; FALSE means the table will be dropped. This function will just use the value configured in **part_config** if not explicitly set. This option is ignored if retention_schema is set. * `p_keep_index` - optional parameter to tell partman whether to keep or drop the indexes of the child table when it is uninherited. TRUE means the indexes will be kept; FALSE means all indexes will be dropped. This function will just use the value configured in **part_config** if not explicitly set. This option is ignored if p_keep_table is set to FALSE or if retention_schema is set. * `p_retention_schema` - optional parameter to tell partman to move a table to another schema instead of dropping it. Set this to the schema you want the table moved to. This function will just use the value configured in **`part_config`** if not explicitly set. If this option is set, the retention `p_keep_table` & `p_keep_index` parameters are ignored. * Returns the number of partitions affected. *`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`* * This function is used to drop child tables from an id-based partition set. By default, the table just uninherited and not actually dropped. For automatically dropping old tables, it is recommended to use the `run_maintenance()` function with retention configured instead of calling this directly. * `p_parent_table` - the existing parent table of a time-based partition set. MUST be schema qualified, even if in public schema. * `p_retention` - optional parameter to give a retention integer interval and immediately drop tables containing only data less than the current maximum id value minus the given retention value. If you have a retention value set in the config table already, the function will use that, otherwise this will override it. If not, this parameter is required. See the **About** section above for more information on retention settings. * `p_keep_table` - optional parameter to tell partman whether to keep or drop the table in addition to uninheriting it. TRUE means the table will not actually be dropped; FALSE means the table will be dropped. This function will just use the value configured in **part_config** if not explicitly set. This option is ignored if retention_schema is set. * `p_keep_index` - optional parameter to tell partman whether to keep or drop the indexes of the child table when it is uninherited. TRUE means the indexes will be kept; FALSE means all indexes will be dropped. This function will just use the value configured in **part_config** if not explicitly set. This option is ignored if p_keep_table is set to FALSE or if retention_schema is set. * `p_retention_schema` - optional parameter to tell partman to move a table to another schema instead of dropping it. Set this to the schema you want the table moved to. This function will just use the value configured in **part_config** if not explicitly set. If this option is set, the retention p_keep_table & p_keep_index parameters are ignored. * Returns the number of partitions affected. ### Tables **`part_config`** Stores all configuration data for partition sets mananged by the extension. The only columns in this table that should ever need to be manually changed are: 1. **`retention`**, **`retention_schema`**, **`retention_keep_table`** & **`retention_keep_index`** to configure the partition set's retention policy 2. **`constraint_cols`** to have partman manage additional constraints and **`optimize_constraint`** to control when they're added 3. **`premake`**, **`optimize_trigger`**, **`inherit_fk`**, **`use_run_maintenance`** & **`jobmon`** to change the default behavior. The rest are managed by the extension itself and should not be changed unless absolutely necessary. - `parent_table` - Parent table of the partition set - `partition_type` - Type of partitioning. Must be one of the types mentioned above in the `create_parent()` info. - `partition_interval` - Text type value that determines the interval for each partition. - Must be a value that can either be cast to the interval or bigint data types. - `control` - Column used as the control for partition constraints. Must be a time or integer based column. - `constraint_cols` - Array column that lists columns to have additional constraints applied. See **About** section for more information on how this feature works. - `premake` - How many partitions to keep pre-made ahead of the current partition. Default is 4. - `optimize_trigger` - Manages number of partitions which are handled most efficiently by trigger. See `create_parent()` function for more info. Default 4. - `optimize_constraint` - Manages which old tables get additional constraints set if configured to do so. See **About** section for more info. Default 30. - `epoch` - Flag the table to be partitioned by time by an integer epoch value instead of a timestamp. See `create_parent()` function for more info. Default false. - `inherit_fk` - Set whether `pg_partman` manages inheriting foreign keys from the parent table to all children. - Defaults to TRUE. Can be set with the `create_parent()` function at creation time as well. - `retention` - Text type value that determines how old the data in a child partition can be before it is dropped. - Must be a value that can either be cast to the interval or bigint data types. - Leave this column NULL (the default) to always keep all child partitions. See **About** section for more info. - `retention_schema` - Schema to move tables to as part of the retentions system instead of dropping them. Overrides retention_keep_* options. - `retention_keep_table` - Boolean value to determine whether dropped child tables are kept or actually dropped. - Default is TRUE to keep the table and only uninherit it. Set to FALSE to have the child tables removed from the database completely. - `retention_keep_index` - Boolean value to determine whether indexes are dropped for child tables that are uninherited. - Default is TRUE. Set to FALSE to have the child table's indexes dropped when it is uninherited. - `datetime_string` - For time-based partitioning, this is the datetime format string used when naming child partitions. - `use_run_maintenance` - Boolean value that tells `run_maintenance()` function whether it should manage new child table creation automatically when `run_maintenance()` is called without a table parameter. - If `run_maintenance()` is given a table parameter, this option is ignored and maintenace will always run. - Defaults to TRUE for time-based partitioning. - Defaults to FALSE for single-level serial-based partitioning and can be changed to TRUE if desired. - If changing an existing serial partitioned set from FALSE to TRUE, you must run create_id_function('parent_schema.parent_table') to change the trigger function so it no longer creates new partitions. - Defaults to TRUE for all sub-partition tables - `jobmon` - Boolean value to determine whether the `pg_jobmon` extension is used to log/monitor partition maintenance. Defaults to true. - `sub_partition_set_full` - Boolean value to denote that the final partition for a sub-partition set has been created. Allows run_maintenance() to run more efficiently when there are large numbers of subpartition sets. - `undo_in_progress` - Set by the undo_partition functions whenever they are run. If true, this causes all partition creation and retention management by the `run_maintenance()` function to stop. Default is false. **`part_config_sub`** * Stores all configuration data for sub-partitioned sets managed by `pg_partman`. * The **`sub_parent`** column is the parent table of the subpartition set and all other columns govern how that parent's children are subpartitioned. * All columns except `sub_parent` work the same exact way as their counterparts in the **`part_config`** table. ### Scripts If the extension was installed using *make*, the below script files should have been installed to the PostgreSQL binary directory. *`partition_data.py`* * A python script to make partitioning in committed batches easier. * Calls either partition_data_time() or partition_data_id() depending on the value given for --type. * A commit is done at the end of each --interval and/or fully created partition. * Returns the total number of rows moved to partitions. Automatically stops when parent is empty. * To help avoid heavy load and contention during partitioning, autovacuum is turned off for the parent table and all child tables when this script is run. When partitioning is complete, autovacuum is set back to its default value and the parent table is vacuumed when it is emptied. * `--parent (-p)`: Parent table of an already created partition set. Required. * `--type (-t)`: Type of partitioning. Valid values are "time" and "id". Required. * `--connection (-c)`: Connection string for use by psycopg. Defaults to "host=" (local socket). * `--interval (-i)`: Value that is passed on to the partitioning function as `p_batch_interval` argument. Use this to set an interval smaller than the partition interval to commit data in smaller batches. Defaults to the partition interval if not given. * `--batch (-b)`: How many times to loop through the value given for --interval. If --interval not set, will use default partition interval and make at most -b partition(s). Script commits at the end of each individual batch. (NOT passed as p_batch_count to partitioning function). If not set, all data in the parent table will be partitioned in a single run of the script. * `--wait (-w)`: Cause the script to pause for a given number of seconds between commits (batches). * `--order (-o)`: Allows you to specify the order that data is migrated from the parent to the children, either ascending (ASC) or descending (DESC). Default is ASC. * `--lockwait (-l)`: Have a lock timeout of this many seconds on the data move. If a lock is not obtained, that batch will be tried again. * `--lockwait_tries`: Number of times to allow a lockwait to time out before giving up on the partitioning. Defaults to 10. * `--autovacuum_on`: Turning autovacuum off requires a brief lock to ALTER the table property. Set this option to leave autovacuum on and avoid the lock attempt. * `--quiet (-q)`: Switch setting to stop all output during and after partitioning. * `--version`: Print out the minimum version of `pg_partman` this script is meant to work with. The version of `pg_partman` installed may be greater than this. * `--debug` Show additional debugging output * Examples: ``` Partition all data in a parent table. Commit after each partition is made. python partition_data.py -c "host=localhost dbname=mydb" -p schema.parent_table -t time Partition by id in smaller intervals and pause between them for 5 seconds (assume >100 partition interval) python partition_data.py -p schema.parent_table -t id -i 100 -w 5 Partition by time in smaller intervals for at most 10 partitions in a single run (assume monthly partition interval) python partition_data.py -p schema.parent_table -t time -i "1 week" -b 10 ``` *`undo_partition.py`* * A python script to make undoing partitions in committed batches easier. * Can also work on any parent/child partition set not managed by `pg_partman` if --type option is not set. * This script calls either undo_partition(), undo_partition_time() or undo_partition_id depending on the value given for --type. * A commit is done at the end of each --interval and/or emptied partition. * Returns the total number of rows put into the to parent. Automatically stops when last child table is empty. * `--parent (-p)`: Parent table of the partition set. Required. * `--type (-t)`: Type of partitioning. Valid values are "time" and "id". Not setting this argument will use undo_partition() and work on any parent/child table set. * `--connection (-c)`: Connection string for use by psycopg. Defaults to "host=" (local socket). * `--interval (-i)`: Value that is passed on to the partitioning function as `p_batch_interval`. Use this to set an interval smaller than the partition interval to commit data in smaller batches. Defaults to the partition interval if not given. * `--batch (-b)`: How many times to loop through the value given for --interval. If --interval not set, will use default partition interval and undo at most -b partition(s). Script commits at the end of each individual batch. (NOT passed as p_batch_count to undo function). If not set, all data will be moved to the parent table in a single run of the script. * `--wait (-w)`: Cause the script to pause for a given number of seconds between commits (batches). * `--droptable (-d)`: Switch setting for whether to drop child tables when they are empty. Leave off option to just uninherit. * `--quiet (-q)`: Switch setting to stop all output during and after partitioning undo. * `--version`: Print out the minimum version of `pg_partman` this script is meant to work with. The version of `pg_partman` installed may be greater than this. * `--debug`: Show additional debugging output *`dump_partition.py`* * A python script to dump out tables contained in the given schema. Uses pg_dump, creates a SHA-512 hash file of the dump file, and then drops the table. * When combined with the retention_schema configuration option, provides a way to reliably dump out tables that would normally just be dropped by the retention system. * Tables are not dropped if pg_dump does not return successfully. * The connection options for psycopg and pg_dump were separated out due to distinct differences in their requirements depending on your database connection configuration. * All dump_* option defaults are the same as they would be for pg_dump if they are not given. * Will work on any given schema, not just the one used to manage `pg_partman` retention. * `--schema (-n)`: The schema that contains the tables that will be dumped. (Required). * `--connection (-c)`: Connection string for use by psycopg. Role used must be able to select from pg_catalog.pg_tables in the relevant database and drop all tables in the given schema. Defaults to "host=" (local socket). Note this is distinct from the parameters sent to pg_dump. * `--output (-o)`: Path to dump file output location. Default is where the script is run from. * `--dump_database (-d)`: Used for pg_dump, same as its --dbname option or final database name parameter. * `--dump_host`: Used for pg_dump, same as its --host option. * `--dump_username`: Used for pg_dump, same as its --username option. * `--dump_port`: Used for pg_dump, same as its --port option. * `--pg_dump_path`: Path to pg_dump binary location. Must set if not in current PATH. * `--Fp`: Dump using pg_dump plain text format. Default is binary custom (-Fc). * `--nohashfile`: Do NOT create a separate file with the SHA-512 hash of the dump. If dump files are very large, hash generation can possibly take a long time. * `--nodrop`: Do NOT drop the tables from the given schema after dumping/hashing. * `--verbose (-v)`: Provide more verbose output. * `--version`: Print out the minimum version of `pg_partman` this script is meant to work with. The version of `pg_partman` installed may be greater than this. *`reapply_indexes.py`* * A python script for reapplying indexes on child tables in a partition set after they are changed on the parent table. * Any indexes that currently exist on the children and match the definition on the parent will be left as is. There is an option to recreate matching as well indexes if desired, as well as the primary key. * Indexes that do not exist on the parent will be dropped from all children. * Commits are done after each index is dropped/created to help prevent long running transactions & locks. * NOTE: New index names are made based off the child table name & columns used, so their naming may differ from the name given on the parent. This is done to allow the tool to account for long or duplicate index names. If an index name would be duplicated, an incremental counter is added on to the end of the index name to allow it to be created. Use the --dryrun option first to see what it will do and which names may cause dupes to be handled like this. * `--parent (-p)`: Parent table of an already created partition set. Required. * `--connection (-c)`: Connection string for use by psycopg. Defaults to "host=" (local socket). * `--concurrent`: Create indexes with the CONCURRENTLY option. Note this does not work on primary keys when --primary is given. * `--drop_concurrent`: Drop indexes concurrently when recreating them (PostgreSQL >= v9.2). Note this does not work on primary keys when --primary is given. * `--recreate_all (-R)`: By default, if an index exists on a child and matches the parent, it will not be touched. Setting this option will force all child indexes to be dropped & recreated. Will obey the --concurrent & --drop_concurrent options if given. Will not recreate primary keys unless --primary option is also given. * `--primary`: By default the primary key is not recreated. Set this option if that is needed. Note this will cause an exclusive lock on the child table for the duration of the recreation. * `--jobs (-j)`: Use the python multiprocessing library to recreate indexes in parallel. Note that this is per table, not per index. Be very careful setting this option if load is a concern on your systems. * `--wait (-w)`: Wait the given number of seconds after indexes have finished being created on a table before moving on to the next. When used with -j, this will set the pause between the batches of parallel jobs instead. * `--dryrun`: Show what the script will do without actually running it against the database. Highly recommend reviewing this before running. Note that if multiple indexes would get the same default name, the duplicated names will show in the dryrun (because the index doesn't exist in the catalog to check for it). When the real thing is run, the duplicated names will be handled as stated in the NOTE above. * `--quiet`: Turn off all output. * `--nonpartman` If the partition set you are running this on is not managed by pg_partman, set this flag otherwise this script may not work. Note that the pg_partman extension is still required to be installed for this to work since it uses certain internal functions. When this is set the order that the tables are reindexed is alphabetical instead of logical. * `--version`: Print out the minimum version of `pg_partman` this script is meant to work with. The version of `pg_partman` installed may be greater than this. *`reapply_constraints.py`* * A python script for redoing constraints on child tables in a given partition set for the columns that are configured in **part_config** table. * Typical useage would be -d mode to drop constraints, edit the data as needed, then -a mode to reapply constraints. * `--parent (-p)`: Parent table of an already created partition set. (Required) * `--connection (-c)`: Connection string for use by psycopg. Defaults to "host=" (local socket). * `--drop_constraints (-d)`: Drop all constraints managed by `pg_partman`. Drops constraints on ALL child tables in the partition set. * `--add_constraints (-a)`: Apply constraints on configured columns to all child tables older than the premake value. * `--jobs (-j)`: Use the python multiprocessing library to recreate indexes in parallel. Value for -j is number of simultaneous jobs to run. Note that this is per table, not per index. Be very careful setting this option if load is a concern on your systems. * `--wait (-w)`: Wait the given number of seconds after a table has had its constraints dropped or applied before moving on to the next. When used with -j, this will set the pause between the batches of parallel jobs instead. * `--dryrun`: Show what the script will do without actually running it against the database. Highly recommend reviewing this before running. * `--quiet (-q)`: Turn off all output. * `--version`: Print out the minimum version of `pg_partman` this script is meant to work with. The version of `pg_partman` installed may be greater than this. *`reapply_foreign_keys.py`* * A python script for redoing the inherited foreign keys for an entire partition set. * All existing foreign keys on all child tables are dropped and the foreign keys that exist on the parent at the time this is run will be applied to all children. * Commits after each foreign key is created to avoid long periods of contention. * `--parent (-p)`: Parent table of an already created partition set. (Required) * `--connection (-c)`: Connection string for use by psycopg. Defaults to "host=" (local socket). * `--quiet (-q)`: Switch setting to stop all output during and after partitioning undo. * `--dryrun`: Show what the script will do without actually running it against the database. Highly recommend reviewing this before running. * `--nonpartman` If the partition set you are running this on is not managed by pg_partman, set this flag. Otherwise internal pg_partman functions are used and this script may not work. When this is set the order that the tables are rekeyed is alphabetical instead of logical. * `--version`: Print out the minimum version of `pg_partman` this script is meant to work with. The version of `pg_partman` installed may be greater than this. * `--debug`: Show additional debugging output *`check_unique_constraints.py`* * Partitioning using inheritance has the shortcoming of not allowing a unique constraint to apply to all tables in the entire partition set without causing large performance issues once the partition set begins to grow very large. This script is used to check that all rows in a partition set are unique for the given columns. * Note that on very large partition sets this can be an expensive operation to run that can consume a large chunk of storage space. The amount of storage space required is enough to dump out the entire index's column data as a plaintext file. * If there is a column value that violates the unique constraint, this script will return those column values along with a count of how many of each value there are. Output can also be simplified to a single, total integer value to make it easier to use with monitoring applications. * `--parent (-p)`: Parent table of the partition set to be checked. (Required) * `--column_list (-l)`: Comma separated list of columns that make up the unique constraint to be checked. (Required) * `--connection (-c)`: Connection string for use by psycopg. Defaults to "host=" (local socket). * `--temp (-t)`: Path to a writable folder that can be used for temp working files. Defaults system temp folder. * `--psql`: Full path to psql binary if not in current PATH. * `--simple`: Output a single integer value with the total duplicate count. Use this for monitoring software that requires a simple value to be checked for. * `--quiet (-q)`: Suppress all output unless there is a constraint violation found. * `--version`: Print out the minimum version of `pg_partman` this script is meant to work with. The version of `pg_partman` installed may be greater than this. pg_partman-2.2.2/doc/pg_partman_howto.md000066400000000000000000001231611262146621700203170ustar00rootroot00000000000000Example Guide On Setting Up Partitioning ======================================== This HowTo guide will show you some examples of how to set up both simple, single level partitioning as well as multi-level sub-partitioning. It will also show you how to partition data out of a table that has existing data (see **Sub-partition ID->ID->ID**) and undo the partitioning of an existing partition set. For more details on what each function does and the additional features in this extension, please see the **pg_partman.md** documentation file. ### Simple Time Based: 1 Partition Per Day ``` keith=# \d partman_test.time_taptest_table Table "partman_test.time_taptest_table" Column | Type | Modifiers --------+--------------------------+------------------------ col1 | integer | not null col2 | text | col3 | timestamp with time zone | not null default now() Indexes: "time_taptest_table_pkey" PRIMARY KEY, btree (col1) keith=# SELECT create_parent('partman_test.time_taptest_table', 'col3', 'time', 'daily'); create_parent --------------- t (1 row) keith=# \d+ partman_test.time_taptest_table Table "partman_test.time_taptest_table" Column | Type | Modifiers | Storage | Stats target | Description --------+--------------------------+------------------------+----------+--------------+------------- col1 | integer | not null | plain | | col2 | text | | extended | | col3 | timestamp with time zone | not null default now() | plain | | Indexes: "time_taptest_table_pkey" PRIMARY KEY, btree (col1) Triggers: time_taptest_table_part_trig BEFORE INSERT ON partman_test.time_taptest_table FOR EACH ROW EXECUTE PROCEDURE partman_test.time_taptest_table_part_trig_func() Child tables: partman_test.time_taptest_table_p2015_05_09, partman_test.time_taptest_table_p2015_05_10, partman_test.time_taptest_table_p2015_05_11, partman_test.time_taptest_table_p2015_05_12, partman_test.time_taptest_table_p2015_05_13, partman_test.time_taptest_table_p2015_05_14, partman_test.time_taptest_table_p2015_05_15, partman_test.time_taptest_table_p2015_05_16, partman_test.time_taptest_table_p2015_05_17 ``` The trigger function most efficiently covers a specific period of time for 4 days before and 4 days after today. This can be adjusted with the `optimize_trigger` config option in the `part_config` table. Outside of that, a dynamic statement tries to find the appropriate child table to put the data into. Note this dynamic statement is far less efficient since a catalog lookup is required and the statement plan cannot be cached as well as looking up the that the child table exists. If the child table does not exist at all for the time value given, the data goes to the parent: ``` keith=# \sf partman_test.time_taptest_table_part_trig_func CREATE OR REPLACE FUNCTION partman_test.time_taptest_table_part_trig_func() RETURNS trigger LANGUAGE plpgsql AS $function$ DECLARE v_count int; v_partition_name text; v_partition_timestamp timestamptz; BEGIN IF TG_OP = 'INSERT' THEN v_partition_timestamp := date_trunc('day', NEW.col3); IF NEW.col3 >= '2015-05-13 00:00:00-04' AND NEW.col3 < '2015-05-14 00:00:00-04' THEN INSERT INTO partman_test.time_taptest_table_p2015_05_13 VALUES (NEW.*); ELSIF NEW.col3 >= '2015-05-12 00:00:00-04' AND NEW.col3 < '2015-05-13 00:00:00-04' THEN INSERT INTO partman_test.time_taptest_table_p2015_05_12 VALUES (NEW.*); ELSIF NEW.col3 >= '2015-05-14 00:00:00-04' AND NEW.col3 < '2015-05-15 00:00:00-04' THEN INSERT INTO partman_test.time_taptest_table_p2015_05_14 VALUES (NEW.*); ELSIF NEW.col3 >= '2015-05-11 00:00:00-04' AND NEW.col3 < '2015-05-12 00:00:00-04' THEN INSERT INTO partman_test.time_taptest_table_p2015_05_11 VALUES (NEW.*); ELSIF NEW.col3 >= '2015-05-15 00:00:00-04' AND NEW.col3 < '2015-05-16 00:00:00-04' THEN INSERT INTO partman_test.time_taptest_table_p2015_05_15 VALUES (NEW.*); ELSIF NEW.col3 >= '2015-05-10 00:00:00-04' AND NEW.col3 < '2015-05-11 00:00:00-04' THEN INSERT INTO partman_test.time_taptest_table_p2015_05_10 VALUES (NEW.*); ELSIF NEW.col3 >= '2015-05-16 00:00:00-04' AND NEW.col3 < '2015-05-17 00:00:00-04' THEN INSERT INTO partman_test.time_taptest_table_p2015_05_16 VALUES (NEW.*); ELSIF NEW.col3 >= '2015-05-09 00:00:00-04' AND NEW.col3 < '2015-05-10 00:00:00-04' THEN INSERT INTO partman_test.time_taptest_table_p2015_05_09 VALUES (NEW.*); ELSIF NEW.col3 >= '2015-05-17 00:00:00-04' AND NEW.col3 < '2015-05-18 00:00:00-04' THEN INSERT INTO partman_test.time_taptest_table_p2015_05_17 VALUES (NEW.*); ELSE v_partition_name := partman.check_name_length('time_taptest_table', 'partman_test', to_char(v_partition_timestamp, 'YYYY_MM_DD'), TRUE); SELECT count(*) INTO v_count FROM pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_count > 0 THEN EXECUTE 'INSERT INTO '||v_partition_name||' VALUES($1.*)' USING NEW; ELSE RETURN NEW; END IF; END IF; END IF; RETURN NULL; END $function$ ``` ### Simple Serial ID: 1 Partition Per 10 ID Values Starting With Empty Table ``` keith=# \d partman_test.id_taptest_table Table "partman_test.id_taptest_table" Column | Type | Modifiers --------+--------------------------+-------------------------------- col1 | integer | not null col2 | text | not null default 'stuff'::text col3 | timestamp with time zone | default now() Indexes: "id_taptest_table_pkey" PRIMARY KEY, btree (col1) keith=# SELECT create_parent('partman_test.id_taptest_table', 'col1', 'id', '10'); create_parent --------------- t (1 row) keith=# \d+ partman_test.id_taptest_table Table "partman_test.id_taptest_table" Column | Type | Modifiers | Storage | Stats target | Description --------+--------------------------+--------------------------------+----------+--------------+------------- col1 | integer | not null | plain | | col2 | text | not null default 'stuff'::text | extended | | col3 | timestamp with time zone | default now() | plain | | Indexes: "id_taptest_table_pkey" PRIMARY KEY, btree (col1) Triggers: id_taptest_table_part_trig BEFORE INSERT ON partman_test.id_taptest_table FOR EACH ROW EXECUTE PROCEDURE partman_test.id_taptest_table_part_trig_func() Child tables: partman_test.id_taptest_table_p0, partman_test.id_taptest_table_p10, partman_test.id_taptest_table_p20, partman_test.id_taptest_table_p30, partman_test.id_taptest_table_p40 ``` This trigger function most efficiently covers for 4x10 intervals above the current max (0). Once max id reaches higher values, it will also efficiently cover up to 4x10 intervals behind the current max. Outside of that, a dynamic statement tries to find the appropriate child table to put the data into. And like I said for time above, the dynamic part is less efficient. This trigger also takes care of making new partitions automatically when current max reaches 50% of the current child table's maximum. ``` CREATE OR REPLACE FUNCTION partman_test.id_taptest_table_part_trig_func() RETURNS trigger LANGUAGE plpgsql AS $function$ DECLARE v_count int; v_current_partition_id bigint; v_current_partition_name text; v_id_position int; v_last_partition text := 'partman_test.id_taptest_table_p40'; v_next_partition_id bigint; v_next_partition_name text; v_partition_created boolean; BEGIN IF TG_OP = 'INSERT' THEN IF NEW.col1 >= 0 AND NEW.col1 < 10 THEN INSERT INTO partman_test.id_taptest_table_p0 VALUES (NEW.*); ELSIF NEW.col1 >= 10 AND NEW.col1 < 20 THEN INSERT INTO partman_test.id_taptest_table_p10 VALUES (NEW.*); ELSIF NEW.col1 >= 20 AND NEW.col1 < 30 THEN INSERT INTO partman_test.id_taptest_table_p20 VALUES (NEW.*); ELSIF NEW.col1 >= 30 AND NEW.col1 < 40 THEN INSERT INTO partman_test.id_taptest_table_p30 VALUES (NEW.*); ELSIF NEW.col1 >= 40 AND NEW.col1 < 50 THEN INSERT INTO partman_test.id_taptest_table_p40 VALUES (NEW.*); ELSE v_current_partition_id := NEW.col1 - (NEW.col1 % 10); v_current_partition_name := partman.check_name_length('id_taptest_table', 'partman_test', v_current_partition_id::text, TRUE); SELECT count(*) INTO v_count FROM pg_tables WHERE schemaname ||'.'|| tablename = v_current_partition_name; IF v_count > 0 THEN EXECUTE 'INSERT INTO '||v_current_partition_name||' VALUES($1.*)' USING NEW; ELSE RETURN NEW; END IF; END IF; v_current_partition_id := NEW.col1 - (NEW.col1 % 10); IF (NEW.col1 % 10) > (10 / 2) THEN v_id_position := (length(v_last_partition) - position('p_' in reverse(v_last_partition))) + 2; v_next_partition_id := (substring(v_last_partition from v_id_position)::bigint) + 10; WHILE ((v_next_partition_id - v_current_partition_id) / 10) <= 4 LOOP v_partition_created := partman.create_partition_id('partman_test.id_taptest_table', ARRAY[v_next_partition_id]); IF v_partition_created THEN PERFORM partman.create_function_id('partman_test.id_taptest_table'); PERFORM partman.apply_constraints('partman_test.id_taptest_table'); END IF; v_next_partition_id := v_next_partition_id + 10; END LOOP; END IF; END IF; RETURN NULL; END $function$ ``` ### Sub-partition Time->Time->Time: Yearly -> Monthly -> Daily ``` keith=# \d partman_test.time_taptest_table Table "partman_test.time_taptest_table" Column | Type | Modifiers --------+--------------------------+------------------------ col1 | integer | not null col2 | text | col3 | timestamp with time zone | not null default now() Indexes: "time_taptest_table_pkey" PRIMARY KEY, btree (col1) ``` Create top yearly partition set that only covers 2 years forward/back ``` keith=# SELECT create_parent('partman_test.time_taptest_table', 'col3', 'time', 'yearly', p_premake := 2); create_parent --------------- t (1 row) ``` Now tell pg_partman to partition all yearly child tables by month. Do this by giving it the parent table of the yearly partition set (happens to be the same as above) ``` keith=# SELECT create_sub_parent('partman_test.time_taptest_table', 'col3', 'time', 'monthly', p_premake := 2); create_sub_parent ------------------- t (1 row) keith=# select tablename from pg_tables where schemaname = 'partman_test' order by tablename; tablename ----------------------------------- time_taptest_table time_taptest_table_p2013 time_taptest_table_p2013_p2013_01 time_taptest_table_p2014 time_taptest_table_p2014_p2014_01 time_taptest_table_p2015 time_taptest_table_p2015_p2015_03 time_taptest_table_p2015_p2015_04 time_taptest_table_p2015_p2015_05 time_taptest_table_p2015_p2015_06 time_taptest_table_p2015_p2015_07 time_taptest_table_p2016 time_taptest_table_p2016_p2016_01 time_taptest_table_p2017 time_taptest_table_p2017_p2017_01 (15 rows) ``` The day this tutorial was written is 2015-05-13. You now see that this causes only 2 new future partitions to be created. And for the monthly partitions, they have been created to cover 2 months ahead as well. Note that the trigger will still cover 4 ahead and 4 behind for both partition levels unless you change the `optimize_trigger` option in the config table. A parent table ALWAYS has at least one child, so for the time period that is outside of what the premake covers, just a single table has been made for the lowest possible month in that yearly time period (January). Now tell pg_partman to partition every monthly table that currently exists by day. Do this by giving it the parent table of each monthly partition set (the parent with the just the year suffix since its children are the monthly partitions). ``` SELECT create_sub_parent('partman_test.time_taptest_table_p2013', 'col3', 'time', 'daily', p_premake := 2); SELECT create_sub_parent('partman_test.time_taptest_table_p2014', 'col3', 'time', 'daily', p_premake := 2); SELECT create_sub_parent('partman_test.time_taptest_table_p2015', 'col3', 'time', 'daily', p_premake := 2); SELECT create_sub_parent('partman_test.time_taptest_table_p2016', 'col3', 'time', 'daily', p_premake := 2); SELECT create_sub_parent('partman_test.time_taptest_table_p2017', 'col3', 'time', 'daily', p_premake := 2); keith=# select tablename from pg_tables where schemaname = 'partman_test' order by tablename; tablename ----------------------------------------------- time_taptest_table time_taptest_table_p2013 time_taptest_table_p2013_p2013_01 time_taptest_table_p2013_p2013_01_p2013_01_01 time_taptest_table_p2014 time_taptest_table_p2014_p2014_01 time_taptest_table_p2014_p2014_01_p2014_01_01 time_taptest_table_p2015 time_taptest_table_p2015_p2015_03 time_taptest_table_p2015_p2015_03_p2015_03_01 time_taptest_table_p2015_p2015_04 time_taptest_table_p2015_p2015_04_p2015_04_01 time_taptest_table_p2015_p2015_05 time_taptest_table_p2015_p2015_05_p2015_05_11 time_taptest_table_p2015_p2015_05_p2015_05_12 time_taptest_table_p2015_p2015_05_p2015_05_13 time_taptest_table_p2015_p2015_05_p2015_05_14 time_taptest_table_p2015_p2015_05_p2015_05_15 time_taptest_table_p2015_p2015_06 time_taptest_table_p2015_p2015_06_p2015_06_01 time_taptest_table_p2015_p2015_07 time_taptest_table_p2015_p2015_07_p2015_07_01 time_taptest_table_p2016 time_taptest_table_p2016_p2016_01 time_taptest_table_p2016_p2016_01_p2016_01_01 time_taptest_table_p2017 time_taptest_table_p2017_p2017_01 time_taptest_table_p2017_p2017_01_p2017_01_01 (28 rows) ``` Again, assuming today's date is 2015-05-13, it has created the sub-partitions to cover 2 days in the future. All other parent tables outside of the current time period have the lowest possible day created for them. ### Sub-partition ID->ID->ID: 10,000 -> 1,000 -> 100 This partition set has existing data already in it. We will partition it out using the python script found in the "bin" directory of the repo. It's possible to use the partition_data_id() function in postgres as well, but that would partition all the data out in a single transaction which, for a live table, could cause serious contention issues. The python script allows commits to be done in batches and avoid that contention. ``` keith=# \d partman_test.id_taptest_table Table "partman_test.id_taptest_table" Column | Type | Modifiers --------+--------------------------+-------------------------------- col1 | integer | not null col2 | text | not null default 'stuff'::text col3 | timestamp with time zone | default now() Indexes: keith=# SELECT count(*) FROM partman_test.id_taptest_table ; count -------- 100000 (1 row) keith=# SELECT min(col1), max(col1) FROM partman_test.id_taptest_table ; min | max -----+-------- 1 | 100000 (1 row) ``` Since there is already data in the table, the child tables initially created will be based around the max value, two before it and two after it. As stated above for time, the trigger still coveres for 4 partitions before & after most efficiently, so if you need to adjust that as well, see the `part_config` table. ``` keith=# SELECT create_parent('partman_test.id_taptest_table', 'col1', 'id', '10000', p_use_run_maintenance := true, p_jobmon := false, p_premake := 2); create_parent --------------- t (1 row) keith=# select tablename from pg_tables where schemaname = 'partman_test' order by tablename; tablename ------------------------- id_taptest_table id_taptest_table_p100000 id_taptest_table_p110000 id_taptest_table_p120000 id_taptest_table_p80000 id_taptest_table_p90000 ``` However, the data still resides in the parent table at this time. To partition it out, use the python script as mentioned above. The options below will cause it to commit every 100 rows. If the interval option was not given, it would commit them at the configured interval of 10,000. Allowing a lower interval decreases the possible contention and allows the data to be more readily available in the newly created partitions: ``` $ python partition_data.py -c host=localhost -p partman_test.id_taptest_table -t id -i 100 Attempting to turn off autovacuum for partition set... ... Success! Rows moved: 100 Rows moved: 100 ... Rows moved: 100 Rows moved: 100 Rows moved: 1 Total rows moved: 100000 Running vacuum analyze on parent table... Attempting to reset autovacuum for old parent table and all child tables... ... Success! ``` Partitioning the data like this has also made the partitions that were needed to store the data ``` keith=# select tablename from pg_tables where schemaname = 'partman_test' order by tablename; tablename ------------------------- id_taptest_table id_taptest_table_p0 id_taptest_table_p10000 id_taptest_table_p100000 id_taptest_table_p110000 id_taptest_table_p120000 id_taptest_table_p20000 id_taptest_table_p30000 id_taptest_table_p40000 id_taptest_table_p50000 id_taptest_table_p60000 id_taptest_table_p70000 id_taptest_table_p80000 id_taptest_table_p90000 (14 rows) ``` Now create the sub-partitions for 1000. As was noted above for time, we give the parent table who's children we want partitioned along with the properties to give those children: ``` keith=# SELECT create_sub_parent('partman_test.id_taptest_table', 'col1', 'id', '1000', p_jobmon := false, p_premake := 2); create_sub_parent ------------------- t (1 row) ``` All children tables get at least their minimum sub-partition made and the sub-partitions based around the current max value are also created. ``` keith=# select tablename from pg_tables where schemaname = 'partman_test' order by tablename; tablename --------------------------------- id_taptest_table id_taptest_table_p0 id_taptest_table_p0_p0 id_taptest_table_p10000 id_taptest_table_p100000 id_taptest_table_p100000_p100000 id_taptest_table_p100000_p101000 id_taptest_table_p100000_p102000 id_taptest_table_p10000_p10000 id_taptest_table_p110000 id_taptest_table_p110000_p110000 id_taptest_table_p120000 id_taptest_table_p120000_p120000 id_taptest_table_p20000 id_taptest_table_p20000_p20000 id_taptest_table_p30000 id_taptest_table_p30000_p30000 id_taptest_table_p40000 id_taptest_table_p40000_p40000 id_taptest_table_p50000 id_taptest_table_p50000_p50000 id_taptest_table_p60000 id_taptest_table_p60000_p60000 id_taptest_table_p70000 id_taptest_table_p70000_p70000 id_taptest_table_p80000 id_taptest_table_p80000_p80000 id_taptest_table_p90000 id_taptest_table_p90000_p98000 id_taptest_table_p90000_p99000 (30 rows) ``` If you're wondering why, even with data in them, the children didn't get all their sub-partitions created, it's for the same reason that the top partition only initially had the 2 previous and 2 after created: the data still exists in the sub-partition parents. You can see this by running the monitoring function built into pg_partman here: ``` keith=# select * from check_parent() order by 1; parent_table | count --------------------------------------+------- partman_test.id_taptest_table_p0 | 9999 partman_test.id_taptest_table_p10000 | 10000 partman_test.id_taptest_table_p100000 | 1 partman_test.id_taptest_table_p20000 | 10000 partman_test.id_taptest_table_p30000 | 10000 partman_test.id_taptest_table_p40000 | 10000 partman_test.id_taptest_table_p50000 | 10000 partman_test.id_taptest_table_p60000 | 10000 partman_test.id_taptest_table_p70000 | 10000 partman_test.id_taptest_table_p80000 | 10000 partman_test.id_taptest_table_p90000 | 10000 (11 rows) ``` So, lets fix that: ``` python partition_data.py -c host=localhost -p partman_test.id_taptest_table_p0 -t id -i 100 python partition_data.py -c host=localhost -p partman_test.id_taptest_table_p10000 -t id -i 100 python partition_data.py -c host=localhost -p partman_test.id_taptest_table_p20000 -t id -i 100 python partition_data.py -c host=localhost -p partman_test.id_taptest_table_p30000 -t id -i 100 python partition_data.py -c host=localhost -p partman_test.id_taptest_table_p40000 -t id -i 100 python partition_data.py -c host=localhost -p partman_test.id_taptest_table_p50000 -t id -i 100 python partition_data.py -c host=localhost -p partman_test.id_taptest_table_p60000 -t id -i 100 python partition_data.py -c host=localhost -p partman_test.id_taptest_table_p70000 -t id -i 100 python partition_data.py -c host=localhost -p partman_test.id_taptest_table_p80000 -t id -i 100 python partition_data.py -c host=localhost -p partman_test.id_taptest_table_p90000 -t id -i 100 python partition_data.py -c host=localhost -p partman_test.id_taptest_table_p100000 -t id -i 100 ``` Now the monitoring function returns nothing (as should be the norm): ``` keith=# select * from check_parent() order by 1; parent_table | count --------------+------- (0 rows) ``` Now we also see all child partitons were created for the data that exists: ``` keith=# SELECT tablename FROM pg_tables WHERE schemaname = 'partman_test' order by tablename; tablename --------------------------------- id_taptest_table id_taptest_table_p0 id_taptest_table_p0_p0 id_taptest_table_p0_p1000 id_taptest_table_p0_p2000 id_taptest_table_p0_p3000 id_taptest_table_p0_p4000 id_taptest_table_p0_p5000 id_taptest_table_p0_p6000 id_taptest_table_p0_p7000 id_taptest_table_p0_p8000 id_taptest_table_p0_p9000 id_taptest_table_p10000 id_taptest_table_p100000 id_taptest_table_p100000_p100000 id_taptest_table_p100000_p101000 id_taptest_table_p100000_p102000 id_taptest_table_p10000_p10000 id_taptest_table_p10000_p11000 id_taptest_table_p10000_p12000 id_taptest_table_p10000_p13000 id_taptest_table_p10000_p14000 id_taptest_table_p10000_p15000 id_taptest_table_p10000_p16000 id_taptest_table_p10000_p17000 id_taptest_table_p10000_p18000 id_taptest_table_p10000_p19000 id_taptest_table_p110000 id_taptest_table_p110000_p110000 id_taptest_table_p120000 id_taptest_table_p120000_p120000 id_taptest_table_p20000 id_taptest_table_p20000_p20000 id_taptest_table_p20000_p21000 id_taptest_table_p20000_p22000 id_taptest_table_p20000_p23000 id_taptest_table_p20000_p24000 id_taptest_table_p20000_p25000 id_taptest_table_p20000_p26000 id_taptest_table_p20000_p27000 id_taptest_table_p20000_p28000 id_taptest_table_p20000_p29000 id_taptest_table_p30000 id_taptest_table_p30000_p30000 id_taptest_table_p30000_p31000 id_taptest_table_p30000_p32000 id_taptest_table_p30000_p33000 id_taptest_table_p30000_p34000 id_taptest_table_p30000_p35000 id_taptest_table_p30000_p36000 id_taptest_table_p30000_p37000 id_taptest_table_p30000_p38000 id_taptest_table_p30000_p39000 id_taptest_table_p40000 id_taptest_table_p40000_p40000 id_taptest_table_p40000_p41000 id_taptest_table_p40000_p42000 id_taptest_table_p40000_p43000 id_taptest_table_p40000_p44000 id_taptest_table_p40000_p45000 id_taptest_table_p40000_p46000 id_taptest_table_p40000_p47000 id_taptest_table_p40000_p48000 id_taptest_table_p40000_p49000 id_taptest_table_p50000 id_taptest_table_p50000_p50000 id_taptest_table_p50000_p51000 id_taptest_table_p50000_p52000 id_taptest_table_p50000_p53000 id_taptest_table_p50000_p54000 id_taptest_table_p50000_p55000 id_taptest_table_p50000_p56000 id_taptest_table_p50000_p57000 id_taptest_table_p50000_p58000 id_taptest_table_p50000_p59000 id_taptest_table_p60000 id_taptest_table_p60000_p60000 id_taptest_table_p60000_p61000 id_taptest_table_p60000_p62000 id_taptest_table_p60000_p63000 id_taptest_table_p60000_p64000 id_taptest_table_p60000_p65000 id_taptest_table_p60000_p66000 id_taptest_table_p60000_p67000 id_taptest_table_p60000_p68000 id_taptest_table_p60000_p69000 id_taptest_table_p70000 id_taptest_table_p70000_p70000 id_taptest_table_p70000_p71000 id_taptest_table_p70000_p72000 id_taptest_table_p70000_p73000 id_taptest_table_p70000_p74000 id_taptest_table_p70000_p75000 id_taptest_table_p70000_p76000 id_taptest_table_p70000_p77000 id_taptest_table_p70000_p78000 id_taptest_table_p70000_p79000 id_taptest_table_p80000 id_taptest_table_p80000_p80000 id_taptest_table_p80000_p81000 id_taptest_table_p80000_p82000 id_taptest_table_p80000_p83000 id_taptest_table_p80000_p84000 id_taptest_table_p80000_p85000 id_taptest_table_p80000_p86000 id_taptest_table_p80000_p87000 id_taptest_table_p80000_p88000 id_taptest_table_p80000_p89000 id_taptest_table_p90000 id_taptest_table_p90000_p90000 id_taptest_table_p90000_p91000 id_taptest_table_p90000_p92000 id_taptest_table_p90000_p93000 id_taptest_table_p90000_p94000 id_taptest_table_p90000_p95000 id_taptest_table_p90000_p96000 id_taptest_table_p90000_p97000 id_taptest_table_p90000_p98000 id_taptest_table_p90000_p99000 (119 rows) ``` We can still take this another level deeper as well. Normally with a large amount of data, it's not recommended to partition down to an interval this low since the benefit gained is minimal compared the management of such a large number of tables. But it's being done here as an example. Just as with the time example above, we now have to sub-partition each one of the sub-parent tables to say how we want their children sub-partitioned: ``` SELECT create_sub_parent('partman_test.id_taptest_table_p0', 'col1', 'id', '100', p_jobmon := false, p_premake := 2); SELECT create_sub_parent('partman_test.id_taptest_table_p10000', 'col1', 'id', '100', p_jobmon := false, p_premake := 2); SELECT create_sub_parent('partman_test.id_taptest_table_p20000', 'col1', 'id', '100', p_jobmon := false, p_premake := 2); SELECT create_sub_parent('partman_test.id_taptest_table_p30000', 'col1', 'id', '100', p_jobmon := false, p_premake := 2); SELECT create_sub_parent('partman_test.id_taptest_table_p40000', 'col1', 'id', '100', p_jobmon := false, p_premake := 2); SELECT create_sub_parent('partman_test.id_taptest_table_p50000', 'col1', 'id', '100', p_jobmon := false, p_premake := 2); SELECT create_sub_parent('partman_test.id_taptest_table_p60000', 'col1', 'id', '100', p_jobmon := false, p_premake := 2); SELECT create_sub_parent('partman_test.id_taptest_table_p70000', 'col1', 'id', '100', p_jobmon := false, p_premake := 2); SELECT create_sub_parent('partman_test.id_taptest_table_p80000', 'col1', 'id', '100', p_jobmon := false, p_premake := 2); SELECT create_sub_parent('partman_test.id_taptest_table_p90000', 'col1', 'id', '100', p_jobmon := false, p_premake := 2); SELECT create_sub_parent('partman_test.id_taptest_table_p100000', 'col1', 'id', '100', p_jobmon := false, p_premake := 2); ``` I won't show the full list here, but you can see how every child table of the above parents is now a parent table itself with the appropriate minimal child table created where needed as well as the child tables around the current max: ``` keith=# SELECT tablename FROM pg_tables WHERE schemaname = 'partman_test' order by tablename; tablename ----------------------------------------- id_taptest_table id_taptest_table_p0 id_taptest_table_p0_p0 id_taptest_table_p0_p0_p0 id_taptest_table_p0_p1000 id_taptest_table_p0_p1000_p1000 id_taptest_table_p0_p2000 id_taptest_table_p0_p2000_p2000 ... id_taptest_table_p10000 id_taptest_table_p100000 id_taptest_table_p100000_p100000 id_taptest_table_p100000_p100000_p100000 id_taptest_table_p100000_p100000_p100100 id_taptest_table_p100000_p100000_p100200 id_taptest_table_p100000_p101000 id_taptest_table_p100000_p101000_p101000 id_taptest_table_p100000_p102000 id_taptest_table_p100000_p102000_p102000 id_taptest_table_p10000_p10000 id_taptest_table_p10000_p10000_p10000 id_taptest_table_p10000_p11000 id_taptest_table_p10000_p11000_p11000 ... id_taptest_table_p90000_p98000 id_taptest_table_p90000_p98000_p98000 id_taptest_table_p90000_p99000 id_taptest_table_p90000_p99000_p99800 id_taptest_table_p90000_p99000_p99900 (225 rows) ``` If you ran the check_parent() function, you'd see that now each one of these new parent tables now needs to have its data moved. Now's a good time show a trick for generating many individual statements based on values returned from a query: ``` SELECT 'python partition_data.py -c host=localhost -p '||parent_table||' -t id -i 100' FROM part_config order by parent_table; ?column? --------------------------------------------------------------------------------------------------------- python partition_data.py -c host=localhost -p partman_test.id_taptest_table -t id -i 100 python partition_data.py -c host=localhost -p partman_test.id_taptest_table_p0 -t id -i 100 python partition_data.py -c host=localhost -p partman_test.id_taptest_table_p0_p0 -t id -i 100 python partition_data.py -c host=localhost -p partman_test.id_taptest_table_p0_p1000 -t id -i 100 ... ``` This will generate the commands to partition out the data found in any parent table managed by pg_partman. Yes some are already empty, but that won't matter since they'll just do nothing and it makes the query to generate these commands easier. Recommend putting the output from this into an executable shell file vs just pasting it all into the shell directly. Now if you get a list of all the tables, you can see there's quite a lot now (the row count returned is the number of tables). ``` keith=# SELECT tablename FROM pg_tables WHERE schemaname = 'partman_test' order by tablename; tablename ----------------------------------------- id_taptest_table id_taptest_table_p0 id_taptest_table_p0_p0 id_taptest_table_p0_p0_p0 id_taptest_table_p0_p0_p100 id_taptest_table_p0_p0_p200 id_taptest_table_p0_p0_p300 id_taptest_table_p0_p0_p400 id_taptest_table_p0_p0_p500 id_taptest_table_p0_p0_p600 id_taptest_table_p0_p0_p700 id_taptest_table_p0_p0_p800 id_taptest_table_p0_p0_p900 id_taptest_table_p0_p1000 id_taptest_table_p0_p1000_p1000 id_taptest_table_p0_p1000_p1100 id_taptest_table_p0_p1000_p1200 id_taptest_table_p0_p1000_p1300 id_taptest_table_p0_p1000_p1400 id_taptest_table_p0_p1000_p1500 id_taptest_table_p0_p1000_p1600 id_taptest_table_p0_p1000_p1700 id_taptest_table_p0_p1000_p1800 id_taptest_table_p0_p1000_p1900 id_taptest_table_p0_p2000 id_taptest_table_p0_p2000_p2000 id_taptest_table_p0_p2000_p2100 ... id_taptest_table_p90000_p98000_p98800 id_taptest_table_p90000_p98000_p98900 id_taptest_table_p90000_p99000 id_taptest_table_p90000_p99000_p99000 id_taptest_table_p90000_p99000_p99100 id_taptest_table_p90000_p99000_p99200 id_taptest_table_p90000_p99000_p99300 id_taptest_table_p90000_p99000_p99400 id_taptest_table_p90000_p99000_p99500 id_taptest_table_p90000_p99000_p99600 id_taptest_table_p90000_p99000_p99700 id_taptest_table_p90000_p99000_p99800 id_taptest_table_p90000_p99000_p99900 (1124 rows) ``` Now all 100,000 rows are properly partitioned where they should be and any new rows should go where they're supposed to. ### Set run_maintenance() to run often enough Using the above time-based partitions, run_maintenance() should be called at least twice a day to ensure it keeps up with the requirements of the smallest time partition interval (daily). For serial based partitioning that uses run_maintenance() (the sub-partitioning above does so), you must know your data ingestion rate and call it often enough to keep new partitions created ahead of that rate. If you're using the Background Worker (BGW), set the pg_partman_bgw.interval value in postgresql.conf. This example sets it to run every 12 hrs (43200 seconds). See the doc/pg_partman.md file for more information on the BGW settings. pg_partman_bgw.interval = 43200 pg_partman_bgw.role = 'keith' pg_partman_bgw.dbname = 'keith' If you're not using the BGW, you must use a third-party scheduling tool like cron to schedule the calls to run_maintenance() 03 01,13 * * * psql -c "SELECT run_maintenance()" ### Use Retention Policy To drop partitions on the first example above that are older than 30 days, set the following: ``` UPDATE part_config SET retention = '30 days', retention_keep_table = false WHERE parent_table = 'partman_test.time_taptest_table'; ``` To drop partitions on the second example above that contain a value 100 less than the current max (max(col1) - 100), set the following: ``` UPDATE part_config SET retention = '100', retention_keep_table = false WHERE parent_table = 'partman_test.id_taptest_table'; ``` For example, once the current id value of col1 reaches 1000, all partitions with values less than 900 will be dropped. If you'd like to keep the old data stored offline in dump files, set the retention_schema column as well (the keep* config options will be overridden if this is set): ``` UPDATE part_config SET retention = '30 days', retention_schema = 'archive' WHERE parent_table = 'partman_test.time_taptest_table'; ``` Then use the included python script **dump_partition.py** to dump out all tables contained in the archive schema: ``` $ python dump_partition.py -c "host=localhost username=postgres" -d mydatabase -n archive -o /path/to/dump/location ``` To implement any retention policy, just ensure run_maintenance() is called often enough for your needs. That function handles both partition creation and the retention policies. ### Undo Partitioning: Simple Time Based As with partitioning data out, it's best to use the python script to undo partitioning as well to avoid contention and moving large amounts of data in a single transaction. Except for the final example, there's no data in these partition sets, but the example would work either way. This also shows how you can give time-based partition sets a lower interval than what they are partitioned at. This set was daily above, but the batches are committed at the hourly marks (if there was data). ``` $ python undo_partition.py -p partman_test.time_taptest_table -c host=localhost -t time -i "1 hour" Attempting to turn off autovacuum for partition set... ... Success! Total rows moved: 0 Running vacuum analyze on parent table... Attempting to reset autovacuum for old parent table... ... Success! ``` ### Undo Partitioning: Simple Serial ID This just undoes the id partitions committing at the default partition interval of 10 given above. ``` $ python undo_partition.py -p partman_test.id_taptest_table -c host=localhost -t id Attempting to turn off autovacuum for partition set... ... Success! Total rows moved: 0 Running vacuum analyze on parent table... Attempting to reset autovacuum for old parent table... ... Success! ``` ### Undo Partitioning: Sub-partition ID->ID->ID Undoing sub-partitioning involves a little more work (or possibly a lot if it's a large set). You have to start from the bottom up. Just as I did above for generating statements for partitioning the data out, I can do the same for the undo_partition.py script. Keep in mind this gets the undo statement for ALL the parents at once. You do have to go through and parse out the top level calls as well as the mid-level partition, but this at least saves you a lot of potential typing (and typos). The bottom partitons must all be done first and the top last. Also, in this case I have no intention of keeping the old, empty tables anymore, so the --droptable option is given. pg_partman tries to be as safe as possible, so it only uninherits tables by default when undoing partitioning. If you want something dropped, you have to be sure and tell it. SELECT 'python undo_partition.py -c host=localhost -p '||parent_table||' -t id -i 100 --droptable' FROM part_config order by parent_table; First do the lowest level sub-partitons: python undo_partition.py -c host=localhost -p partman_test.id_taptest_table_p0_p0 -t id -i 100 --droptable python undo_partition.py -c host=localhost -p partman_test.id_taptest_table_p0_p1000 -t id -i 100 --droptable python undo_partition.py -c host=localhost -p partman_test.id_taptest_table_p0_p2000 -t id -i 100 --droptable ... python undo_partition.py -c host=localhost -p partman_test.id_taptest_table_p100000_p100000 -t id -i 100 --droptable python undo_partition.py -c host=localhost -p partman_test.id_taptest_table_p100000_p101000 -t id -i 100 --droptable python undo_partition.py -c host=localhost -p partman_test.id_taptest_table_p100000_p102000 -t id -i 100 --droptable Next do what were the mid level sub-partitions: python undo_partition.py -c host=localhost -p partman_test.id_taptest_table_p0 -t id -i 100 --droptable python undo_partition.py -c host=localhost -p partman_test.id_taptest_table_p10000 -t id -i 100 --droptable python undo_partition.py -c host=localhost -p partman_test.id_taptest_table_p100000 -t id -i 100 --droptable python undo_partition.py -c host=localhost -p partman_test.id_taptest_table_p110000 -t id -i 100 --droptable python undo_partition.py -c host=localhost -p partman_test.id_taptest_table_p120000 -t id -i 100 --droptable python undo_partition.py -c host=localhost -p partman_test.id_taptest_table_p20000 -t id -i 100 --droptable python undo_partition.py -c host=localhost -p partman_test.id_taptest_table_p30000 -t id -i 100 --droptable python undo_partition.py -c host=localhost -p partman_test.id_taptest_table_p40000 -t id -i 100 --droptable python undo_partition.py -c host=localhost -p partman_test.id_taptest_table_p50000 -t id -i 100 --droptable python undo_partition.py -c host=localhost -p partman_test.id_taptest_table_p60000 -t id -i 100 --droptable python undo_partition.py -c host=localhost -p partman_test.id_taptest_table_p70000 -t id -i 100 --droptable python undo_partition.py -c host=localhost -p partman_test.id_taptest_table_p80000 -t id -i 100 --droptable python undo_partition.py -c host=localhost -p partman_test.id_taptest_table_p90000 -t id -i 100 --droptable And finally do the last, top level partition: python undo_partition.py -c host=localhost -p partman_test.id_taptest_table -t id -i 100 --droptable Now there is only one table left with all the data keith=# SELECT tablename FROM pg_tables WHERE schemaname = 'partman_test' order by tablename; tablename ----------------- id_taptest_table keith=# SELECT count(*) FROM partman_test.id_taptest_table ; count -------- 100000 (1 row) ### Undo Partitioning: Sub-partition Time->Time->Time This is done in the same exact way as for ID->ID->ID except the undo_partition.py script would use the -t time setting and -i would use a time interval value. Hopefully these working examples can help you get started. Again, please see the `pg_partman.md` doc for the full details on all the functions and features of this extension. If you have any issues or questions, feel free to open an issue on the github page: https://github.com/keithf4/pg_partman pg_partman-2.2.2/pg_partman.control000066400000000000000000000001571262146621700174110ustar00rootroot00000000000000default_version = '2.2.2' comment = 'Extension to manage partitioned tables by time or ID' relocatable = false pg_partman-2.2.2/sql/000077500000000000000000000000001262146621700144535ustar00rootroot00000000000000pg_partman-2.2.2/sql/functions/000077500000000000000000000000001262146621700164635ustar00rootroot00000000000000pg_partman-2.2.2/sql/functions/apply_constraints.sql000066400000000000000000000307121262146621700227630ustar00rootroot00000000000000/* * Apply constraints managed by partman extension */ CREATE FUNCTION apply_constraints(p_parent_table text, p_child_table text DEFAULT NULL, p_analyze boolean DEFAULT FALSE, p_job_id bigint DEFAULT NULL, p_debug boolean DEFAULT FALSE) RETURNS void LANGUAGE plpgsql AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_child_table text; v_child_tablename text; v_col text; v_constraint_cols text[]; v_constraint_col_type text; v_constraint_name text; v_constraint_values record; v_control text; v_datetime_string text; v_epoch boolean; v_existing_constraint_name text; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_last_partition text; v_last_partition_id int; v_last_partition_timestamp timestamp; v_max_id bigint; v_max_timestamp timestamp; v_old_search_path text; v_optimize_constraint int; v_parent_schema text; v_parent_tablename text; v_partition_interval text; v_partition_suffix text; v_row_max record; v_sql text; v_step_id bigint; v_suffix_position int; v_type text; BEGIN SELECT partition_type , control , epoch , partition_interval , optimize_constraint , datetime_string , constraint_cols , jobmon INTO v_type , v_control , v_epoch , v_partition_interval , v_optimize_constraint , v_datetime_string , v_constraint_cols , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND constraint_cols IS NOT NULL; IF v_constraint_cols IS NULL THEN IF p_debug THEN RAISE NOTICE 'Given parent table (%) not set up for constraint management (constraint_cols is NULL)', p_parent_table; END IF; -- Returns silently to allow this function to be simply called by maintenance processes without having to check if config options are set. RETURN; END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', '@extschema@,'||v_jobmon_schema, 'false'); END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN IF p_job_id IS NULL THEN v_job_id := add_job(format('PARTMAN CREATE CONSTRAINT: %s', p_parent_table)); ELSE v_job_id = p_job_id; END IF; END IF; -- If p_child_table is null, figure out the partition that is the one right before the optimize_constraint value backwards. IF p_child_table IS NULL THEN -- 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 IN SELECT partition_schemaname, partition_tablename FROM @extschema@.show_partitions(p_parent_table, 'DESC') LOOP IF (v_type = 'time' OR v_type = 'time-custom') THEN IF v_epoch = false THEN EXECUTE format('SELECT max(%I) FROM %I.%I', v_control, v_row_max.partition_schemaname, v_row_max.partition_tablename) INTO v_max_timestamp; ELSE EXECUTE format('SELECT to_timestamp(max(%I)) FROM %I.%I', v_control, v_row_max.partition_schemaname, v_row_max.partition_tablename) INTO v_max_timestamp; END IF; IF v_max_timestamp IS NOT NULL THEN SELECT suffix_timestamp FROM @extschema@.show_partition_name(p_parent_table, v_max_timestamp::text) INTO v_last_partition_timestamp; v_partition_suffix := to_char(v_last_partition_timestamp - (v_partition_interval::interval * (v_optimize_constraint + 1) ), v_datetime_string); EXIT; END IF; ELSIF v_type = 'id' THEN EXECUTE format('SELECT max(%I) FROM %I.%I', v_control, v_row_max.partition_schemaname, v_row_max.partition_tablename) INTO v_max_id; IF v_max_id IS NOT NULL THEN SELECT suffix_id FROM @extschema@.show_partition_name(p_parent_table, v_max_id::text) INTO v_last_partition_id; v_last_partition_id := v_last_partition_id - (v_partition_interval::int * (v_optimize_constraint + 1) ); IF v_last_partition_id < 0 THEN v_last_partition_id := 0; END IF; v_partition_suffix := v_last_partition_id::text; EXIT; END IF; ELSE RAISE EXCEPTION 'Unknown type encountered in apply_constraints()'; END IF; IF p_debug THEN RAISE NOTICE 'apply_constraint: p_parent_table: %, partition_schemaname: %, partition_tablename: %', p_parent_table , v_row_max.partition_schemaname, v_row_max.partition_tablename; END IF; END LOOP; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Applying additional constraints: Automatically determining most recent child on which to apply constraints'); END IF; IF p_debug THEN RAISE NOTICE 'apply_constraint: v_parent_tablename: % , v_partition_suffix: %', v_parent_tablename, v_partition_suffix; END IF; IF v_partition_suffix IS NULL THEN IF p_debug THEN RAISE NOTICE 'No values for control column found in any child table. Unable to automatically determine which child table to apply additional constraints to'; END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'WARNING', 'No values for control column found in any child table. Unable to automatically determine which child table to apply additional constraints to'); IF p_job_id IS NULL THEN -- If not part of a sub-job, fail this one with a warning PERFORM fail_job(v_job_id, 2); END IF; END IF; -- Return cleanly so that if maintenance calls under this condition it produces no errors that will interfere with other partition sets. RETURN; END IF; v_child_table := v_parent_schema ||'.'|| @extschema@.check_name_length(v_parent_tablename, v_partition_suffix, TRUE); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', format('Target child table: %s.%s', v_parent_schema, v_child_table)); END IF; ELSE v_child_table := p_child_table; 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; SELECT tablename INTO v_child_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_child_table; IF v_child_tablename IS NULL THEN IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'NOTICE', format('Target child table (%s) does not exist. Skipping constraint creation.', v_child_table)); IF p_job_id IS 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'); END IF; IF p_debug THEN RAISE NOTICE 'Target child table (%) does not exist. Skipping constraint creation.', v_child_table; END IF; RETURN; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; FOREACH v_col IN ARRAY v_constraint_cols LOOP SELECT con.conname INTO v_existing_constraint_name 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 WHERE c.relname = v_child_tablename AND n.nspname = v_parent_schema AND con.conname LIKE 'partmanconstr_%' AND con.contype = 'c' AND a.attname = v_col AND ARRAY[a.attnum] <@ con.conkey AND a.attisdropped = false; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, format('Applying additional constraints: Applying new constraint on column: %s', v_col)); END IF; IF v_existing_constraint_name IS NOT NULL THEN IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'NOTICE', format('Partman managed constraint already exists on this table (%s) and column (%s). Skipping creation.', v_child_table, v_col)); END IF; IF p_debug THEN RAISE NOTICE 'Partman managed constraint already exists on this table (%) and column (%). Skipping creation.', v_child_table, v_col ; END IF; CONTINUE; END IF; -- 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; IF v_constraint_values IS NOT NULL THEN v_sql := format('ALTER TABLE %I.%I ADD CONSTRAINT %I CHECK (%I >= %L AND %I <= %L)' , v_parent_schema , v_child_tablename , v_constraint_name , v_col , v_constraint_values.min , v_col , v_constraint_values.max); IF p_debug THEN RAISE NOTICE 'Constraint creation query: %', v_sql; END IF; EXECUTE v_sql; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', format('New constraint created: %s', v_sql)); END IF; ELSE IF p_debug THEN RAISE NOTICE 'Given column (%) contains all NULLs. No constraint created', v_col; END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'NOTICE', format('Given column (%s) contains all NULLs. No constraint created', v_col)); END IF; END IF; END LOOP; IF p_analyze THEN IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, format('Applying additional constraints: Running analyze on partition set: %s', p_parent_table)); END IF; IF p_debug THEN RAISE NOTICE 'Running analyze on partition set: %', 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 PERFORM close_job(v_job_id); EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', v_old_search_path, 'false'); END IF; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN CREATE CONSTRAINT: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; pg_partman-2.2.2/sql/functions/apply_foreign_keys.sql000066400000000000000000000111361262146621700230770ustar00rootroot00000000000000/* * Apply foreign keys that exist on the given parent to the given child table */ CREATE FUNCTION apply_foreign_keys(p_parent_table text, p_child_table text, p_job_id bigint DEFAULT NULL, p_debug boolean DEFAULT false) RETURNS void LANGUAGE plpgsql AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_count int := 0; v_job_id bigint; v_jobmon text; v_jobmon_schema text; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_ref_schema text; v_ref_table text; v_row record; v_schemaname text; v_sql text; v_step_id bigint; v_tablename text; BEGIN SELECT jobmon INTO v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', '@extschema@,'||v_jobmon_schema, 'false'); END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN IF p_job_id IS NULL THEN v_job_id := add_job(format('PARTMAN APPLYING FOREIGN KEYS: %s', p_parent_table)); ELSE -- Don't create a new job, add steps into given job v_job_id := p_job_id; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, format('Applying foreign keys to %s if they exist on parent', p_child_table)); END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname||'.'||tablename = p_parent_table; SELECT schemaname, tablename INTO v_schemaname, v_tablename FROM pg_catalog.pg_tables WHERE schemaname||'.'||tablename = p_child_table; IF v_tablename IS NULL THEN IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'CRITICAL', format('Target child table (%s) does not exist.', p_child_table)); PERFORM fail_job(v_job_id); EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', v_old_search_path, 'false'); END IF; RAISE EXCEPTION 'Target child table (%) does not exist.', p_child_table; RETURN; END IF; FOR v_row 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 JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid WHERE c.relname = v_parent_tablename AND n.nspname = v_parent_schema AND contype = 'f' LOOP v_sql := format('ALTER TABLE %I.%I ADD %s' , v_schemaname , v_tablename , v_row.constraint_def); IF p_debug THEN RAISE NOTICE 'Constraint creation query: %', v_sql; END IF; EXECUTE v_sql; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'FK applied'); END IF; v_count := v_count + 1; END LOOP; IF v_count = 0 AND v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'No FKs found on parent'); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', v_old_search_path, 'false'); END IF; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN CREATE APPLYING FOREIGN KEYS: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; pg_partman-2.2.2/sql/functions/check_name_length.sql000066400000000000000000000026161262146621700226270ustar00rootroot00000000000000/* * Truncate the name of the given object if it is greater than the postgres default max (63 characters). * Also appends given suffix and schema if given and truncates the name so that the entire suffix will fit. * Returns original name with schema given if it doesn't require truncation */ CREATE FUNCTION check_name_length (p_object_name text, p_suffix text DEFAULT NULL, p_table_partition boolean DEFAULT FALSE) RETURNS text LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_new_length int; v_new_name text; BEGIN IF p_table_partition IS TRUE AND (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; END IF; RETURN v_new_name; END $$; pg_partman-2.2.2/sql/functions/check_parent.sql000066400000000000000000000017351262146621700216400ustar00rootroot00000000000000/* * Function to monitor for data getting inserted into parent tables managed by extension */ CREATE FUNCTION check_parent() RETURNS SETOF @extschema@.check_parent_table LANGUAGE plpgsql STABLE SECURITY DEFINER AS $$ DECLARE v_count bigint = 0; v_row record; v_schemaname text; v_tablename text; v_sql text; v_trouble @extschema@.check_parent_table%rowtype; BEGIN FOR v_row IN SELECT DISTINCT parent_table FROM @extschema@.part_config LOOP SELECT schemaname, tablename INTO v_schemaname, v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'||tablename = v_row.parent_table; v_sql := format('SELECT count(1) AS n FROM ONLY %I.%I', v_schemaname, v_tablename); EXECUTE v_sql INTO v_count; IF v_count > 0 THEN v_trouble.parent_table := v_schemaname ||'.'|| v_tablename; v_trouble.count := v_count; RETURN NEXT v_trouble; END IF; v_count := 0; END LOOP; RETURN; END $$; pg_partman-2.2.2/sql/functions/check_subpart_sameconfig.sql000066400000000000000000000043201262146621700242130ustar00rootroot00000000000000/* * 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. * If anyone can get a working constraint/trigger, please help! */ CREATE FUNCTION @extschema@.check_subpart_sameconfig(p_parent_table text) RETURNS TABLE (sub_partition_type text , sub_control text , sub_partition_interval text , sub_constraint_cols text[] , sub_premake int , sub_inherit_fk boolean , sub_retention text , sub_retention_schema text , sub_retention_keep_table boolean , sub_retention_keep_index boolean , sub_use_run_maintenance boolean , sub_epoch boolean , sub_optimize_trigger int , sub_optimize_constraint int , sub_jobmon boolean) LANGUAGE sql STABLE SECURITY DEFINER AS $$ WITH parent_info AS ( SELECT c1.oid FROM pg_catalog.pg_class c1 JOIN pg_catalog.pg_namespace n1 ON c1.relnamespace = n1.oid WHERE n1.nspname||'.'||c1.relname = p_parent_table ) , child_tables AS ( SELECT n.nspname||'.'||c.relname AS 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 JOIN parent_info pi ON h.inhparent = pi.oid ) -- Column order here must match the RETURNS TABLE definition SELECT DISTINCT a.sub_partition_type , a.sub_control , a.sub_partition_interval , a.sub_constraint_cols , a.sub_premake , a.sub_inherit_fk , a.sub_retention , a.sub_retention_schema , a.sub_retention_keep_table , a.sub_retention_keep_index , a.sub_use_run_maintenance , a.sub_epoch , a.sub_optimize_trigger , a.sub_optimize_constraint , a.sub_jobmon FROM @extschema@.part_config_sub a JOIN child_tables b on a.sub_parent = b.tablename; $$; pg_partman-2.2.2/sql/functions/check_subpartition_limits.sql000066400000000000000000000107271262146621700244540ustar00rootroot00000000000000/* * Check if parent table is a subpartition of an already existing partition set managed by pg_partman * If so, return the limits of what child tables can be created under the given parent table based on its own suffix */ CREATE FUNCTION check_subpartition_limits(p_parent_table text, p_type text, OUT sub_min text, OUT sub_max text) RETURNS record LANGUAGE plpgsql AS $$ DECLARE v_datetime_string text; v_id_position int; v_partition_interval interval; v_quarter text; v_sub_id_max bigint; v_sub_id_min bigint; v_sub_timestamp_max timestamp; v_sub_timestamp_min timestamp; v_time_position int; v_top_datetime_string text; v_top_interval text; v_top_parent text; v_top_type text; v_year text; BEGIN -- CTE query is done individually for each type (time, id) because it should return NULL if the top parent is not the same type in a subpartition set (id->time or time->id) IF p_type = 'id' THEN WITH top_oid AS ( SELECT i.inhparent AS top_parent_oid FROM pg_catalog.pg_class c JOIN pg_catalog.pg_inherits i ON c.oid = i.inhrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname||'.'||c.relname = p_parent_table ) SELECT n.nspname||'.'||c.relname, p.datetime_string, p.partition_interval, p.partition_type INTO v_top_parent, v_top_datetime_string, v_top_interval, v_top_type FROM pg_catalog.pg_class c JOIN top_oid t ON c.oid = t.top_parent_oid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace JOIN @extschema@.part_config p ON p.parent_table = n.nspname||'.'||c.relname WHERE c.oid = t.top_parent_oid AND p.partition_type = 'id'; IF v_top_parent IS NOT NULL THEN v_id_position := (length(p_parent_table) - position('p_' in reverse(p_parent_table))) + 2; v_sub_id_min := substring(p_parent_table from v_id_position)::bigint; v_sub_id_max := (v_sub_id_min + v_top_interval::bigint) - 1; sub_min := v_sub_id_min::text; sub_max := v_sub_id_max::text; END IF; ELSIF p_type = 'time' THEN WITH top_oid AS ( SELECT i.inhparent AS top_parent_oid FROM pg_catalog.pg_class c JOIN pg_catalog.pg_inherits i ON c.oid = i.inhrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname||'.'||c.relname = p_parent_table ) SELECT n.nspname||'.'||c.relname, p.datetime_string, p.partition_interval, p.partition_type INTO v_top_parent, v_top_datetime_string, v_top_interval, v_top_type FROM pg_catalog.pg_class c JOIN top_oid t ON c.oid = t.top_parent_oid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace JOIN @extschema@.part_config p ON p.parent_table = n.nspname||'.'||c.relname WHERE c.oid = t.top_parent_oid AND p.partition_type = 'time' OR p.partition_type = 'time-custom'; IF v_top_parent IS NOT NULL THEN v_time_position := (length(p_parent_table) - position('p_' in reverse(p_parent_table))) + 2; IF v_top_interval::interval <> '3 months' OR (v_top_interval::interval = '3 months' AND v_top_type = 'time-custom') THEN v_sub_timestamp_min := to_timestamp(substring(p_parent_table from v_time_position), v_top_datetime_string); ELSE -- to_timestamp doesn't recognize 'Q' date string formater. Handle it v_year := split_part(substring(p_parent_table from v_time_position), 'q', 1); v_quarter := split_part(substring(p_parent_table from v_time_position), 'q', 2); CASE WHEN v_quarter = '1' THEN v_sub_timestamp_min := to_timestamp(v_year || '-01-01', 'YYYY-MM-DD'); WHEN v_quarter = '2' THEN v_sub_timestamp_min := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); WHEN v_quarter = '3' THEN v_sub_timestamp_min := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); WHEN v_quarter = '4' THEN v_sub_timestamp_min := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); END CASE; END IF; v_sub_timestamp_max = (v_sub_timestamp_min + v_top_interval::interval) - '1 sec'::interval; sub_min := v_sub_timestamp_min::text; sub_max := v_sub_timestamp_max::text; END IF; ELSE RAISE EXCEPTION 'Invalid type given as parameter to check_subpartition_limits()'; END IF; RETURN; END $$; pg_partman-2.2.2/sql/functions/check_version.sql000066400000000000000000000016701262146621700220320ustar00rootroot00000000000000/* * Check PostgreSQL version number. Parameter must be full 3 point version. * Returns true if current version is greater than or equal to the parameter given. */ CREATE FUNCTION check_version(p_check_version text) RETURNS boolean LANGUAGE plpgsql STABLE AS $$ DECLARE v_check_version text[]; v_current_version text[] := string_to_array(current_setting('server_version'), '.'); BEGIN v_check_version := string_to_array(p_check_version, '.'); IF v_current_version[1]::int > v_check_version[1]::int THEN RETURN true; END IF; IF v_current_version[1]::int = v_check_version[1]::int THEN IF v_current_version[2]::int > v_check_version[2]::int THEN RETURN true; END IF; IF v_current_version[2]::int = v_check_version[2]::int THEN IF v_current_version[3]::int >= v_check_version[3]::int THEN RETURN true; END IF; -- 0.0.x END IF; -- 0.x.0 END IF; -- x.0.0 RETURN false; END $$; pg_partman-2.2.2/sql/functions/create_function_id.sql000066400000000000000000000272271262146621700230420ustar00rootroot00000000000000/* * Create the trigger function for the parent table of an id-based partition set */ CREATE FUNCTION create_function_id(p_parent_table text, p_job_id bigint DEFAULT NULL) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_control text; v_count int; v_current_partition_name text; v_current_partition_id bigint; v_datetime_string text; v_final_partition_id bigint; v_function_name text; v_higher_parent text := p_parent_table; v_id_position int; v_job_id bigint; v_jobmon text; v_jobmon_schema text; v_last_partition text; v_max bigint; v_next_partition_id bigint; v_next_partition_name text; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_partition_interval bigint; v_premake int; v_prev_partition_id bigint; v_prev_partition_name text; v_row_max_id record; v_run_maint boolean; v_step_id bigint; v_top_parent text := p_parent_table; v_trig_func text; v_optimize_trigger int; BEGIN SELECT partition_interval::bigint , control , premake , optimize_trigger , use_run_maintenance , jobmon INTO v_partition_interval , v_control , v_premake , v_optimize_trigger , v_run_maint , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND partition_type = 'id'; IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; SELECT partition_tablename INTO v_last_partition FROM @extschema@.show_partitions(p_parent_table, 'DESC') LIMIT 1; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', '@extschema@,'||v_jobmon_schema, 'false'); END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN IF p_job_id IS NULL THEN v_job_id := add_job(format('PARTMAN CREATE FUNCTION: %s', p_parent_table)); ELSE v_job_id = p_job_id; END IF; v_step_id := add_step(v_job_id, format('Creating partition function for table %s', p_parent_table)); END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; v_function_name := @extschema@.check_name_length(v_parent_tablename, '_part_trig_func', FALSE); -- Get the highest level top parent if multi-level partitioned in order to get proper max() value below WHILE v_higher_parent IS NOT NULL LOOP -- initially set in DECLARE WITH top_oid AS ( SELECT i.inhparent AS top_parent_oid FROM pg_catalog.pg_inherits i JOIN pg_catalog.pg_class c ON c.oid = i.inhrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname||'.'||c.relname = v_higher_parent ) SELECT n.nspname||'.'||c.relname INTO v_higher_parent 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 JOIN @extschema@.part_config p ON p.parent_table = n.nspname||'.'||c.relname WHERE p.partition_type = 'id'; IF v_higher_parent IS NOT NULL THEN -- initially set in DECLARE v_top_parent := v_higher_parent; END IF; END 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 parent. FOR v_row_max_id IN SELECT partition_schemaname, partition_tablename FROM @extschema@.show_partitions(v_top_parent, 'DESC') LOOP EXECUTE format('SELECT max(%I) FROM %I.%I', v_control, v_row_max_id.partition_schemaname, v_row_max_id.partition_tablename) INTO v_max; IF v_max IS NOT NULL THEN EXIT; END IF; END LOOP; IF v_max IS NULL THEN v_max := 0; END IF; v_current_partition_id = v_max - (v_max % v_partition_interval); v_next_partition_id := v_current_partition_id + v_partition_interval; v_current_partition_name := @extschema@.check_name_length(v_parent_tablename, v_current_partition_id::text, TRUE); v_trig_func := format('CREATE OR REPLACE FUNCTION %I.%I() RETURNS trigger LANGUAGE plpgsql AS $t$ DECLARE v_count int; v_current_partition_id bigint; v_current_partition_name text; v_id_position int; v_last_partition text := %L; v_next_partition_id bigint; v_next_partition_name text; v_partition_created boolean; BEGIN IF TG_OP = ''INSERT'' THEN IF NEW.%I >= %s AND NEW.%I < %s THEN ' , v_parent_schema , v_function_name , v_last_partition , v_control , v_current_partition_id , v_control , v_next_partition_id ); SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname = v_parent_schema AND tablename = v_current_partition_name; IF v_count > 0 THEN v_trig_func := v_trig_func || format(' INSERT INTO %I.%I VALUES (NEW.*); ', v_parent_schema, v_current_partition_name); ELSE v_trig_func := v_trig_func || ' -- Child table for current values does not exist in this partition set, so write to parent RETURN NEW;'; END IF; FOR i IN 1..v_optimize_trigger LOOP v_prev_partition_id := v_current_partition_id - (v_partition_interval * i); v_next_partition_id := v_current_partition_id + (v_partition_interval * i); v_final_partition_id := v_next_partition_id + v_partition_interval; v_prev_partition_name := @extschema@.check_name_length(v_parent_tablename, v_prev_partition_id::text, TRUE); v_next_partition_name := @extschema@.check_name_length(v_parent_tablename, v_next_partition_id::text, TRUE); -- Check that child table exist before making a rule to insert to them. -- Handles optimize_trigger being larger than premake (to go back in time further) and edge case of changing optimize_trigger immediately after running create_parent(). SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname = v_parent_schema AND tablename = v_prev_partition_name; IF v_count > 0 THEN -- Only handle previous partitions if they're starting above zero IF v_prev_partition_id >= 0 THEN v_trig_func := v_trig_func ||format(' ELSIF NEW.%I >= %s AND NEW.%I < %s THEN INSERT INTO %I.%I VALUES (NEW.*); ' , v_control , v_prev_partition_id , v_control , v_prev_partition_id + v_partition_interval , v_parent_schema , v_prev_partition_name ); END IF; END IF; SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname ||'.'||tablename = v_next_partition_name; IF v_count > 0 THEN v_trig_func := v_trig_func ||format(' ELSIF NEW.%I >= %s AND NEW.%I < %s THEN INSERT INTO %I.%I VALUES (NEW.*);' , v_control , v_next_partition_id , v_control , v_final_partition_id , v_parent_schema , v_next_partition_name ); END IF; END LOOP; v_trig_func := v_trig_func ||format(' ELSE v_current_partition_id := NEW.%I - (NEW.%I %% %s); v_current_partition_name := @extschema@.check_name_length(%L, v_current_partition_id::text, TRUE); SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname = %L AND tablename = v_current_partition_name; IF v_count > 0 THEN EXECUTE format(''INSERT INTO %%I.%%I VALUES($1.*)'', %L, v_current_partition_name) USING NEW; ELSE RETURN NEW; END IF; END IF;' , v_control , v_control , v_partition_interval , v_parent_tablename , v_parent_schema , v_parent_schema ); IF v_run_maint IS FALSE THEN v_trig_func := v_trig_func ||format(' v_current_partition_id := NEW.%I - (NEW.%I %% %s); IF (NEW.%I %% %s) > (%s / 2) THEN v_id_position := (length(v_last_partition) - position(''p_'' in reverse(v_last_partition))) + 2; v_next_partition_id := (substring(v_last_partition from v_id_position)::bigint) + %s; WHILE ((v_next_partition_id - v_current_partition_id) / %s) <= %s LOOP v_partition_created := @extschema@.create_partition_id(%L, ARRAY[v_next_partition_id]); IF v_partition_created THEN PERFORM @extschema@.create_function_id(%L); PERFORM @extschema@.apply_constraints(%L); END IF; v_next_partition_id := v_next_partition_id + %s; END LOOP; END IF;' , v_control , v_control , v_partition_interval , v_control , v_partition_interval , v_partition_interval , v_partition_interval , v_partition_interval , v_premake , p_parent_table , p_parent_table , p_parent_table , v_partition_interval ); END IF; v_trig_func := v_trig_func ||' END IF; RETURN NULL; END $t$;'; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', format('Added function for current id interval: %s to %s', v_current_partition_id, v_final_partition_id-1)); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', v_old_search_path, 'false'); END IF; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN CREATE FUNCTION: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''Partition function maintenance for table %s failed'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; pg_partman-2.2.2/sql/functions/create_function_time.sql000066400000000000000000000416251262146621700234020ustar00rootroot00000000000000/* * Create the trigger function for the parent table of a time-based partition set */ CREATE FUNCTION create_function_time(p_parent_table text, p_job_id bigint DEFAULT NULL) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_control text; v_count int; v_current_partition_name text; v_current_partition_timestamp timestamptz; v_datetime_string text; v_epoch boolean; v_final_partition_timestamp timestamptz; v_function_name text; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_new_length int; v_next_partition_name text; v_next_partition_timestamp timestamptz; v_parent_schema text; v_parent_tablename text; v_partition_interval interval; v_prev_partition_name text; v_prev_partition_timestamp timestamptz; v_step_id bigint; v_trig_func text; v_optimize_trigger int; v_type text; BEGIN SELECT partition_type , partition_interval::interval , epoch , control , optimize_trigger , datetime_string , jobmon INTO v_type , v_partition_interval , v_epoch , v_control , v_optimize_trigger , v_datetime_string , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (partition_type = 'time' OR partition_type = 'time-custom'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', '@extschema@,'||v_jobmon_schema, 'false'); END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN IF p_job_id IS NULL THEN v_job_id := add_job(format('PARTMAN CREATE FUNCTION: %s', p_parent_table)); ELSE v_job_id = p_job_id; END IF; v_step_id := add_step(v_job_id, format('Creating partition function for table %s', p_parent_table)); END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; v_function_name := @extschema@.check_name_length(v_parent_tablename, '_part_trig_func', FALSE); IF v_type = 'time' THEN v_trig_func := format('CREATE OR REPLACE FUNCTION %I.%I() RETURNS trigger LANGUAGE plpgsql AS $t$ DECLARE v_count int; v_partition_name text; v_partition_timestamp timestamptz; BEGIN IF TG_OP = ''INSERT'' THEN ' , v_parent_schema , v_function_name); IF v_epoch = false THEN CASE WHEN v_partition_interval = '15 mins' THEN v_trig_func := v_trig_func||format('v_partition_timestamp := date_trunc(''hour'', NEW.%I) + ''15min''::interval * floor(date_part(''minute'', NEW.%I) / 15.0);' , v_control , v_control); v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0); WHEN v_partition_interval = '30 mins' THEN v_trig_func := v_trig_func||format('v_partition_timestamp := date_trunc(''hour'', NEW.%I) + ''30min''::interval * floor(date_part(''minute'', NEW.%I) / 30.0);' , v_control , v_control); v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0); WHEN v_partition_interval = '1 hour' THEN v_trig_func := v_trig_func||format('v_partition_timestamp := date_trunc(''hour'', NEW.%I);', v_control); v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP); WHEN v_partition_interval = '1 day' THEN v_trig_func := v_trig_func||format('v_partition_timestamp := date_trunc(''day'', NEW.%I);', v_control); v_current_partition_timestamp := date_trunc('day', CURRENT_TIMESTAMP); WHEN v_partition_interval = '1 week' THEN v_trig_func := v_trig_func||format('v_partition_timestamp := date_trunc(''week'', NEW.%I);', v_control); v_current_partition_timestamp := date_trunc('week', CURRENT_TIMESTAMP); WHEN v_partition_interval = '1 month' THEN v_trig_func := v_trig_func||format('v_partition_timestamp := date_trunc(''month'', NEW.%I);', v_control); v_current_partition_timestamp := date_trunc('month', CURRENT_TIMESTAMP); WHEN v_partition_interval = '3 months' THEN v_trig_func := v_trig_func||format('v_partition_timestamp := date_trunc(''quarter'', NEW.%I);', v_control); v_current_partition_timestamp := date_trunc('quarter', CURRENT_TIMESTAMP); WHEN v_partition_interval = '1 year' THEN v_trig_func := v_trig_func||format('v_partition_timestamp := date_trunc(''year'', NEW.%I);', v_control); v_current_partition_timestamp := date_trunc('year', CURRENT_TIMESTAMP); END CASE; ELSE -- epoch is true CASE WHEN v_partition_interval = '15 mins' THEN v_trig_func := v_trig_func||format('v_partition_timestamp := date_trunc(''hour'', to_timestamp(NEW.%I)) + ''15min''::interval * floor(date_part(''minute'', NEW.%I) / 15.0);' , v_control , v_control); v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0); WHEN v_partition_interval = '30 mins' THEN v_trig_func := v_trig_func||format('v_partition_timestamp := date_trunc(''hour'', to_timestamp(NEW.%I)) + ''30min''::interval * floor(date_part(''minute'', NEW.%I) / 30.0);' , v_control , v_control); v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0); WHEN v_partition_interval = '1 hour' THEN v_trig_func := v_trig_func||format('v_partition_timestamp := date_trunc(''hour'', to_timestamp(NEW.%I));', v_control); v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP); WHEN v_partition_interval = '1 day' THEN v_trig_func := v_trig_func||format('v_partition_timestamp := date_trunc(''day'', to_timestamp(NEW.%I));', v_control); v_current_partition_timestamp := date_trunc('day', CURRENT_TIMESTAMP); WHEN v_partition_interval = '1 week' THEN v_trig_func := v_trig_func||format('v_partition_timestamp := date_trunc(''week'', to_timestamp(NEW.%I));', v_control); v_current_partition_timestamp := date_trunc('week', CURRENT_TIMESTAMP); WHEN v_partition_interval = '1 month' THEN v_trig_func := v_trig_func||format('v_partition_timestamp := date_trunc(''month'', to_timestamp(NEW.%I));', v_control); v_current_partition_timestamp := date_trunc('month', CURRENT_TIMESTAMP); WHEN v_partition_interval = '3 months' THEN v_trig_func := v_trig_func||format('v_partition_timestamp := date_trunc(''quarter'', to_timestamp(NEW.%I));', v_control); v_current_partition_timestamp := date_trunc('quarter', CURRENT_TIMESTAMP); WHEN v_partition_interval = '1 year' THEN v_trig_func := v_trig_func||format('v_partition_timestamp := date_trunc(''year'', to_timestamp(NEW.%I));', v_control); v_current_partition_timestamp := date_trunc('year', CURRENT_TIMESTAMP); END CASE; END IF; v_current_partition_name := @extschema@.check_name_length(v_parent_tablename, to_char(v_current_partition_timestamp, v_datetime_string), TRUE); v_next_partition_timestamp := v_current_partition_timestamp + v_partition_interval::interval; IF v_epoch = false THEN v_trig_func := v_trig_func ||format(' IF NEW.%I >= %L AND NEW.%I < %L THEN ' , v_control , v_current_partition_timestamp , v_control , v_next_partition_timestamp); ELSE v_trig_func := v_trig_func ||format(' IF to_timestamp(NEW.%I) >= %L AND to_timestamp(NEW.%I) < %L THEN ' , v_control , v_current_partition_timestamp , v_control , v_next_partition_timestamp); END IF; SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname = v_parent_schema AND tablename = v_current_partition_name; IF v_count > 0 THEN v_trig_func := v_trig_func || format(' INSERT INTO %I.%I VALUES (NEW.*); ', v_parent_schema, v_current_partition_name); ELSE v_trig_func := v_trig_func || ' -- Child table for current values does not exist in this partition set, so write to parent RETURN NEW;'; END IF; FOR i IN 1..v_optimize_trigger LOOP v_prev_partition_timestamp := v_current_partition_timestamp - (v_partition_interval::interval * i); v_next_partition_timestamp := v_current_partition_timestamp + (v_partition_interval::interval * i); v_final_partition_timestamp := v_next_partition_timestamp + (v_partition_interval::interval); v_prev_partition_name := @extschema@.check_name_length(v_parent_tablename, to_char(v_prev_partition_timestamp, v_datetime_string), TRUE); v_next_partition_name := @extschema@.check_name_length(v_parent_tablename, to_char(v_next_partition_timestamp, v_datetime_string), TRUE); -- Check that child table exist before making a rule to insert to them. -- Handles optimize_trigger being larger than premake (to go back in time further) and edge case of changing optimize_trigger immediately after running create_parent(). SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname = v_parent_schema AND tablename = v_prev_partition_name; IF v_count > 0 THEN IF v_epoch = false THEN v_trig_func := v_trig_func ||format(' ELSIF NEW.%I >= %L AND NEW.%I < %L THEN INSERT INTO %I.%I VALUES (NEW.*);' , v_control , v_prev_partition_timestamp , v_control , v_prev_partition_timestamp + v_partition_interval::interval , v_parent_schema , v_prev_partition_name); ELSE v_trig_func := v_trig_func ||format(' ELSIF to_timestamp(NEW.%I) >= %L AND to_timestamp(NEW.%I) < %L THEN INSERT INTO %I.%I VALUES (NEW.*);' , v_control , v_prev_partition_timestamp , v_control , v_prev_partition_timestamp + v_partition_interval::interval , v_parent_schema , v_prev_partition_name); END IF; END IF; SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname = v_parent_schema AND tablename = v_next_partition_name; IF v_count > 0 THEN IF v_epoch = false THEN v_trig_func := v_trig_func ||format(' ELSIF NEW.%I >= %L AND NEW.%I < %L THEN INSERT INTO %I.%I VALUES (NEW.*);' , v_control , v_next_partition_timestamp , v_control , v_final_partition_timestamp , v_parent_schema , v_next_partition_name); ELSE v_trig_func := v_trig_func ||format(' ELSIF to_timestamp(NEW.%I) >= %L AND to_timestamp(NEW.%I) < %L THEN INSERT INTO %I.%I VALUES (NEW.*);' , v_control , v_next_partition_timestamp , v_control , v_final_partition_timestamp , v_parent_schema , v_next_partition_name); END IF; END IF; END LOOP; v_trig_func := v_trig_func||format(' ELSE v_partition_name := @extschema@.check_name_length(%L, to_char(v_partition_timestamp, %L), TRUE); SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname = %L AND tablename = v_partition_name; IF v_count > 0 THEN EXECUTE format(''INSERT INTO %%I.%%I VALUES($1.*)'', %L, v_partition_name) USING NEW; ELSE RETURN NEW; END IF; END IF;' , v_parent_tablename , v_datetime_string , v_parent_schema , v_parent_schema); v_trig_func := v_trig_func ||' END IF; RETURN NULL; END $t$;'; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', format('Added function for current time interval: %s to %s' , v_current_partition_timestamp , v_final_partition_timestamp-'1sec'::interval)); END IF; ELSIF v_type = 'time-custom' THEN v_trig_func := format('CREATE OR REPLACE FUNCTION %I.%I() RETURNS trigger LANGUAGE plpgsql AS $t$ DECLARE v_child_schemaname text; v_child_table text; v_child_tablename text; BEGIN ' , v_parent_schema , v_function_name); IF v_epoch = false THEN v_trig_func := v_trig_func || format(' SELECT child_table INTO v_child_table FROM @extschema@.custom_time_partitions WHERE partition_range @> NEW.%I AND parent_table = %L;' , v_control , v_parent_schema||'.'||v_parent_tablename); ELSE -- epoch true v_trig_func := v_trig_func || format(' SELECT child_table INTO v_child_table FROM @extschema@.custom_time_partitions WHERE partition_range @> to_timestamp(NEW.%I) AND parent_table = %L;' , v_control , v_parent_schema||'.'||v_parent_tablename); END IF; v_trig_func := v_trig_func || ' SELECT schemaname, tablename INTO v_child_schemaname, v_child_tablename FROM pg_catalog.pg_tables WHERE schemaname ||''.''|| tablename = v_child_table; IF v_child_schemaname IS NOT NULL AND v_child_tablename IS NOT NULL THEN EXECUTE format(''INSERT INTO %I.%I VALUES ($1.*)'', v_child_schemaname, v_child_tablename) USING NEW; ELSE RETURN NEW; END IF; RETURN NULL; END $t$;'; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', format('Added function for custom time table: %s', p_parent_table)); END IF; ELSE RAISE EXCEPTION 'ERROR: Invalid time partitioning type given: %', v_type; END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', v_old_search_path, 'false'); END IF; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN CREATE FUNCTION: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''Partition function maintenance for table %s failed'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; pg_partman-2.2.2/sql/functions/create_parent.sql000066400000000000000000000544261262146621700220330ustar00rootroot00000000000000/* * Function to turn a table into the parent of a partition set */ CREATE FUNCTION create_parent( p_parent_table text , p_control text , p_type text , p_interval text , p_constraint_cols text[] DEFAULT NULL , p_premake int DEFAULT 4 , p_use_run_maintenance boolean DEFAULT NULL , p_start_partition text DEFAULT NULL , p_inherit_fk boolean DEFAULT true , p_epoch boolean DEFAULT false , p_jobmon boolean DEFAULT true , p_debug boolean DEFAULT false) RETURNS boolean LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_base_timestamp timestamp; v_count int := 1; v_datetime_string text; v_higher_parent text := p_parent_table; v_id_interval bigint; v_id_position int; v_job_id bigint; v_jobmon_schema text; v_last_partition_created boolean; v_max bigint; v_notnull boolean; v_old_search_path text; v_parent_partition_id bigint; v_parent_partition_timestamp timestamp; v_parent_schema text; v_parent_tablename text; v_partition_time timestamp; v_partition_time_array timestamp[]; v_partition_id_array bigint[]; v_row record; v_run_maint boolean; v_sql text; v_start_time timestamp; v_starting_partition_id bigint; v_step_id bigint; v_step_overflow_id bigint; v_sub_parent text; v_success boolean := false; v_time_interval interval; v_time_position int; v_top_datetime_string text; v_top_parent text := p_parent_table; v_top_schemaname text; v_top_tablename text; BEGIN IF position('.' in p_parent_table) = 0 THEN RAISE EXCEPTION 'Parent table must be schema qualified'; END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname || '.' || tablename = p_parent_table; IF v_parent_tablename IS NULL THEN RAISE EXCEPTION 'Please create given parent table first: %', p_parent_table; END IF; 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 AND n.nspname = v_parent_schema AND a.attname = p_control; 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; IF p_type = 'id' AND p_epoch = true THEN RAISE EXCEPTION 'p_epoch can only be used with time-based partitioning'; END IF; IF NOT @extschema@.check_partition_type(p_type) THEN RAISE EXCEPTION '% is not a valid partitioning type', p_type; END IF; EXECUTE format('LOCK TABLE %I.%I IN ACCESS EXCLUSIVE MODE', v_parent_schema, v_parent_tablename); IF p_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', '@extschema@,'||v_jobmon_schema, 'false'); END IF; END IF; IF p_use_run_maintenance IS NOT NULL THEN IF p_use_run_maintenance IS FALSE AND (p_type = 'time' OR p_type = 'time-custom') THEN RAISE EXCEPTION 'p_run_maintenance cannot be set to false for time based partitioning'; END IF; v_run_maint := p_use_run_maintenance; ELSIF p_type = 'time' OR p_type = 'time-custom' THEN v_run_maint := TRUE; ELSIF p_type = 'id' THEN v_run_maint := FALSE; ELSE RAISE EXCEPTION 'use_run_maintenance value cannot be set NULL'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job(format('PARTMAN SETUP PARENT: %s', p_parent_table)); v_step_id := add_step(v_job_id, format('Creating initial partitions on new parent table: %s', p_parent_table)); 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 WITH parent_table AS ( SELECT h.inhparent AS parent_oid 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 c.relname = v_parent_tablename AND n.nspname = v_parent_schema ), sibling_children AS ( SELECT i.inhrelid::regclass::text AS tablename FROM pg_inherits i JOIN parent_table p ON i.inhparent = p.parent_oid ) 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_use_run_maintenance , sub_epoch , sub_optimize_trigger , sub_optimize_constraint , sub_jobmon FROM @extschema@.part_config_sub a JOIN sibling_children b on a.sub_parent = b.tablename LIMIT 1 LOOP INSERT INTO @extschema@.part_config_sub ( sub_parent , 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_use_run_maintenance , sub_epoch , sub_optimize_trigger , sub_optimize_constraint , sub_jobmon) VALUES ( p_parent_table , v_row.sub_partition_type , v_row.sub_control , 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_use_run_maintenance , v_row.sub_epoch , v_row.sub_optimize_trigger , v_row.sub_optimize_constraint , v_row.sub_jobmon); END LOOP; IF p_type = 'time' OR p_type = 'time-custom' 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 <> 'time-custom' THEN RAISE EXCEPTION 'Must use a predefined time interval if not using type "time-custom". See documentation.'; 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; -- First partition is either the min premake or p_start_partition v_start_time := COALESCE(p_start_partition::timestamp, CURRENT_TIMESTAMP - (v_time_interval * p_premake)); 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 v_datetime_string := 'YYYY'; 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 v_partition_time_array := array_append(v_partition_time_array, v_base_timestamp); LOOP -- If current loop value is less than or equal to the value of the max premake, add time to array. IF (v_base_timestamp + (v_time_interval * v_count)) < (CURRENT_TIMESTAMP + (v_time_interval * p_premake)) THEN BEGIN v_partition_time := (v_base_timestamp + (v_time_interval * v_count))::timestamp; 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. 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'); CONTINUE; END; ELSE EXIT; -- all needed partitions added to array. Exit the loop. END IF; v_count := v_count + 1; END LOOP; INSERT INTO @extschema@.part_config ( parent_table , partition_type , partition_interval , epoch , control , premake , constraint_cols , datetime_string , use_run_maintenance , inherit_fk , jobmon) VALUES ( p_parent_table , p_type , v_time_interval , p_epoch , p_control , p_premake , p_constraint_cols , v_datetime_string , v_run_maint , p_inherit_fk , p_jobmon); v_last_partition_created := @extschema@.create_partition_time(p_parent_table, v_partition_time_array, false); 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 ( SELECT i.inhparent AS top_parent_oid FROM pg_catalog.pg_inherits i JOIN pg_catalog.pg_class c ON c.oid = i.inhrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE c.relname = v_parent_tablename AND n.nspname = v_parent_schema ) SELECT n.nspname||'.'||c.relname, p.datetime_string INTO v_top_parent, v_top_datetime_string 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 JOIN @extschema@.part_config p ON p.parent_table = n.nspname||'.'||c.relname; IF v_top_parent IS NOT NULL THEN -- If so create the lowest possible partition that is within the boundary of the parent v_time_position := (length(p_parent_table) - position('p_' in reverse(p_parent_table))) + 2; v_parent_partition_timestamp := to_timestamp(substring(p_parent_table from v_time_position), v_top_datetime_string); IF v_base_timestamp >= v_parent_partition_timestamp THEN WHILE v_base_timestamp >= v_parent_partition_timestamp LOOP v_base_timestamp := v_base_timestamp - v_time_interval; END LOOP; v_base_timestamp := v_base_timestamp + v_time_interval; -- add one back since while loop set it one lower than is needed ELSIF v_base_timestamp < v_parent_partition_timestamp THEN WHILE v_base_timestamp < v_parent_partition_timestamp LOOP v_base_timestamp := v_base_timestamp + v_time_interval; END LOOP; -- Don't need to remove one since new starting time will fit in top parent interval 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); ELSE -- Currently unknown edge case if code gets here RAISE EXCEPTION 'No child tables created. Unexpected edge case encountered. Please report this error to author with conditions that led to it.'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', format('Time partitions premade: %s', p_premake)); END IF; END IF; IF p_type = 'id' THEN v_id_interval := p_interval::bigint; IF v_id_interval < 10 THEN RAISE EXCEPTION 'Interval for serial partitioning must be greater than or equal to 10'; END IF; -- Check if parent table is a subpartition of an already existing id partition set managed by pg_partman. WHILE v_higher_parent IS NOT NULL LOOP -- initially set in DECLARE WITH top_oid AS ( SELECT i.inhparent AS top_parent_oid FROM pg_catalog.pg_inherits i JOIN pg_catalog.pg_class c ON c.oid = i.inhrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname||'.'||c.relname = v_higher_parent ) SELECT n.nspname||'.'||c.relname INTO v_higher_parent 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 JOIN @extschema@.part_config p ON p.parent_table = n.nspname||'.'||c.relname WHERE p.partition_type = 'id'; IF v_higher_parent IS NOT NULL THEN -- v_top_parent initially set in DECLARE v_top_parent := v_higher_parent; END IF; END LOOP; -- If custom start partition is set, use that. -- If custom start is not set and there is already data, start partitioning with the highest current value and ensure it's grabbed from highest top parent table SELECT schemaname, tablename INTO v_top_schemaname, v_top_tablename FROM pg_catalog.pg_tables WHERE schemaname||'.'||tablename = v_top_parent; v_sql := format('SELECT COALESCE(%L, max(%I)::bigint, 0) FROM %I.%I LIMIT 1' , p_start_partition::bigint , p_control , v_top_schemaname , v_top_tablename); EXECUTE v_sql INTO v_max; 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 THEN v_partition_id_array = array_append(v_partition_id_array, (v_starting_partition_id - v_id_interval*i)); END IF; v_partition_id_array = array_append(v_partition_id_array, (v_id_interval*i) + v_starting_partition_id); END LOOP; INSERT INTO @extschema@.part_config ( parent_table , partition_type , partition_interval , control , premake , constraint_cols , use_run_maintenance , inherit_fk , jobmon) VALUES ( p_parent_table , p_type , v_id_interval , p_control , p_premake , p_constraint_cols , v_run_maint , p_inherit_fk , p_jobmon); v_last_partition_created := @extschema@.create_partition_id(p_parent_table, v_partition_id_array, false); 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 -- See if it's actually a subpartition of a parent id partition WITH top_oid AS ( SELECT i.inhparent AS top_parent_oid FROM pg_catalog.pg_inherits i JOIN pg_catalog.pg_class c ON c.oid = i.inhrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE c.relname = v_parent_tablename AND n.nspname = v_parent_schema ) SELECT n.nspname||'.'||c.relname INTO v_top_parent 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 JOIN @extschema@.part_config p ON p.parent_table = n.nspname||'.'||c.relname WHERE p.partition_type = 'id'; IF v_top_parent IS NOT NULL THEN -- Create the lowest possible partition that is within the boundary of the parent v_id_position := (length(p_parent_table) - position('p_' in reverse(p_parent_table))) + 2; v_parent_partition_id = substring(p_parent_table from v_id_position)::bigint; IF v_starting_partition_id >= v_parent_partition_id THEN WHILE v_starting_partition_id >= v_parent_partition_id LOOP v_starting_partition_id := v_starting_partition_id - v_id_interval; END LOOP; v_starting_partition_id := v_starting_partition_id + v_id_interval; -- add one back since while loop set it one lower than is needed ELSIF v_starting_partition_id < v_parent_partition_id THEN WHILE v_starting_partition_id < v_parent_partition_id LOOP v_starting_partition_id := v_starting_partition_id + v_id_interval; END LOOP; -- Don't need to remove one since new starting id will fit in top parent interval 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); ELSE -- Currently unknown edge case if code gets here RAISE EXCEPTION 'No child tables created. Unexpected edge case encountered. Please report this error to author with conditions that led to it.'; END IF; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Creating partition function'); END IF; IF p_type = 'time' OR p_type = 'time-custom' 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 p_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); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); IF v_step_overflow_id IS NOT NULL THEN PERFORM fail_job(v_job_id); ELSE PERFORM close_job(v_job_id); END IF; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', v_old_search_path, 'false'); END IF; v_success := true; RETURN v_success; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN CREATE PARENT: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''Partition creation for table '||p_parent_table||' failed'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; pg_partman-2.2.2/sql/functions/create_partition_id.sql000066400000000000000000000270221262146621700232170ustar00rootroot00000000000000/* * Function to create id partitions */ CREATE FUNCTION create_partition_id(p_parent_table text, p_partition_ids bigint[], p_analyze boolean DEFAULT true) RETURNS boolean LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE 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_exists text; v_grantees text[]; v_hasoids boolean; v_id bigint; v_inherit_fk boolean; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_parent_grant record; v_parent_owner text; 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_revoke text; v_row record; v_sql text; v_step_id bigint; v_sub_id_max bigint; v_sub_id_min bigint; v_unlogged char; BEGIN SELECT control , partition_interval , inherit_fk , jobmon INTO v_control , v_partition_interval , v_inherit_fk , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND partition_type = 'id'; IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', '@extschema@,'||v_jobmon_schema, 'false'); END IF; END IF; -- Determine if this table is a child of a subpartition parent. If so, get limits of what child tables can be created based on parent suffix SELECT sub_min::bigint, sub_max::bigint INTO v_sub_id_min, v_sub_id_max FROM @extschema@.check_subpartition_limits(p_parent_table, 'id'); SELECT tableowner, schemaname, tablename, tablespace INTO v_parent_owner, v_parent_schema, v_parent_tablename, v_parent_tablespace FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job(format('PARTMAN CREATE TABLE: %s', p_parent_table)); 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. 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; END IF; END IF; v_partition_name := @extschema@.check_name_length(v_parent_tablename, v_id::text, TRUE); -- If child table already exists, skip creation SELECT tablename INTO v_exists FROM pg_catalog.pg_tables WHERE schemaname = v_parent_schema AND tablename = v_partition_name; IF v_exists IS NOT NULL 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, 'Creating new partition '||v_partition_name||' with interval from '||v_id||' to '||(v_id + v_partition_interval)-1); END IF; 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 AND n.nspname = v_parent_schema; v_sql := 'CREATE'; IF v_unlogged = 'u' THEN v_sql := v_sql || ' UNLOGGED'; END IF; v_sql := v_sql || format(' TABLE %I.%I (LIKE %I.%I INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING STORAGE INCLUDING COMMENTS)' , v_parent_schema , v_partition_name , v_parent_schema , v_parent_tablename); 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 AND n.nspname = v_parent_schema; IF v_hasoids IS TRUE THEN v_sql := v_sql || ' WITH (OIDS)'; END IF; EXECUTE v_sql; 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); EXECUTE format('ALTER TABLE %I.%I INHERIT %I.%I', v_parent_schema, v_partition_name, v_parent_schema, v_parent_tablename); FOR v_parent_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = p_parent_table GROUP BY grantee LOOP EXECUTE format('GRANT %s ON %I.%I TO %I' , array_to_string(v_parent_grant.types, ',') , v_parent_schema , v_partition_name , v_parent_grant.grantee); SELECT string_agg(r, ',') INTO v_revoke FROM (SELECT unnest(v_all) AS r EXCEPT SELECT unnest(v_parent_grant.types)) x; IF v_revoke IS NOT NULL THEN EXECUTE format('REVOKE %s ON %I.%I FROM %I CASCADE' , v_revoke , v_parent_schema , v_partition_name , v_parent_grant.grantee); END IF; v_grantees := array_append(v_grantees, v_parent_grant.grantee::text); END LOOP; -- Revoke all privileges from roles that have none on the parent IF v_grantees IS NOT NULL THEN FOR v_row IN SELECT role FROM ( SELECT DISTINCT grantee::text AS role FROM information_schema.table_privileges WHERE table_schema = v_parent_schema AND table_name = v_partition_name EXCEPT SELECT unnest(v_grantees)) x LOOP IF v_row.role IS NOT NULL THEN EXECUTE format('REVOKE ALL ON %I.%I FROM %I' , v_parent_schema , v_partition_name , v_row.role); END IF; END LOOP; END IF; EXECUTE format('ALTER TABLE %I.%I OWNER TO %I', v_parent_schema, v_partition_name, v_parent_owner); IF v_inherit_fk THEN PERFORM @extschema@.apply_foreign_keys(p_parent_table, v_parent_schema||'.'||v_partition_name, v_job_id); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); 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 FOR v_row IN SELECT sub_parent , sub_control , sub_partition_type , 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_use_run_maintenance , sub_epoch , sub_optimize_trigger , sub_optimize_constraint , sub_jobmon FROM @extschema@.part_config_sub WHERE sub_parent = p_parent_table LOOP IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Subpartitioning '||v_partition_name); END IF; v_sql := format('SELECT @extschema@.create_parent( p_parent_table := %L , p_control := %L , p_type := %L , p_interval := %L , p_constraint_cols := %L , p_premake := %L , p_use_run_maintenance := %L , p_inherit_fk := %L , p_epoch := %L , p_jobmon := %L )' , v_parent_schema||'.'||v_partition_name , v_row.sub_control , v_row.sub_partition_type , v_row.sub_partition_interval , v_row.sub_constraint_cols , v_row.sub_premake , v_row.sub_use_run_maintenance , v_row.sub_inherit_fk , v_row.sub_epoch , v_row.sub_jobmon); EXECUTE v_sql; 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 WHERE parent_table = v_parent_schema||'.'||v_partition_name; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END LOOP; -- end sub partitioning LOOP 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 v_step_id := add_step(v_job_id, format('No partitions created for partition set: %s', p_parent_table)); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; PERFORM close_job(v_job_id); END IF; IF v_jobmon_schema IS NOT NULL THEN EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', v_old_search_path, 'false'); END IF; RETURN v_partition_created; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN CREATE TABLE: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; pg_partman-2.2.2/sql/functions/create_partition_time.sql000066400000000000000000000352121262146621700235610ustar00rootroot00000000000000/* * Function to create a child table in a time-based partition set */ CREATE FUNCTION create_partition_time (p_parent_table text, p_partition_times timestamp[], p_analyze boolean DEFAULT true) RETURNS boolean LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE 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_datetime_string text; v_exists text; v_epoch boolean; v_grantees text[]; v_hasoids boolean; v_inherit_fk boolean; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_parent_grant record; v_parent_owner text; v_parent_schema text; v_parent_tablename text; v_partition_created boolean := false; v_partition_name text; v_partition_suffix text; v_parent_tablespace text; v_partition_interval interval; v_partition_timestamp_end timestamp; v_partition_timestamp_start timestamp; v_quarter text; v_revoke text; v_row record; v_sql text; v_step_id bigint; v_step_overflow_id bigint; v_sub_timestamp_max timestamp; v_sub_timestamp_min timestamp; v_trunc_value text; v_time timestamp; v_type text; v_unlogged char; v_year text; BEGIN SELECT partition_type , control , partition_interval , epoch , inherit_fk , jobmon , datetime_string INTO v_type , v_control , v_partition_interval , v_epoch , v_inherit_fk , v_jobmon , v_datetime_string FROM @extschema@.part_config WHERE parent_table = p_parent_table AND partition_type = 'time' OR partition_type = 'time-custom'; IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', '@extschema@,'||v_jobmon_schema, 'false'); END IF; END IF; -- Determine if this table is a child of a subpartition parent. If so, get limits of what child tables can be created based on parent suffix SELECT sub_min::timestamp, sub_max::timestamp INTO v_sub_timestamp_min, v_sub_timestamp_max FROM @extschema@.check_subpartition_limits(p_parent_table, 'time'); SELECT tableowner, schemaname, tablename, tablespace INTO v_parent_owner, v_parent_schema, v_parent_tablename, v_parent_tablespace FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job(format('PARTMAN CREATE TABLE: %s', p_parent_table)); END IF; 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. 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'); CONTINUE; END; -- 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 CONTINUE; END IF; END IF; -- 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); SELECT tablename INTO v_exists FROM pg_catalog.pg_tables WHERE schemaname = v_parent_schema AND tablename = v_partition_name; IF v_exists IS NOT NULL 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 , v_partition_name , v_partition_timestamp_start , v_partition_timestamp_end-'1sec'::interval)); END IF; 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 AND n.nspname = v_parent_schema; v_sql := 'CREATE'; IF v_unlogged = 'u' THEN v_sql := v_sql || ' UNLOGGED'; END IF; v_sql := v_sql || format(' TABLE %I.%I (LIKE %I.%I INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING STORAGE INCLUDING COMMENTS)' , v_parent_schema , v_partition_name , v_parent_schema , v_parent_tablename); 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 AND n.nspname = v_parent_schema; IF v_hasoids IS TRUE THEN v_sql := v_sql || ' WITH (OIDS)'; END IF; EXECUTE v_sql; 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; IF v_epoch = false 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_check' , v_control , v_partition_timestamp_start , v_control , v_partition_timestamp_end); ELSE EXECUTE format('ALTER TABLE %I.%I ADD CONSTRAINT %I CHECK (to_timestamp(%I) >= %L AND to_timestamp(%I) < %L)' , v_parent_schema , v_partition_name , v_partition_name||'_partition_check' , v_control , v_partition_timestamp_start , v_control , v_partition_timestamp_end); 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_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; FOR v_parent_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = p_parent_table GROUP BY grantee LOOP EXECUTE format('GRANT %s ON %I.%I TO %I' , array_to_string(v_parent_grant.types, ',') , v_parent_schema , v_partition_name , v_parent_grant.grantee); SELECT string_agg(r, ',') INTO v_revoke FROM (SELECT unnest(v_all) AS r EXCEPT SELECT unnest(v_parent_grant.types)) x; IF v_revoke IS NOT NULL THEN EXECUTE format('REVOKE %s ON %I.%I FROM %I CASCADE' , v_revoke , v_parent_schema , v_partition_name , v_parent_grant.grantee); END IF; v_grantees := array_append(v_grantees, v_parent_grant.grantee::text); END LOOP; -- Revoke all privileges from roles that have none on the parent IF v_grantees IS NOT NULL THEN FOR v_row IN SELECT role FROM ( SELECT DISTINCT grantee::text AS role FROM information_schema.table_privileges WHERE table_schema = v_parent_schema AND table_name = v_partition_name EXCEPT SELECT unnest(v_grantees)) x LOOP IF v_row.role IS NOT NULL THEN EXECUTE format('REVOKE ALL ON %I.%I FROM %I' , v_parent_schema , v_partition_name , v_row.role); END IF; END LOOP; END IF; EXECUTE format('ALTER TABLE %I.%I OWNER TO %I', v_parent_schema, v_partition_name, v_parent_owner); IF v_inherit_fk THEN PERFORM @extschema@.apply_foreign_keys(p_parent_table, v_parent_schema||'.'||v_partition_name, v_job_id); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); 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 FOR v_row IN SELECT sub_parent , sub_control , sub_partition_type , 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_use_run_maintenance , sub_epoch , sub_optimize_trigger , sub_optimize_constraint , sub_jobmon FROM @extschema@.part_config_sub WHERE sub_parent = p_parent_table LOOP IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, format('Subpartitioning %s.%s', v_parent_schema, v_partition_name)); END IF; v_sql := format('SELECT @extschema@.create_parent( p_parent_table := %L , p_control := %L , p_type := %L , p_interval := %L , p_constraint_cols := %L , p_premake := %L , p_use_run_maintenance := %L , p_inherit_fk := %L , p_epoch := %L , p_jobmon := %L )' , v_parent_schema||'.'||v_partition_name , v_row.sub_control , v_row.sub_partition_type , v_row.sub_partition_interval , v_row.sub_constraint_cols , v_row.sub_premake , v_row.sub_use_run_maintenance , v_row.sub_inherit_fk , v_row.sub_epoch , v_row.sub_jobmon); EXECUTE v_sql; 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 WHERE parent_table = v_parent_schema||'.'||v_partition_name; END LOOP; -- end sub partitioning LOOP 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 v_step_id := add_step(v_job_id, format('No partitions created for partition set: %s. Attempted intervals: %s', p_parent_table, p_partition_times)); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; IF v_step_overflow_id IS NOT NULL THEN PERFORM fail_job(v_job_id); ELSE PERFORM close_job(v_job_id); END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', v_old_search_path, 'false'); END IF; RETURN v_partition_created; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN CREATE TABLE: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; pg_partman-2.2.2/sql/functions/create_sub_parent.sql000066400000000000000000000065771262146621700227100ustar00rootroot00000000000000/* * Create a partition set that is a subpartition of an already existing partition set. * Given the parent table of any current partition set, it will turn all existing children into parent tables of their own partition sets * using the configuration options given as parameters to this function. * Uses another config table that allows for turning all future child partitions into a new parent automatically. * To avoid logical complications and contention issues, ALL subpartitions must be maintained using run_maintenance(). * This means the automatic, trigger based partition creation for serial partitioning will not work if it is a subpartition. */ CREATE FUNCTION create_sub_parent( p_top_parent text , p_control text , p_type text , p_interval text , 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 boolean DEFAULT false , p_jobmon boolean DEFAULT true , p_debug boolean DEFAULT false) RETURNS boolean LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_last_partition text; v_row record; v_row_last_part record; v_run_maint boolean; v_sql text; v_success boolean := false; v_top_type text; BEGIN SELECT use_run_maintenance INTO v_run_maint FROM @extschema@.part_config WHERE parent_table = p_top_parent; IF v_run_maint 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; ELSIF v_run_maint = false THEN RAISE EXCEPTION 'Any parent table that will be part of a sub-partitioned set (on any level) must have use_run_maintenance set to true in part_config table, even for serial partitioning. See documentation for more info.'; END IF; FOR v_row IN -- Loop through all current children to turn them into partitioned tables SELECT partition_schemaname||'.'||partition_tablename AS child_table FROM @extschema@.show_partitions(p_top_parent) LOOP -- Just call existing create_parent() function but add the given parameters to the part_config_sub table as well v_sql := format('SELECT @extschema@.create_parent( p_parent_table := %L , p_control := %L , p_type := %L , p_interval := %L , p_constraint_cols := %L , p_premake := %L , p_use_run_maintenance := %L , p_start_partition := %L , p_inherit_fk := %L , p_epoch := %L , p_jobmon := %L , p_debug := %L )' , v_row.child_table , p_control , p_type , p_interval , p_constraint_cols , p_premake , true , p_start_partition , p_inherit_fk , p_epoch , p_jobmon , p_debug); EXECUTE v_sql; END LOOP; INSERT INTO @extschema@.part_config_sub ( sub_parent , sub_control , sub_partition_type , sub_partition_interval , sub_constraint_cols , sub_premake , sub_inherit_fk , sub_use_run_maintenance , sub_epoch , sub_jobmon) VALUES ( p_top_parent , p_control , p_type , p_interval , p_constraint_cols , p_premake , p_inherit_fk , true , p_epoch , p_jobmon); v_success := true; RETURN v_success; END $$; pg_partman-2.2.2/sql/functions/create_trigger.sql000066400000000000000000000017011262146621700221710ustar00rootroot00000000000000CREATE FUNCTION create_trigger(p_parent_table text) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_function_name text; v_new_length int; v_parent_schema text; v_parent_tablename text; v_trig_name text; v_trig_sql text; BEGIN SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; v_trig_name := @extschema@.check_name_length(p_object_name := v_parent_tablename, p_suffix := '_part_trig'); -- Ensure function name matches the naming pattern v_function_name := @extschema@.check_name_length(v_parent_tablename, '_part_trig_func', FALSE); v_trig_sql := format('CREATE TRIGGER %I BEFORE INSERT ON %I.%I FOR EACH ROW EXECUTE PROCEDURE %I.%I()' , v_trig_name , v_parent_schema , v_parent_tablename , v_parent_schema , v_function_name); EXECUTE v_trig_sql; END $$; pg_partman-2.2.2/sql/functions/drop_constraints.sql000066400000000000000000000113401262146621700225760ustar00rootroot00000000000000/* * Drop constraints managed by pg_partman */ CREATE FUNCTION drop_constraints(p_parent_table text, p_child_table text, p_debug boolean DEFAULT false) RETURNS void LANGUAGE plpgsql AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_child_schemaname text; v_child_tablename text; v_col text; v_constraint_cols text[]; v_existing_constraint_name text; v_exists boolean := FALSE; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_sql text; v_step_id bigint; BEGIN SELECT constraint_cols , jobmon INTO v_constraint_cols , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_constraint_cols IS NULL THEN RAISE EXCEPTION 'Given parent table (%) not set up for constraint management (constraint_cols is NULL)', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', '@extschema@,'||v_jobmon_schema, 'false'); END IF; END IF; SELECT schemaname, tablename INTO v_child_schemaname, v_child_tablename FROM pg_catalog.pg_tables WHERE schemaname||'.'||tablename = p_child_table; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job(format('PARTMAN DROP CONSTRAINT: %s', p_parent_table)); v_step_id := add_step(v_job_id, 'Entering constraint drop loop'); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; FOREACH v_col IN ARRAY v_constraint_cols LOOP SELECT con.conname INTO v_existing_constraint_name 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 WHERE c.relname = v_child_tablename AND n.nspname = v_child_schemaname AND con.conname LIKE 'partmanconstr_%' AND con.contype = 'c' AND a.attname = v_col AND ARRAY[a.attnum] <@ con.conkey AND a.attisdropped = false; IF v_existing_constraint_name IS NOT NULL THEN v_exists := TRUE; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, format('Dropping constraint on column: %s', v_col)); END IF; v_sql := format('ALTER TABLE %I.%I DROP CONSTRAINT %I', v_child_schemaname, v_child_tablename, v_existing_constraint_name); IF p_debug THEN RAISE NOTICE 'Constraint drop query: %', v_sql; END IF; EXECUTE v_sql; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', format('Drop constraint query: %s', v_sql)); END IF; END IF; END LOOP; IF v_jobmon_schema IS NOT NULL AND v_exists IS FALSE THEN v_step_id := add_step(v_job_id, format('No constraints found to drop on child table: %s', p_child_table)); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', v_old_search_path, 'false'); END IF; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN DROP CONSTRAINT: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; pg_partman-2.2.2/sql/functions/drop_partition_id.sql000066400000000000000000000247631262146621700227310ustar00rootroot00000000000000/* * 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. */ CREATE FUNCTION 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 SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_adv_lock boolean; v_control text; v_drop_count int := 0; v_id_position int; v_index record; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_max bigint; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_partition_interval bigint; v_partition_id bigint; v_retention bigint; v_retention_keep_index boolean; v_retention_keep_table boolean; v_retention_schema text; v_row record; v_row_max_id record; v_step_id bigint; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman drop_partition_id')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'drop_partition_id already running.'; RETURN 0; END IF; -- Allow override of configuration options IF p_retention IS NULL THEN SELECT partition_interval::bigint , control , retention::bigint , retention_keep_table , retention_keep_index , retention_schema , jobmon INTO v_partition_interval , v_control , v_retention , v_retention_keep_table , v_retention_keep_index , v_retention_schema , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND partition_type = 'id' 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_interval::bigint , control , retention_keep_table , retention_keep_index , retention_schema , jobmon INTO v_partition_interval , v_control , v_retention_keep_table , v_retention_keep_index , v_retention_schema , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND partition_type = 'id'; v_retention := p_retention; IF v_partition_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', '@extschema@,'||v_jobmon_schema, 'false'); END IF; END IF; IF p_keep_table IS NOT NULL THEN v_retention_keep_table = p_keep_table; END IF; IF p_keep_index IS NOT NULL THEN v_retention_keep_index = p_keep_index; END IF; IF p_retention_schema IS NOT NULL THEN v_retention_schema = p_retention_schema; END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = p_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. FOR v_row_max_id IN SELECT partition_schemaname, partition_tablename FROM @extschema@.show_partitions(p_parent_table, 'DESC') LOOP EXECUTE format('SELECT max(%I) FROM %I.%I', v_control, v_row_max_id.partition_schemaname, v_row_max_id.partition_tablename) INTO v_max; IF v_max IS NOT NULL THEN EXIT; END IF; END LOOP; -- Loop through child tables of the given parent FOR v_row IN SELECT partition_schemaname, partition_tablename FROM @extschema@.show_partitions(p_parent_table, 'ASC') LOOP v_id_position := (length(v_row.partition_tablename) - position('p_' in reverse(v_row.partition_tablename))) + 2; v_partition_id := substring(v_row.partition_tablename from v_id_position)::bigint; -- Add one interval since partition names contain the start of the constraint period IF v_retention <= (v_max - (v_partition_id + v_partition_interval)) THEN -- Only create a jobmon entry if there's actual retention work done IF v_jobmon_schema IS NOT NULL AND v_job_id IS NULL THEN v_job_id := add_job(format('PARTMAN DROP ID PARTITION: %s', p_parent_table)); END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, format('Uninherit table %s.%s from %s', v_row.partition_schemaname, v_row.partition_tablename, p_parent_table)); END IF; 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; 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; EXECUTE format('DROP TABLE %I.%I CASCADE', 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 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 AND n1.nspname = v_row.partition_schema ) 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; 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' , v_row.partition_schemaname , v_row.partition_tablename , v_retention_schema)); END IF; EXECUTE format('ALTER TABLE %I.%I SET SCHEMA %I' , v_row.partition_schemaname , v_row.partition_tablename , v_retention_schema); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; -- End retention schema if -- If child table is a subpartition, remove it from part_config & part_config_sub (should cascade due to FK) DELETE FROM @extschema@.part_config WHERE parent_table = v_row.partition_schemaname ||'.'||v_row.partition_tablename; v_drop_count := v_drop_count + 1; END IF; -- End retention check IF END LOOP; -- End child table loop IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Finished partition drop maintenance'); PERFORM update_step(v_step_id, 'OK', format('%s partitions dropped.', v_drop_count)); PERFORM close_job(v_job_id); END IF; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', v_old_search_path, 'false'); END IF; RETURN v_drop_count; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN DROP ID PARTITION: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; pg_partman-2.2.2/sql/functions/drop_partition_time.sql000066400000000000000000000274061262146621700232700ustar00rootroot00000000000000/* * Function to drop child tables from a time-based partition set. * Options to move table to different schema, drop only indexes or actually drop the table from the database. */ CREATE FUNCTION 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) RETURNS int LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_adv_lock boolean; v_datetime_string text; v_drop_count int := 0; v_index record; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_partition_interval interval; v_partition_timestamp timestamp; v_quarter text; v_retention interval; v_retention_keep_index boolean; v_retention_keep_table boolean; v_retention_schema text; v_row record; v_step_id bigint; v_time_position int; v_type text; v_year text; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman drop_partition_time')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'drop_partition_time already running.'; RETURN 0; END IF; -- Allow override of configuration options IF p_retention IS NULL THEN SELECT partition_type , partition_interval::interval , retention::interval , retention_keep_table , retention_keep_index , datetime_string , retention_schema , jobmon INTO v_type , v_partition_interval , v_retention , v_retention_keep_table , v_retention_keep_index , v_datetime_string , v_retention_schema , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (partition_type = 'time' OR partition_type = 'time-custom') 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 , retention_keep_table , retention_keep_index , datetime_string , retention_schema , jobmon INTO v_type , v_partition_interval , v_retention_keep_table , v_retention_keep_index , v_datetime_string , v_retention_schema , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (partition_type = 'time' OR partition_type = 'time-custom'); v_retention := p_retention; IF v_partition_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', '@extschema@,'||v_jobmon_schema, 'false'); END IF; END IF; IF p_keep_table IS NOT NULL THEN v_retention_keep_table = p_keep_table; END IF; IF p_keep_index IS NOT NULL THEN v_retention_keep_index = p_keep_index; END IF; IF p_retention_schema IS NOT NULL THEN v_retention_schema = p_retention_schema; END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; -- Loop through child tables of the given parent FOR v_row IN SELECT partition_schemaname, partition_tablename FROM @extschema@.show_partitions(p_parent_table, 'DESC') LOOP -- pull out datetime portion of partition's tablename to make the next one v_time_position := (length(v_row.partition_tablename) - position('p_' in reverse(v_row.partition_tablename))) + 2; IF v_partition_interval <> '3 months' OR (v_partition_interval = '3 months' AND v_type = 'time-custom') THEN v_partition_timestamp := to_timestamp(substring(v_row.partition_tablename from v_time_position), v_datetime_string); ELSE -- to_timestamp doesn't recognize 'Q' date string formater. Handle it v_year := split_part(substring(v_row.partition_tablename from v_time_position), 'q', 1); v_quarter := split_part(substring(v_row.partition_tablename from v_time_position), 'q', 2); CASE WHEN v_quarter = '1' THEN v_partition_timestamp := to_timestamp(v_year || '-01-01', 'YYYY-MM-DD'); WHEN v_quarter = '2' THEN v_partition_timestamp := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); WHEN v_quarter = '3' THEN v_partition_timestamp := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); WHEN v_quarter = '4' THEN v_partition_timestamp := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); END CASE; END IF; -- Add one interval since partition names contain the start of the constraint period IF v_retention < (CURRENT_TIMESTAMP - (v_partition_timestamp + v_partition_interval)) THEN -- Only create a jobmon entry if there's actual retention work done IF v_jobmon_schema IS NOT NULL AND v_job_id IS NULL THEN v_job_id := add_job(format('PARTMAN DROP TIME PARTITION: %s', p_parent_table)); END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, format('Uninherit table %s.%s from %s' , v_row.partition_schemaname , v_row.partition_tablename , p_parent_table)); END IF; 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_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; 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; EXECUTE format('DROP TABLE %I.%I CASCADE', 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 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 AND n1.nspname = v_row.partition_schemaname ) 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_parent_schema, 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; 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' , v_row.partition_schemaname , v_row.partition_tablename , v_retention_schema)); END IF; EXECUTE format('ALTER TABLE %I.%I SET SCHEMA %I', v_row.partition_schemaname, v_row.partition_tablename, v_retention_schema); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; -- End retention schema if -- If child table is a subpartition, remove it from part_config & part_config_sub (should cascade due to FK) DELETE FROM @extschema@.part_config WHERE parent_table = v_row.partition_schemaname||'.'||v_row.partition_tablename; v_drop_count := v_drop_count + 1; END IF; -- End retention check IF END LOOP; -- End child table loop IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Finished partition drop maintenance'); PERFORM update_step(v_step_id, 'OK', format('%s partitions dropped.', v_drop_count)); PERFORM close_job(v_job_id); END IF; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', v_old_search_path, 'false'); END IF; RETURN v_drop_count; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN DROP TIME PARTITION: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; pg_partman-2.2.2/sql/functions/partition_data_id.sql000066400000000000000000000115211262146621700226620ustar00rootroot00000000000000/* * Populate the child table(s) of an id-based partition set with old data from the original parent */ CREATE FUNCTION partition_data_id(p_parent_table text, p_batch_count int DEFAULT 1, p_batch_interval int DEFAULT NULL, p_lock_wait numeric DEFAULT 0, p_order text DEFAULT 'ASC') RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_control text; v_current_partition_name text; 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_rowcount bigint; v_sql text; v_start_control bigint; v_total_rows bigint := 0; BEGIN SELECT partition_interval::bigint , control INTO v_partition_interval , v_control FROM @extschema@.part_config WHERE parent_table = p_parent_table AND partition_type = 'id'; IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; IF p_batch_interval IS NULL OR p_batch_interval > v_partition_interval THEN p_batch_interval := v_partition_interval; END IF; FOR i IN 1..p_batch_count LOOP IF p_order = 'ASC' THEN EXECUTE format('SELECT min(%I) FROM ONLY %I.%I', v_control, v_parent_schema, v_parent_tablename) INTO v_start_control; IF v_start_control IS NULL THEN EXIT; END IF; v_min_partition_id = v_start_control - (v_start_control % v_partition_interval); v_partition_id := ARRAY[v_min_partition_id]; -- Check if custom batch interval overflows current partition maximum IF (v_start_control + p_batch_interval) >= (v_min_partition_id + v_partition_interval) THEN v_max_partition_id := v_min_partition_id + v_partition_interval; ELSE v_max_partition_id := v_start_control + p_batch_interval; END IF; ELSIF p_order = 'DESC' THEN EXECUTE 'SELECT max('||v_control||') FROM ONLY '||p_parent_table INTO v_start_control; IF v_start_control IS NULL THEN EXIT; END IF; v_min_partition_id = v_start_control - (v_start_control % v_partition_interval); -- Must be greater than max value still in parent table since query below grabs < max v_max_partition_id := v_min_partition_id + v_partition_interval; v_partition_id := ARRAY[v_min_partition_id]; -- Make sure minimum doesn't underflow current partition minimum IF (v_start_control - p_batch_interval) >= v_min_partition_id THEN v_min_partition_id = v_start_control - p_batch_interval; END IF; ELSE RAISE EXCEPTION 'Invalid value for p_order. Must be ASC or DESC'; END IF; -- do some locking with timeout, if required IF p_lock_wait > 0 THEN v_lock_iter := 0; WHILE v_lock_iter <= 5 LOOP v_lock_iter := v_lock_iter + 1; BEGIN v_sql := format('SELECT * FROM ONLY %I.%I WHERE %I >= %s AND %I < %s FOR UPDATE NOWAIT' , v_parent_schema , v_parent_tablename , v_control , v_min_partition_id , v_control , v_max_partition_id); EXECUTE v_sql; 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 RETURN -1; END IF; END IF; PERFORM @extschema@.create_partition_id(p_parent_table, v_partition_id); v_current_partition_name := @extschema@.check_name_length(v_parent_tablename, v_min_partition_id::text, TRUE); EXECUTE format('WITH partition_data AS ( DELETE FROM ONLY %I.%I WHERE %I >= %s AND %I < %s RETURNING *) INSERT INTO %I.%I SELECT * FROM partition_data' , v_parent_schema , v_parent_tablename , v_control , v_min_partition_id , v_control , v_max_partition_id , v_parent_schema , v_current_partition_name); GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total_rows := v_total_rows + v_rowcount; IF v_rowcount = 0 THEN EXIT; END IF; END LOOP; PERFORM @extschema@.create_function_id(p_parent_table); RETURN v_total_rows; END $$; pg_partman-2.2.2/sql/functions/partition_data_time.sql000066400000000000000000000235051262146621700232310ustar00rootroot00000000000000/* * Populate the child table(s) of a time-based partition set with old data from the original parent */ CREATE FUNCTION 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') RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_control text; v_datetime_string text; v_current_partition_name text; v_epoch boolean; v_last_partition text; v_lock_iter int := 1; v_lock_obtained boolean := FALSE; v_max_partition_timestamp timestamp; v_min_partition_timestamp timestamp; v_parent_schema text; v_parent_tablename text; v_partition_interval interval; v_partition_suffix text; v_partition_timestamp timestamp[]; v_quarter text; v_rowcount bigint; v_sql text; v_start_control timestamp; v_time_position int; v_total_rows bigint := 0; v_type text; v_year text; BEGIN SELECT partition_type , partition_interval::interval , control , datetime_string , epoch INTO v_type , v_partition_interval , v_control , v_datetime_string , v_epoch FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (partition_type = 'time' OR partition_type = 'time-custom'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF p_batch_interval IS NULL OR p_batch_interval > v_partition_interval THEN p_batch_interval := v_partition_interval; END IF; SELECT partition_tablename INTO v_last_partition FROM @extschema@.show_partitions(p_parent_table, 'DESC') LIMIT 1; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; FOR i IN 1..p_batch_count LOOP IF v_epoch = false THEN IF p_order = 'ASC' THEN EXECUTE format('SELECT min(%I) FROM ONLY %I.%I', v_control, v_parent_schema, v_parent_tablename) INTO v_start_control; ELSIF p_order = 'DESC' THEN EXECUTE format('SELECT max(%I) FROM ONLY %I.%I', v_control, v_parent_schema, v_parent_tablename) INTO v_start_control; ELSE RAISE EXCEPTION 'Invalid value for p_order. Must be ASC or DESC'; END IF; ELSE IF p_order = 'ASC' THEN EXECUTE format('SELECT to_timestamp(min(%I)) FROM ONLY %I.%I', v_control, v_parent_schema, v_parent_tablename) INTO v_start_control; ELSIF p_order = 'DESC' THEN EXECUTE format('SELECT to_timestamp(max(%I)) FROM ONLY %I.%I', v_control, v_parent_schema, v_parent_tablename) INTO v_start_control; ELSE RAISE EXCEPTION 'Invalid value for p_order. Must be ASC or DESC'; END IF; END IF; IF v_start_control IS NULL THEN EXIT; END IF; IF v_type = 'time' 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_type = 'time-custom' THEN -- Keep going backwards, checking if the time interval encompases the current v_start_control value v_time_position := (length(v_last_partition) - position('p_' in reverse(v_last_partition))) + 2; v_min_partition_timestamp := to_timestamp(substring(v_last_partition from v_time_position), v_datetime_string); 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 v_max_partition_timestamp := v_min_partition_timestamp; BEGIN v_min_partition_timestamp := v_min_partition_timestamp - v_partition_interval; 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_interval; END; END IF; END LOOP; END IF; v_partition_timestamp := ARRAY[v_min_partition_timestamp]; IF p_order = 'ASC' THEN IF (v_start_control + p_batch_interval) >= (v_min_partition_timestamp + v_partition_interval) THEN v_max_partition_timestamp := v_min_partition_timestamp + v_partition_interval; ELSE v_max_partition_timestamp := v_start_control + p_batch_interval; END IF; ELSIF p_order = 'DESC' THEN -- Must be greater than max value still in parent table since query below grabs < max v_max_partition_timestamp := v_min_partition_timestamp + v_partition_interval; -- Make sure minimum doesn't underflow current partition minimum IF (v_start_control - p_batch_interval) >= v_min_partition_timestamp THEN v_min_partition_timestamp = v_start_control - p_batch_interval; END IF; ELSE RAISE EXCEPTION 'Invalid value for p_order. Must be ASC or DESC'; END IF; -- do some locking with timeout, if required IF p_lock_wait > 0 THEN v_lock_iter := 0; WHILE v_lock_iter <= 5 LOOP v_lock_iter := v_lock_iter + 1; BEGIN IF v_epoch = false THEN v_sql := format('SELECT * FROM ONLY %I.%I WHERE %I >= %L AND %I < %L FOR UPDATE NOWAIT' , v_parent_schema , v_parent_tablename , v_control , v_min_partition_timestamp , v_control , v_max_partition_timestamp); ELSE v_sql := format('SELECT * FROM ONLY %I.%I WHERE to_timestamp(%I) >= %L AND to_timestamp(%I) < %L FOR UPDATE NOWAIT' , v_parent_schema , v_parent_tablename , v_control , v_min_partition_timestamp , v_control , v_max_partition_timestamp); END IF; EXECUTE v_sql; 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 RETURN -1; END IF; END IF; PERFORM @extschema@.create_partition_time(p_parent_table, v_partition_timestamp); -- This suffix generation code is in create_partition_time() as well v_partition_suffix := to_char(v_min_partition_timestamp, v_datetime_string); v_current_partition_name := @extschema@.check_name_length(v_parent_tablename, v_partition_suffix, TRUE); IF v_epoch = false THEN v_sql := format('WITH partition_data AS ( DELETE FROM ONLY %I.%I WHERE %I >= %L AND %I < %L RETURNING *) INSERT INTO %I.%I SELECT * FROM partition_data' , v_parent_schema , v_parent_tablename , v_control , v_min_partition_timestamp , v_control , v_max_partition_timestamp , v_parent_schema , v_current_partition_name); ELSE v_sql := format('WITH partition_data AS ( DELETE FROM ONLY %I.%I WHERE to_timestamp(%I) >= %L AND to_timestamp(%I) < %L RETURNING *) INSERT INTO %I.%I SELECT * FROM partition_data' , v_parent_schema , v_parent_tablename , v_control , v_min_partition_timestamp , v_control , v_max_partition_timestamp , v_parent_schema , v_current_partition_name); END IF; EXECUTE v_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total_rows := v_total_rows + v_rowcount; IF v_rowcount = 0 THEN EXIT; END IF; END LOOP; PERFORM @extschema@.create_function_time(p_parent_table); RETURN v_total_rows; END $$; pg_partman-2.2.2/sql/functions/reapply_privileges.sql000066400000000000000000000155761262146621700231270ustar00rootroot00000000000000/* * Function to re-apply ownership & privileges on all child tables in a partition set using parent table as reference */ CREATE FUNCTION reapply_privileges(p_parent_table text) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_all text[] := ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER']; v_child_owner text; v_child_grant record; v_grant text; v_grantees text[]; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_match boolean; v_old_search_path text; v_parent_owner text; v_parent_schema text; v_parent_tablename text; v_owner_sql text; v_revoke text; v_row record; v_row_revoke record; v_parent_grant record; v_sql text; v_step_id bigint; BEGIN SELECT jobmon INTO v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon IS NULL THEN RAISE EXCEPTION 'Given table is not managed by this extention: %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', '@extschema@,'||v_jobmon_schema, 'false'); END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job(format('PARTMAN RE-APPLYING PRIVILEGES TO ALL CHILD TABLES OF: %s', p_parent_table)); v_step_id := add_step(v_job_id, 'Setting new child table privileges'); END IF; SELECT schemaname, tablename, tableowner INTO v_parent_schema, v_parent_tablename, v_parent_owner FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; FOR v_row IN SELECT partition_schemaname, partition_tablename FROM @extschema@.show_partitions(p_parent_table, 'ASC') LOOP IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'PENDING', format('Currently on child partition in ascending order: %s.%s' , v_row.partition_schemaname , v_row.partition_tablename)); END IF; v_grantees := NULL; FOR v_parent_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types , grantee FROM information_schema.table_privileges WHERE table_schema = v_parent_schema AND table_name = v_parent_tablename GROUP BY grantee LOOP -- Compare parent & child grants. Don't re-apply if it already exists v_match := false; FOR v_child_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types , grantee FROM information_schema.table_privileges WHERE table_schema = v_row.partition_schemaname AND table_name = v_row.partition_tablename GROUP BY grantee LOOP IF v_parent_grant.types = v_child_grant.types AND v_parent_grant.grantee = v_child_grant.grantee THEN v_match := true; END IF; END LOOP; IF v_match = false THEN EXECUTE format('GRANT %s ON %I.%I TO %I' , array_to_string(v_parent_grant.types, ',') , v_row.partition_schemaname , v_row.partition_tablename , v_parent_grant.grantee); SELECT string_agg(r, ',') INTO v_revoke FROM (SELECT unnest(v_all) AS r EXCEPT SELECT unnest(v_parent_grant.types)) x; IF v_revoke IS NOT NULL THEN EXECUTE format('REVOKE %s ON %I.%I FROM %I CASCADE' , v_revoke , v_row.partition_schemaname , v_row.partition_tablename , v_parent_grant.grantee); END IF; END IF; v_grantees := array_append(v_grantees, v_parent_grant.grantee::text); END LOOP; -- Revoke all privileges from roles that have none on the parent IF v_grantees IS NOT NULL THEN FOR v_row_revoke IN SELECT role FROM ( SELECT DISTINCT grantee::text AS role FROM information_schema.table_privileges WHERE table_schema = v_row.partition_schemaname AND table_name = v_row.partition_tablename EXCEPT SELECT unnest(v_grantees)) x LOOP IF v_row_revoke.role IS NOT NULL THEN EXECUTE format('REVOKE ALL ON %I.%I FROM %I' , v_row.partition_schemaname , v_row.partition_tablename , v_row_revoke.role); END IF; END LOOP; END IF; SELECT tableowner INTO v_child_owner FROM pg_tables WHERE schemaname = v_row.partition_schemaname AND tablename = v_row.partition_tablename; IF v_parent_owner <> v_child_owner THEN EXECUTE format('ALTER TABLE %I.%I OWNER TO %I' , v_row.partition_schemaname , v_row.partition_tablename , v_parent_owner); END IF; END LOOP; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); PERFORM close_job(v_job_id); END IF; IF v_jobmon_schema IS NOT NULL THEN EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', v_old_search_path, 'false'); END IF; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN RE-APPLYING PRIVILEGES TO ALL CHILD TABLES OF: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; pg_partman-2.2.2/sql/functions/run_maintenance.sql000066400000000000000000000514661262146621700223660ustar00rootroot00000000000000/* * Function to manage pre-creation of the next partitions in a set. * 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_run_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. * Be aware that constraint exclusion may not work properly until an analyze on the partition set is run. */ CREATE FUNCTION run_maintenance(p_parent_table text DEFAULT NULL, p_analyze boolean DEFAULT true, p_jobmon boolean DEFAULT true, p_debug boolean DEFAULT false) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_adv_lock boolean; v_check_subpart int; v_create_count int := 0; v_current_partition text; v_current_partition_id bigint; v_current_partition_timestamp timestamp; v_datetime_string text; v_drop_count int := 0; v_id_position int; 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 timestamp; v_max_id_parent bigint; v_max_time_parent timestamp; v_next_partition_id bigint; v_next_partition_timestamp timestamp; v_parent_schema text; v_parent_tablename text; v_premade_count int; v_premake_id_max bigint; v_premake_id_min bigint; v_premake_timestamp_min timestamp; v_premake_timestamp_max timestamp; v_quarter text; v_row record; v_row_max_id record; v_row_max_time record; v_row_sub record; v_skip_maint boolean; v_step_id bigint; v_step_overflow_id bigint; v_step_serial_id bigint; v_sub_id_max bigint; v_sub_id_max_suffix bigint; v_sub_id_min bigint; v_sub_parent text; v_sub_timestamp_max timestamp; v_sub_timestamp_max_suffix timestamp; v_sub_timestamp_min timestamp; v_tablename text; v_tables_list_sql text; v_time_position int; v_year text; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman run_maintenance')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'Partman maintenance already running.'; RETURN; END IF; IF p_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; END IF; IF v_jobmon_schema IS NOT NULL THEN EXECUTE format('SELECT %I.add_job(%L)', v_jobmon_schema, 'PARTMAN RUN MAINTENANCE') INTO v_job_id; EXECUTE format('SELECT %I.add_step(%L, %L)', v_jobmon_schema, v_job_id, 'Running maintenance loop') INTO v_step_id; 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! -- Don't have to worry about this in the serial trigger maintenance since subpartitioning requires run_maintenance(). FOR v_row IN SELECT sub_parent FROM @extschema@.part_config_sub LOOP SELECT count(*) INTO v_check_subpart FROM @extschema@.check_subpart_sameconfig(v_row.sub_parent); 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, results are all children of the given parent. Update the differing values to be consistent for your desired values.', v_row.sub_parent; END IF; END LOOP; v_row := NULL; -- Ensure it's reset v_tables_list_sql := 'SELECT parent_table , partition_type , partition_interval , control , premake , datetime_string , undo_in_progress , sub_partition_set_full , epoch FROM @extschema@.part_config WHERE sub_partition_set_full = false'; IF p_parent_table IS NULL THEN v_tables_list_sql := v_tables_list_sql || ' AND use_run_maintenance = true'; ELSE v_tables_list_sql := v_tables_list_sql || format(' AND parent_table = %L', p_parent_table); END IF; FOR v_row IN EXECUTE v_tables_list_sql LOOP CONTINUE WHEN v_row.undo_in_progress; v_skip_maint := true; -- reset every loop SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_row.parent_table; SELECT partition_tablename INTO v_last_partition FROM @extschema@.show_partitions(v_row.parent_table, 'DESC') LIMIT 1; IF p_debug THEN RAISE NOTICE 'run_maint: parent_table: %, v_last_partition: %', v_row.parent_table, v_last_partition; END IF; IF v_row.partition_type = 'time' OR v_row.partition_type = 'time-custom' THEN v_time_position := (length(v_last_partition) - position('p_' in reverse(v_last_partition))) + 2; IF v_row.partition_interval::interval <> '3 months' OR (v_row.partition_interval::interval = '3 months' AND v_row.partition_type = 'time-custom') THEN v_last_partition_timestamp := to_timestamp(substring(v_last_partition from v_time_position), v_row.datetime_string); ELSE -- to_timestamp doesn't recognize 'Q' date string formater. Handle it v_year := split_part(substring(v_last_partition FROM v_time_position), 'q', 1); v_quarter := split_part(substring(v_last_partition FROM v_time_position), 'q', 2); CASE WHEN v_quarter = '1' THEN v_last_partition_timestamp := to_timestamp(v_year || '-01-01', 'YYYY-MM-DD'); WHEN v_quarter = '2' THEN v_last_partition_timestamp := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); WHEN v_quarter = '3' THEN v_last_partition_timestamp := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); WHEN v_quarter = '4' THEN v_last_partition_timestamp := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); END CASE; 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_time IN SELECT partition_schemaname, partition_tablename FROM @extschema@.show_partitions(v_row.parent_table, 'DESC') LOOP IF v_row.epoch = false THEN EXECUTE format('SELECT max(%I)::text FROM %I.%I' , v_row.control , v_row_max_time.partition_schemaname , v_row_max_time.partition_tablename ) INTO v_current_partition_timestamp; ELSE EXECUTE format('SELECT to_timestamp(max(%I))::text FROM %I.%I' , v_row.control , v_row_max_time.partition_schemaname , v_row_max_time.partition_tablename ) INTO v_current_partition_timestamp; 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 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 IF v_row.epoch = false THEN EXECUTE format('SELECT max(%I) FROM ONLY %I.%I', v_row.control, v_parent_schema, v_parent_tablename) INTO v_max_time_parent; ELSE EXECUTE format('SELECT to_timestamp(max(%I)) FROM ONLY %I.%I', v_row.control, v_parent_schema, v_parent_tablename) INTO v_max_time_parent; END IF; IF p_debug THEN RAISE NOTICE 'run_maint: v_current_partition_timestamp: %, v_max_time_parent: %', v_current_partition_timestamp, v_max_time_parent; END IF; IF v_max_time_parent > v_current_partition_timestamp THEN SELECT suffix_timestamp INTO v_current_partition_timestamp FROM @extschema@.show_partition_name(v_row.parent_table, v_max_time_parent::text); END IF; IF v_current_partition_timestamp IS NULL THEN -- Partition set is completely empty. Nothing to do CONTINUE; END IF; -- If this is a subpartition, determine if the last child table has been made. If so, mark it as full so future maintenance runs can skip it SELECT sub_min::timestamp, sub_max::timestamp INTO v_sub_timestamp_min, v_sub_timestamp_max FROM @extschema@.check_subpartition_limits(v_row.parent_table, 'time'); IF v_sub_timestamp_max IS NOT NULL THEN SELECT suffix_timestamp INTO v_sub_timestamp_max_suffix FROM @extschema@.show_partition_name(v_row.parent_table, v_sub_timestamp_max::text); IF v_sub_timestamp_max_suffix = v_last_partition_timestamp THEN -- Final partition for this set is created. Set full and skip it UPDATE @extschema@.part_config SET sub_partition_set_full = true WHERE parent_table = v_row.parent_table; CONTINUE; END IF; END IF; -- Check and see how many premade partitions there are. 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; IF p_debug THEN RAISE NOTICE 'run_maint before loop: current_partition_timestamp: %, v_premade_count: %, v_sub_timestamp_min: %, v_sub_timestamp_max: %' , v_current_partition_timestamp , v_premade_count , v_sub_timestamp_min , v_sub_timestamp_max; END IF; -- 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 IF p_debug THEN RAISE NOTICE 'run_maint: parent_table: %, v_premade_count: %, v_next_partition_timestamp: %', v_row.parent_table, v_premade_count, v_next_partition_timestamp; END IF; IF v_next_partition_timestamp < v_sub_timestamp_min OR v_next_partition_timestamp > v_sub_timestamp_max THEN -- With subpartitioning, no need to run if the timestamp is not in the parent table's range EXIT; END IF; 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 IF v_jobmon_schema IS NOT NULL THEN EXECUTE format('SELECT %I.add_step(%L, %L)', v_jobmon_schema, v_job_id, 'Attempted partition time interval is outside PostgreSQL''s supported time range.') INTO v_step_overflow_id; EXECUTE format('SELECT %I.update_step(%L, %L, %L)', v_jobmon_schema, v_step_overflow_id, 'CRITICAL', 'Child partition creation skippd for parent table '||v_partition_time); END IF; RAISE WARNING 'Attempted partition time interval is outside PostgreSQL''s supported time range. Child partition creation skipped for parent table %', v_row.parent_table; CONTINUE; END; v_last_partition_created := @extschema@.create_partition_time(v_row.parent_table, ARRAY[v_next_partition_timestamp], p_analyze); IF v_last_partition_created THEN v_create_count := v_create_count + 1; PERFORM @extschema@.create_function_time(v_row.parent_table, v_job_id); 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)); END LOOP; ELSIF v_row.partition_type = 'id' THEN -- 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') LOOP EXECUTE format('SELECT max(%I)::text FROM %I.%I' , v_row.control , v_row_max_id.partition_schemaname , v_row_max_id.partition_tablename) INTO v_current_partition_id; IF v_current_partition_id IS NOT NULL THEN SELECT suffix_id INTO v_current_partition_id FROM @extschema@.show_partition_name(v_row.parent_table, v_current_partition_id::text); EXIT; END IF; END LOOP; -- Check for values in the parent 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_parent_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); END IF; IF v_current_partition_id IS NULL THEN -- Partition set is completely empty. Nothing to do CONTINUE; END IF; v_id_position := (length(v_last_partition) - position('p_' in reverse(v_last_partition))) + 2; v_last_partition_id = substring(v_last_partition from v_id_position)::bigint; -- Determine if this table is a child of a subpartition parent. If so, get limits to see if run_maintenance even needs to run for it. SELECT sub_min::bigint, sub_max::bigint INTO v_sub_id_min, v_sub_id_max FROM @extschema@.check_subpartition_limits(v_row.parent_table, 'id'); IF v_sub_id_max IS NOT NULL THEN SELECT suffix_id INTO v_sub_id_max_suffix FROM @extschema@.show_partition_name(v_row.parent_table, v_sub_id_max::text); IF v_sub_id_max_suffix = v_last_partition_id THEN -- Final partition for this set is created. Set full and skip it UPDATE @extschema@.part_config SET sub_partition_set_full = true WHERE parent_table = v_row.parent_table; CONTINUE; END IF; END IF; 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 IF p_debug THEN RAISE NOTICE 'run_maint: parent_table: %, v_premade_count: %, v_next_partition_id: %', v_row.parent_table, v_premade_count, v_next_partition_id; END IF; 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], p_analyze); IF v_last_partition_created THEN v_create_count := v_create_count + 1; PERFORM @extschema@.create_function_id(v_row.parent_table, v_job_id); 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 -- Manage additonal constraints if set PERFORM @extschema@.apply_constraints(p_parent_table := v_row.parent_table, p_job_id := v_job_id, p_debug := p_debug); END LOOP; -- end of creation loop -- Manage dropping old partitions if retention option is set FOR v_row IN SELECT parent_table FROM @extschema@.part_config WHERE retention IS NOT NULL AND undo_in_progress = false AND (partition_type = 'time' OR partition_type = 'time-custom') LOOP IF p_parent_table IS NULL THEN v_drop_count := v_drop_count + @extschema@.drop_partition_time(v_row.parent_table); ELSE -- Only run retention on table given in parameter IF p_parent_table <> v_row.parent_table THEN CONTINUE; ELSE v_drop_count := v_drop_count + @extschema@.drop_partition_time(v_row.parent_table); END IF; END IF; IF v_drop_count > 0 THEN PERFORM @extschema@.create_function_time(v_row.parent_table, v_job_id); END IF; END LOOP; FOR v_row IN SELECT parent_table FROM @extschema@.part_config WHERE retention IS NOT NULL AND undo_in_progress = false AND partition_type = 'id' LOOP IF p_parent_table IS NULL THEN v_drop_count := v_drop_count + @extschema@.drop_partition_id(v_row.parent_table); ELSE -- Only run retention on table given in parameter IF p_parent_table <> v_row.parent_table THEN CONTINUE; ELSE v_drop_count := v_drop_count + @extschema@.drop_partition_id(v_row.parent_table); END IF; END IF; IF v_drop_count > 0 THEN PERFORM @extschema@.create_function_id(v_row.parent_table, v_job_id); END IF; END LOOP; IF v_jobmon_schema IS NOT NULL THEN EXECUTE format('SELECT %I.update_step(%L, %L, ''Partition maintenance finished. %s partitons made. %s partitions dropped.'')' , v_jobmon_schema , v_step_id , 'OK' , v_create_count , v_drop_count); IF v_step_overflow_id IS NOT NULL OR v_step_serial_id IS NOT NULL THEN EXECUTE format('SELECT %I.fail_job(%L)', v_jobmon_schema, v_job_id); ELSE EXECUTE format('SELECT %I.close_job(%L)', v_jobmon_schema, v_job_id); END IF; END IF; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN RUN MAINTENANCE'')', v_jobmon_schema) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; pg_partman-2.2.2/sql/functions/show_partition_name.sql000066400000000000000000000125171262146621700232630ustar00rootroot00000000000000/* * Given a parent table and partition value, return the name of the child partition it would go in. * If using epoch time partitioning, give the text representation of the timestamp NOT the epoch integer value (use to_timestamp() to convert epoch values). * Also returns just the suffix value and true if the child table exists or false if it does not */ CREATE FUNCTION show_partition_name(p_parent_table text, p_value text, OUT partition_table text, OUT suffix_timestamp timestamp, OUT suffix_id bigint, OUT table_exists boolean) RETURNS record LANGUAGE plpgsql STABLE AS $$ DECLARE v_child_exists text; v_datetime_string text; v_max_range timestamptz; v_min_range timestamptz; v_parent_schema text; v_parent_tablename text; v_partition_interval text; v_time_position int; v_type text; BEGIN SELECT partition_type , partition_interval , datetime_string INTO v_type , v_partition_interval , v_datetime_string FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_type IS NULL THEN RAISE EXCEPTION 'Parent table given is not managed by pg_partman (%)', p_parent_table; END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; IF v_parent_tablename IS NULL THEN RAISE EXCEPTION 'Parent table given does not exist (%)', p_parent_table; END IF; IF v_type = 'time' THEN CASE WHEN v_partition_interval::interval = '15 mins' THEN suffix_timestamp := date_trunc('hour', p_value::timestamp) + '15min'::interval * floor(date_part('minute', p_value::timestamp) / 15.0); WHEN v_partition_interval::interval = '30 mins' THEN suffix_timestamp := date_trunc('hour', p_value::timestamp) + '30min'::interval * floor(date_part('minute', p_value::timestamp) / 30.0); WHEN v_partition_interval::interval = '1 hour' THEN suffix_timestamp := date_trunc('hour', p_value::timestamp); WHEN v_partition_interval::interval = '1 day' THEN suffix_timestamp := date_trunc('day', p_value::timestamp); WHEN v_partition_interval::interval = '1 week' THEN suffix_timestamp := date_trunc('week', p_value::timestamp); WHEN v_partition_interval::interval = '1 month' THEN suffix_timestamp := date_trunc('month', p_value::timestamp); WHEN v_partition_interval::interval = '3 months' THEN suffix_timestamp := date_trunc('quarter', p_value::timestamp); WHEN v_partition_interval::interval = '1 year' THEN suffix_timestamp := date_trunc('year', p_value::timestamp); END CASE; partition_table := v_parent_schema||'.'||@extschema@.check_name_length(v_parent_tablename, to_char(suffix_timestamp, v_datetime_string), TRUE); ELSIF v_type = 'id' THEN suffix_id := (p_value::bigint - (p_value::bigint % v_partition_interval::bigint)); partition_table := v_parent_schema||'.'||@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::timestamp >= 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::timestamp < 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::timestamp < 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::timestamp >= 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 := v_parent_schema||'.'||@extschema@.check_name_length(v_parent_tablename, to_char(suffix_timestamp, v_datetime_string), TRUE); END IF; END IF; SELECT schemaname, tablename INTO v_child_exists FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = partition_table; IF v_child_exists IS NOT NULL THEN table_exists := true; ELSE table_exists := false; END IF; RETURN; END $$; pg_partman-2.2.2/sql/functions/show_partitions.sql000066400000000000000000000061341262146621700224440ustar00rootroot00000000000000/* * Function to list all child partitions in a set. */ CREATE FUNCTION show_partitions (p_parent_table text, p_order text DEFAULT 'ASC') RETURNS TABLE (partition_schemaname text, partition_tablename text) LANGUAGE plpgsql STABLE SECURITY DEFINER AS $$ DECLARE v_datetime_string text; v_parent_schema text; v_parent_tablename text; v_partition_interval text; v_quarter text; v_type text; v_year text; BEGIN IF upper(p_order) NOT IN ('ASC', 'DESC') THEN RAISE EXCEPTION 'p_order paramter must be one of the following values: ASC, DESC'; END IF; SELECT partition_type , partition_interval , datetime_string INTO v_type , v_partition_interval , v_datetime_string FROM @extschema@.part_config WHERE parent_table = p_parent_table; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname||'.'||tablename = p_parent_table; IF v_type IN ('time', 'time-custom') THEN IF v_partition_interval::interval <> '3 months' OR (v_partition_interval::interval = '3 months' AND v_type = 'time-custom') THEN RETURN QUERY EXECUTE 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 ORDER BY to_timestamp(substring(c.relname from ((length(c.relname) - position(''p_'' in reverse(c.relname))) + 2) ), %L) %s' , v_parent_schema , v_parent_tablename , v_datetime_string , p_order); ELSE -- 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) RETURN QUERY EXECUTE 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 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' , v_parent_schema , v_parent_tablename , p_order , p_order); END IF; ELSIF v_type = 'id' THEN RETURN QUERY EXECUTE 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 ORDER BY substring(c.relname from ((length(c.relname) - position(''p_'' in reverse(c.relname))) + 2) )::bigint %s' , v_parent_schema , v_parent_tablename , p_order); END IF; END $$; pg_partman-2.2.2/sql/functions/undo_partition.sql000066400000000000000000000247001262146621700222450ustar00rootroot00000000000000/* * Function to undo partitioning. * Will actually work on any parent/child table set, not just ones created by pg_partman. */ CREATE FUNCTION undo_partition(p_parent_table text, p_batch_count int DEFAULT 1, p_keep_table boolean DEFAULT true, p_jobmon boolean DEFAULT true, p_lock_wait numeric DEFAULT 0) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_adv_lock boolean; v_batch_loop_count bigint := 0; v_child_count bigint; v_child_table text; v_copy_sql text; v_function_name text; v_job_id bigint; v_jobmon_schema text; v_lock_iter int := 1; v_lock_obtained boolean := FALSE; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_partition_interval interval; v_rowcount bigint; v_step_id bigint; v_total bigint := 0; v_trig_name text; v_undo_count int := 0; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman undo_partition')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'undo_partition already running.'; RETURN 0; END IF; IF p_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', '@extschema@,'||v_jobmon_schema, 'false'); END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job(format('PARTMAN UNDO PARTITIONING: ', p_parent_table)); v_step_id := add_step(v_job_id, format('Undoing partitioning for table ', p_parent_table)); END IF; -- Stops new time partitons 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; -- Stop data going into child tables and stop new id partitions from being made. SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; 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); SELECT tgname INTO v_trig_name FROM pg_catalog.pg_trigger t WHERE tgname = v_trig_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'; RETURN -1; 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 EXECUTE format('DROP FUNCTION IF EXISTS %I.%I()', v_parent_schema, v_function_name); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Stopped partition creation process. Removed trigger & trigger function'); END IF; WHILE v_batch_loop_count < p_batch_count LOOP -- Get ordered list of child table in set. Store in variable one at a time per loop until none are left. WITH parent_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_parent_tablename AND n1.nspname = v_parent_schema ) SELECT c.relname INTO v_child_table FROM pg_catalog.pg_inherits i JOIN pg_catalog.pg_class c ON i.inhrelid = c.oid JOIN parent_info p ON i.inhparent = p.oid ORDER BY i.inhrelid ASC; EXIT WHEN v_child_table IS NULL; EXECUTE format('SELECT count(*) FROM %I.%I', v_parent_schema, v_child_table) INTO v_child_count; IF v_child_count = 0 THEN -- No rows left in this child table. Remove from partition set. -- lockwait timeout for table 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_child_table); 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 child table for removal from partition set'; RETURN -1; END IF; END IF; -- END p_lock_wait IF v_lock_obtained := FALSE; -- reset for reuse later EXECUTE format('ALTER TABLE %I.%I NO INHERIT %I.%I', v_parent_schema, v_child_table, v_parent_schema, v_parent_tablename); IF p_keep_table = false THEN EXECUTE format('DROP TABLE %I.%I', v_parent_schema, v_child_table); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', format('Child table DROPPED. Moved %s rows to parent', COALESCE(v_rowcount, 0))); END IF; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', format('Child table UNINHERITED, not DROPPED. Copied %s rows to parent', COALESCE(v_rowcount, 0))); END IF; END IF; v_undo_count := v_undo_count + 1; CONTINUE; END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, format('Removing child partition: %s.%s', v_parent_schema, v_child_table)); END IF; -- do some locking with timeout, if required 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('SELECT * FROM %I.%I FOR UPDATE NOWAIT', v_parent_schema, v_child_table); 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 batch of rows to move'; RETURN -1; END IF; END IF; v_copy_sql := format('INSERT INTO %I.%I SELECT * FROM %I.%I' , v_parent_schema , v_parent_tablename , v_parent_schema , v_child_table); EXECUTE v_copy_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total := v_total + v_rowcount; EXECUTE format('ALTER TABLE %I.%I NO INHERIT %I.%I' , v_parent_schema , v_child_table , v_parent_schema , v_parent_tablename); IF p_keep_table = false THEN EXECUTE format('DROP TABLE %I.%I', v_parent_schema, v_child_table); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', format('Child table DROPPED. Moved %s rows to parent', v_rowcount)); END IF; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', format('Child table UNINHERITED, not DROPPED. Copied %s rows to parent', v_rowcount)); END IF; END IF; v_batch_loop_count := v_batch_loop_count + 1; v_undo_count := v_undo_count + 1; END LOOP; IF v_undo_count = 0 THEN -- FOR loop never ran, so there's no child tables left. DELETE FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing config from pg_partman (if it existed)'); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; RAISE NOTICE 'Copied % row(s) from % child table(s) to the parent: %', v_total, v_undo_count, p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Final stats'); PERFORM update_step(v_step_id, 'OK', format('Copied %s row(s) from %s child table(s) to the parent', v_total, v_undo_count)); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', v_old_search_path, 'false'); END IF; RETURN v_total; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN UNDO PARTITIONING: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; pg_partman-2.2.2/sql/functions/undo_partition_id.sql000066400000000000000000000311451262146621700227220ustar00rootroot00000000000000/* * Function to undo id-based partitioning created by this extension */ CREATE FUNCTION undo_partition_id(p_parent_table text, p_batch_count int DEFAULT 1, p_batch_interval bigint DEFAULT NULL, p_keep_table boolean DEFAULT true, p_lock_wait numeric DEFAULT 0) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_adv_lock boolean; v_batch_loop_count int := 0; v_child_loop_total bigint := 0; v_child_min bigint; v_child_table text; v_control text; v_exists int; v_function_name text; v_inner_loop_count int; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_lock_iter int := 1; v_lock_obtained boolean := FALSE; v_move_sql text; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_partition_interval bigint; v_row record; v_rowcount bigint; v_step_id bigint; v_sub_count int; v_trig_name text; v_total bigint := 0; v_undo_count int := 0; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman undo_partition_id')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'undo_partition_id already running.'; RETURN 0; END IF; SELECT partition_interval::bigint , control , jobmon INTO v_partition_interval , v_control , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND partition_type = 'id'; IF v_partition_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; -- Check if any child tables are themselves partitioned or part of an inheritance tree. Prevent undo at this level if so. -- Need to either lock child tables at all levels or handle the proper removal of triggers on all child tables first -- before multi-level undo can be performed safely. FOR v_row IN SELECT partition_schemaname, partition_tablename FROM @extschema@.show_partitions(p_parent_table) LOOP SELECT count(*) INTO v_sub_count FROM pg_catalog.pg_inherits i JOIN pg_catalog.pg_class c ON i.inhparent = c.oid JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid WHERE c.relname = v_row.partition_tablename AND n.nspname = v_row.partition_schemaname; 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; END IF; END LOOP; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', '@extschema@,'||v_jobmon_schema, 'false'); END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job(format('PARTMAN UNDO PARTITIONING: %s', p_parent_table)); v_step_id := add_step(v_job_id, format('Undoing partitioning for table %s', p_parent_table)); END IF; IF p_batch_interval IS NULL THEN p_batch_interval := v_partition_interval; END IF; -- Stops new time partitons 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; -- Stop data going into child tables and stop new id partitions from being made. SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; 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); SELECT tgname INTO v_trig_name FROM pg_catalog.pg_trigger t WHERE tgname = v_trig_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'; RETURN -1; 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 EXECUTE format('DROP FUNCTION IF EXISTS %I.%I()', v_parent_schema, v_function_name); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Stopped partition creation process. Removed trigger & trigger function'); END IF; <> WHILE v_batch_loop_count < p_batch_count LOOP -- Get ordered list of child table in set. Store in variable one at a time per loop until none are left. WITH parent_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_parent_tablename AND n1.nspname = v_parent_schema ) SELECT c.relname INTO v_child_table FROM pg_catalog.pg_inherits i JOIN pg_catalog.pg_class c ON i.inhrelid = c.oid JOIN parent_info p ON i.inhparent = p.oid ORDER BY i.inhrelid ASC; EXIT WHEN v_child_table IS NULL; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, format('Removing child partition: %s.%s', v_parent_schema, v_child_table)); END IF; EXECUTE format('SELECT min(%I) FROM %I.%I', v_control, v_parent_schema, v_child_table) INTO v_child_min; IF v_child_min IS NULL THEN -- No rows left in this child table. Remove from partition set. -- lockwait timeout for table 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_child_table); 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 child table for removal from partition set'; RETURN -1; END IF; END IF; -- END p_lock_wait IF v_lock_obtained := FALSE; -- reset for reuse later EXECUTE format('ALTER TABLE %I.%I NO INHERIT %I.%I' , v_parent_schema , v_child_table , v_parent_schema , v_parent_tablename); IF p_keep_table = false THEN EXECUTE format('DROP TABLE %I.%I', v_parent_schema, v_child_table); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', format('Child table DROPPED. Moved %s rows to parent', v_child_loop_total)); END IF; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', format('Child table UNINHERITED, not DROPPED. Moved %s rows to parent', v_child_loop_total)); END IF; END IF; v_undo_count := v_undo_count + 1; CONTINUE outer_child_loop; END IF; v_inner_loop_count := 1; v_child_loop_total := 0; <> LOOP -- lockwait timeout for row batches 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('SELECT * FROM %I.%I WHERE %I <= %s FOR UPDATE NOWAIT' , v_parent_schema , v_child_table , v_control , v_child_min + (p_batch_interval * v_inner_loop_count)); 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 batch of rows to move'; RETURN -1; END IF; END IF; -- Get everything from the current child minimum up to the multiples of the given interval v_move_sql := format('WITH move_data AS ( DELETE FROM %I.%I WHERE %I <= %s RETURNING *) INSERT INTO %I.%I SELECT * FROM move_data' , v_parent_schema , v_child_table , v_control , v_child_min + (p_batch_interval * v_inner_loop_count) , v_parent_schema , v_parent_tablename); EXECUTE v_move_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total := v_total + v_rowcount; v_child_loop_total := v_child_loop_total + v_rowcount; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', format('Moved %s rows to parent.', v_child_loop_total)); END IF; EXIT inner_child_loop WHEN v_rowcount = 0; -- exit before loop incr if table is empty v_inner_loop_count := v_inner_loop_count + 1; v_batch_loop_count := v_batch_loop_count + 1; EXIT outer_child_loop WHEN v_batch_loop_count >= p_batch_count; -- Exit outer FOR loop if p_batch_count is reached END LOOP inner_child_loop; END LOOP outer_child_loop; IF v_batch_loop_count < p_batch_count THEN -- FOR loop never ran, so there's no child tables left. DELETE FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing config from pg_partman'); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; RAISE NOTICE 'Copied % row(s) to the parent. Removed % partitions.', v_total, v_undo_count; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Final stats'); PERFORM update_step(v_step_id, 'OK', format('Copied %s row(s) to the parent. Removed %s partitions.', v_total, v_undo_count)); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', v_old_search_path, 'false'); END IF; RETURN v_total; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN UNDO PARTITIONING: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; pg_partman-2.2.2/sql/functions/undo_partition_time.sql000066400000000000000000000327251262146621700232710ustar00rootroot00000000000000/* * Function to undo time-based partitioning created by this extension */ CREATE FUNCTION undo_partition_time(p_parent_table text, p_batch_count int DEFAULT 1, p_batch_interval interval DEFAULT NULL, p_keep_table boolean DEFAULT true, p_lock_wait numeric DEFAULT 0) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_adv_lock boolean; v_batch_loop_count int := 0; v_child_min timestamptz; v_child_loop_total bigint := 0; v_child_table text; v_control text; v_epoch boolean; v_function_name text; v_inner_loop_count int; v_lock_iter int := 1; v_lock_obtained boolean := FALSE; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_move_sql text; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_partition_interval interval; v_row record; v_rowcount bigint; v_step_id bigint; v_sub_count int; v_total bigint := 0; v_trig_name text; v_type text; v_undo_count int := 0; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman undo_partition_time')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'undo_partition_time already running.'; RETURN 0; END IF; SELECT partition_type , partition_interval::interval , control , jobmon , epoch INTO v_type , v_partition_interval , v_control , v_jobmon , v_epoch FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (partition_type = 'time' OR partition_type = 'time-custom'); IF v_partition_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; -- Check if any child tables are themselves partitioned or part of an inheritance tree. Prevent undo at this level if so. -- Need to either lock child tables at all levels or handle the proper removal of triggers on all child tables first -- before multi-level undo can be performed safely. FOR v_row IN SELECT partition_schemaname, partition_tablename FROM @extschema@.show_partitions(p_parent_table) LOOP SELECT count(*) INTO v_sub_count FROM pg_catalog.pg_inherits i JOIN pg_catalog.pg_class c ON i.inhparent = c.oid JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid WHERE c.relname = v_row.partition_tablename AND n.nspname = v_row.partition_schemaname; 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; END IF; END LOOP; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', '@extschema@,'||v_jobmon_schema, 'false'); END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job(format('PARTMAN UNDO PARTITIONING: %s', p_parent_table)); v_step_id := add_step(v_job_id, format('Undoing partitioning for table %s', p_parent_table)); END IF; IF p_batch_interval IS NULL THEN p_batch_interval := v_partition_interval; END IF; -- Stops new time partitons 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; -- Stop data going into child tables. SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; 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); SELECT tgname INTO v_trig_name FROM pg_catalog.pg_trigger t WHERE tgname = v_trig_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'; RETURN -1; 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 EXECUTE format('DROP FUNCTION IF EXISTS %I.%I()', v_parent_schema, v_function_name); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Stopped partition creation process. Removed trigger & trigger function'); END IF; <> WHILE v_batch_loop_count < p_batch_count LOOP -- Get ordered list of child table in set. Store in variable one at a time per loop until none are left. SELECT partition_tablename INTO v_child_table FROM @extschema@.show_partitions(p_parent_table, 'ASC'); EXIT WHEN v_child_table IS NULL; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, format('Removing child partition: %s.%s', v_parent_schema, v_child_table)); END IF; IF v_epoch = false THEN EXECUTE format('SELECT min(%I) FROM %I.%I', v_control, v_parent_schema, v_child_table) INTO v_child_min; ELSE EXECUTE format('SELECT to_timestamp(min(%I)) FROM %I.%I', v_control, v_parent_schema, v_child_table) INTO v_child_min; END IF; IF v_child_min IS NULL THEN -- No rows left in this child table. Remove from partition set. -- lockwait timeout for table 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_child_table); 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 child table for removal from partition set'; RETURN -1; END IF; END IF; -- END p_lock_wait IF v_lock_obtained := FALSE; -- reset for reuse later EXECUTE format('ALTER TABLE %I.%I NO INHERIT %I.%I' , v_parent_schema , v_child_table , v_parent_schema , v_parent_tablename); IF p_keep_table = false THEN EXECUTE format('DROP TABLE %I.%I', v_parent_schema, v_child_table); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', format('Child table DROPPED. Moved %s rows to parent', v_child_loop_total)); END IF; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', format('Child table UNINHERITED, not DROPPED. Moved %s rows to parent', v_child_loop_total)); END IF; END IF; IF v_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; CONTINUE outer_child_loop; END IF; v_inner_loop_count := 1; v_child_loop_total := 0; <> LOOP -- do some locking with timeout, if required 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('SELECT * FROM %I.%I WHERE %I <= %L FOR UPDATE NOWAIT' , v_parent_schema , v_child_table , v_control , v_child_min + (p_batch_interval * v_inner_loop_count)); 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 batch of rows to move'; RETURN -1; END IF; END IF; -- Get everything from the current child minimum up to the multiples of the given interval IF v_epoch = false THEN v_move_sql := format('WITH move_data AS ( DELETE FROM %I.%I WHERE %I <= %L RETURNING *) INSERT INTO %I.%I SELECT * FROM move_data' , v_parent_schema , v_child_table , v_control , v_child_min + (p_batch_interval * v_inner_loop_count) , v_parent_schema , v_parent_tablename); ELSE v_move_sql := format('WITH move_data AS ( DELETE FROM %I.%I WHERE to_timestamp(%I) <= %L RETURNING *) INSERT INTO %I.%I SELECT * FROM move_data' , v_parent_schema , v_child_table , v_control , v_child_min + (p_batch_interval * v_inner_loop_count) , v_parent_schema , v_parent_tablename); END IF; EXECUTE v_move_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total := v_total + v_rowcount; v_child_loop_total := v_child_loop_total + v_rowcount; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', format('Moved %s rows to parent.', v_child_loop_total)); END IF; EXIT inner_child_loop WHEN v_rowcount = 0; -- exit before loop incr if table is empty v_inner_loop_count := v_inner_loop_count + 1; v_batch_loop_count := v_batch_loop_count + 1; EXIT outer_child_loop WHEN v_batch_loop_count >= p_batch_count; -- Exit outer FOR loop if p_batch_count is reached END LOOP inner_child_loop; END LOOP outer_child_loop; IF v_batch_loop_count < p_batch_count THEN -- FOR loop never ran, so there's no child tables left. DELETE FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing config from pg_partman'); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; RAISE NOTICE 'Copied % row(s) to the parent. Removed % partitions.', v_total, v_undo_count; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Final stats'); PERFORM update_step(v_step_id, 'OK', format('Copied %s row(s) to the parent. Removed %s partitions.', v_total, v_undo_count)); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', v_old_search_path, 'false'); END IF; RETURN v_total; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN UNDO PARTITIONING: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; pg_partman-2.2.2/sql/tables/000077500000000000000000000000001262146621700157255ustar00rootroot00000000000000pg_partman-2.2.2/sql/tables/tables.sql000066400000000000000000000060671262146621700177310ustar00rootroot00000000000000CREATE TABLE part_config ( parent_table text NOT NULL , control text NOT NULL , partition_type text NOT NULL , partition_interval text NOT NULL , constraint_cols text[] , premake int NOT NULL DEFAULT 4 , optimize_trigger int NOT NULL DEFAULT 4 , optimize_constraint int NOT NULL DEFAULT 30 , epoch boolean NOT NULL DEFAULT false , inherit_fk boolean NOT NULL DEFAULT true , retention text , retention_schema text , retention_keep_table boolean NOT NULL DEFAULT true , retention_keep_index boolean NOT NULL DEFAULT true , datetime_string text , use_run_maintenance BOOLEAN NOT NULL DEFAULT true , jobmon boolean NOT NULL DEFAULT true , sub_partition_set_full boolean NOT NULL DEFAULT false , undo_in_progress boolean NOT NULL DEFAULT false , CONSTRAINT part_config_parent_table_pkey PRIMARY KEY (parent_table) , CONSTRAINT positive_premake_check CHECK (premake > 0) ); CREATE INDEX part_config_type_idx ON @extschema@.part_config (partition_type); SELECT pg_catalog.pg_extension_config_dump('part_config', ''); -- FK set deferrable because create_parent() inserts to this table before part_config CREATE TABLE part_config_sub ( sub_parent text PRIMARY KEY REFERENCES @extschema@.part_config (parent_table) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED , sub_partition_type text NOT NULL , sub_control text NOT NULL , sub_partition_interval text NOT NULL , sub_constraint_cols text[] , 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 boolean NOT NULL DEFAULT false , sub_inherit_fk boolean NOT NULL DEFAULT true , 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_use_run_maintenance BOOLEAN NOT NULL DEFAULT true , sub_jobmon boolean NOT NULL DEFAULT true ); CREATE TABLE 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 custom_time_partitions USING gist (partition_range); -- Put constraint functions & definitions here because having them separate makes the ordering of their creation harder to control. Some require the above tables to exist first. /* * Check function for config table partition types */ CREATE FUNCTION @extschema@.check_partition_type (p_type text) RETURNS boolean LANGUAGE plpgsql IMMUTABLE SECURITY DEFINER AS $$ DECLARE v_result boolean; BEGIN SELECT p_type IN ('time', 'time-custom', 'id') INTO v_result; RETURN v_result; END $$; ALTER TABLE @extschema@.part_config 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)); pg_partman-2.2.2/sql/types/000077500000000000000000000000001262146621700156175ustar00rootroot00000000000000pg_partman-2.2.2/sql/types/types.sql000066400000000000000000000001051262146621700175000ustar00rootroot00000000000000CREATE TYPE check_parent_table AS (parent_table text, count bigint); pg_partman-2.2.2/src/000077500000000000000000000000001262146621700144435ustar00rootroot00000000000000pg_partman-2.2.2/src/pg_partman_bgw.c000066400000000000000000000410051262146621700175760ustar00rootroot00000000000000/* * pg_partman_bgw.c * * A background worker process for the pg_partman extension to allow * partition maintenance to be scheduled and run within the database * itself without required a third-party scheduler (ex. cron) * */ #include "postgres.h" /* These are always necessary for a bgworker */ #include "miscadmin.h" #include "postmaster/bgworker.h" #include "storage/ipc.h" #include "storage/latch.h" #include "storage/lwlock.h" #include "storage/proc.h" #include "storage/shmem.h" /* these headers are used by this particular worker's code */ #include "access/xact.h" #include "executor/spi.h" #include "fmgr.h" #include "lib/stringinfo.h" #include "pgstat.h" #include "utils/builtins.h" #include "utils/snapmgr.h" #include "tcop/utility.h" PG_MODULE_MAGIC; void _PG_init(void); void pg_partman_bgw_main(Datum); void pg_partman_bgw_run_maint(Datum); /* flags set by signal handlers */ static volatile sig_atomic_t got_sighup = false; static volatile sig_atomic_t got_sigterm = false; /* GUC variables */ static int pg_partman_bgw_interval = 3600; // Default hourly static char *pg_partman_bgw_role = "postgres"; // Default to postgres role static char *pg_partman_bgw_analyze = "on"; static char *pg_partman_bgw_jobmon = "on"; static char *pg_partman_bgw_dbname = NULL; /* * Signal handler for SIGTERM * Set a flag to let the main loop to terminate, and set our latch to wake * it up. */ static void pg_partman_bgw_sigterm(SIGNAL_ARGS) { int save_errno = errno; got_sigterm = true; if (MyProc) SetLatch(&MyProc->procLatch); errno = save_errno; } /* * Signal handler for SIGHUP * Set a flag to tell the main loop to reread the config file, and set * our latch to wake it up. */ static void pg_partman_bgw_sighup(SIGNAL_ARGS) { int save_errno = errno; got_sighup = true; if (MyProc) SetLatch(&MyProc->procLatch); errno = save_errno; } /* * Entrypoint of this module. */ void _PG_init(void) { BackgroundWorker worker; DefineCustomIntVariable("pg_partman_bgw.interval", "How often run_maintenance() is called (in seconds).", NULL, &pg_partman_bgw_interval, 3600, 1, INT_MAX, PGC_SIGHUP, 0, NULL, NULL, NULL); DefineCustomStringVariable("pg_partman_bgw.analyze", "Whether to run an analyze on a partition set whenever a new partition is created during run_maintenance(). Set to 'on' to send TRUE (default). Set to 'off' to send FALSE.", NULL, &pg_partman_bgw_analyze, "on", PGC_SIGHUP, 0, NULL, NULL, NULL); DefineCustomStringVariable("pg_partman_bgw.dbname", "CSV list of specific databases in the cluster to run pg_partman BGW on. This setting is not required and when not set, pg_partman will dynamically figure out which ones to run on. If set, forces the BGW to only run on these specific databases and never any others. Recommend leaving this unset unless necessary.", NULL, &pg_partman_bgw_dbname, NULL, PGC_SIGHUP, 0, NULL, NULL, NULL); DefineCustomStringVariable("pg_partman_bgw.jobmon", "Whether to log run_maintenance() calls to pg_jobmon if it is installed. Set to 'on' to send TRUE (default). Set to 'off' to send FALSE.", NULL, &pg_partman_bgw_jobmon, "on", PGC_SIGHUP, 0, NULL, NULL, NULL); DefineCustomStringVariable("pg_partman_bgw.role", "Role to be used by BGW. Must have execute permissions on run_maintenance()", NULL, &pg_partman_bgw_role, "postgres", PGC_SIGHUP, 0, NULL, NULL, NULL); /* Kept as comment for reference for future development DefineCustomStringVariable("pg_partman_bgw.maintenance_db", "The BGW requires connecting to a local database for reading system catalogs. By default it uses template1. You can change that with this setting if needed.", NULL, &pg_partman_bgw_maint_db, "template1", PGC_SIGHUP, 0, NULL, NULL, NULL); */ if (!process_shared_preload_libraries_in_progress) return; // Start BGW when database starts sprintf(worker.bgw_name, "pg_partman master background worker"); worker.bgw_flags = BGWORKER_SHMEM_ACCESS | BGWORKER_BACKEND_DATABASE_CONNECTION; worker.bgw_start_time = BgWorkerStart_RecoveryFinished; worker.bgw_restart_time = BGW_NEVER_RESTART; worker.bgw_main = pg_partman_bgw_main; worker.bgw_main_arg = CStringGetDatum(pg_partman_bgw_dbname); worker.bgw_notify_pid = 0; RegisterBackgroundWorker(&worker); } void pg_partman_bgw_main(Datum main_arg) { StringInfoData buf; /* Establish signal handlers before unblocking signals. */ pqsignal(SIGHUP, pg_partman_bgw_sighup); pqsignal(SIGTERM, pg_partman_bgw_sigterm); /* We're now ready to receive signals */ BackgroundWorkerUnblockSignals(); /* Keep for when master requires persistent connection elog(LOG, "%s master process initialized with role %s on database %s" , MyBgworkerEntry->bgw_name , pg_partman_bgw_role , pg_partman_bgw_dbname); */ elog(LOG, "%s master process initialized with role %s" , MyBgworkerEntry->bgw_name , pg_partman_bgw_role); initStringInfo(&buf); /* * Main loop: do this until the SIGTERM handler tells us to terminate */ while (!got_sigterm) { BackgroundWorker worker; BackgroundWorkerHandle *handle; BgwHandleStatus status; char *rawstring; int dbcounter; int rc; List *elemlist; ListCell *l; pid_t pid; /* * Background workers mustn't call usleep() or any direct equivalent: * instead, they may wait on their process latch, which sleeps as * necessary, but is awakened if postmaster dies. That way the * background process goes away immediately in an emergency. */ rc = WaitLatch(&MyProc->procLatch, WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH, pg_partman_bgw_interval * 1000L); ResetLatch(&MyProc->procLatch); /* emergency bailout if postmaster has died */ if (rc & WL_POSTMASTER_DEATH) { proc_exit(1); } /* In case of a SIGHUP, just reload the configuration. */ if (got_sighup) { got_sighup = false; ProcessConfigFile(PGC_SIGHUP); } /* In case of a SIGTERM in middle of loop, stop all further processing and return from BGW function to allow it to exit cleanly. */ if (got_sigterm) { elog(LOG, "pg_partman master BGW received SIGTERM. Shutting down."); 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 if (pg_partman_bgw_dbname != NULL) { rawstring = pstrdup(pg_partman_bgw_dbname); // Parse string into list of identifiers if (!SplitIdentifierString(rawstring, ',', &elemlist)) { // syntax error in list pfree(rawstring); list_free(elemlist); ereport(LOG, (errcode(ERRCODE_SYNTAX_ERROR), 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; worker.bgw_start_time = BgWorkerStart_RecoveryFinished; worker.bgw_restart_time = BGW_NEVER_RESTART; worker.bgw_main = NULL; sprintf(worker.bgw_library_name, "pg_partman_bgw"); sprintf(worker.bgw_function_name, "pg_partman_bgw_run_maint"); sprintf(worker.bgw_name, "pg_partman dynamic background worker (dbname=%s)", dbname); worker.bgw_main_arg = Int32GetDatum(dbcounter); worker.bgw_notify_pid = MyProcPid; dbcounter++; if (!RegisterDynamicBackgroundWorker(&worker, &handle)) elog(FATAL, "Unable to register dynamic background worker for pg_partman"); continue; status = WaitForBackgroundWorkerStartup(handle, &pid); if (status == BGWH_STOPPED) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_RESOURCES), errmsg("Could not start dynamic pg_partman background process"), errhint("More details may be available in the server log."))); if (status == BGWH_POSTMASTER_DIED) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_RESOURCES), errmsg("Cannot start dynamic pg_partman background processes without postmaster"), errhint("Kill all remaining database processes and restart the database."))); Assert(status == BGWH_STARTED); } pfree(rawstring); list_free(elemlist); } else { // pg_partman_bgw_dbname if null elog(DEBUG1, "pg_partman_bgw.dbname GUC is NULL. Nothing to do in main loop."); } continue; } // end sigterm while } // end main /* * 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 * is passed to determine which database the BGW will run in. */ void pg_partman_bgw_run_maint(Datum arg) { char *analyze; char *dbname = "template1"; char *jobmon; char *partman_schema; char *rawstring; int dbcounter; int db_main_counter = DatumGetInt32(arg); List *elemlist; ListCell *l; int ret; StringInfoData buf; /* Establish signal handlers before unblocking signals. */ pqsignal(SIGHUP, pg_partman_bgw_sighup); pqsignal(SIGTERM, pg_partman_bgw_sigterm); /* We're now ready to receive signals */ BackgroundWorkerUnblockSignals(); 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 if (!SplitIdentifierString(rawstring, ',', &elemlist)) { // syntax error in list pfree(rawstring); list_free(elemlist); ereport(LOG, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("invalid list syntax in parameter \"pg_partman_bgw.dbname\" in postgresql.conf"))); return; } dbcounter = 0; foreach(l, elemlist) { elog(DEBUG1, "Entered foreach loop: name (%s), db_main_counter (%d), dbcounter (%d)", (char *) lfirst(l), db_main_counter, dbcounter); if (db_main_counter == dbcounter) { dbname = (char *) lfirst(l); elog(DEBUG1, "Parsing list: %s (%d)", dbname, dbcounter); } dbcounter++; } if (strcmp(dbname, "template1") == 0) { elog(DEBUG1, "Default database name found in dbname local variable (\"template1\")."); } elog(DEBUG1, "Before run_maint initialize connection for db %s", dbname); BackgroundWorkerInitializeConnection(dbname, pg_partman_bgw_role); elog(DEBUG1, "After run_maint initialize connection for db %s", dbname); initStringInfo(&buf); SetCurrentStatementStartTimestamp(); StartTransactionCommand(); SPI_connect(); PushActiveSnapshot(GetTransactionSnapshot()); pgstat_report_appname("pg_partman dynamic background worker"); // First determine if pg_partman is even installed in this database appendStringInfo(&buf, "SELECT extname FROM pg_catalog.pg_extension WHERE extname = 'pg_partman'"); pgstat_report_activity(STATE_RUNNING, buf.data); elog(DEBUG1, "Checking if pg_partman extension is installed in database: %s" , dbname); ret = SPI_execute(buf.data, true, 1); if (ret != SPI_OK_SELECT) { elog(FATAL, "Cannot determine if pg_partman is installed in database %s: error code %d", dbname, ret); } if (SPI_processed <= 0) { elog(DEBUG1, "pg_partman not installed in database %s. Nothing to do so dynamic worker exiting gracefully.", dbname); // Nothing left to do. Return end the run of BGW function. SPI_finish(); PopActiveSnapshot(); CommitTransactionCommand(); pgstat_report_activity(STATE_IDLE, NULL); pfree(rawstring); list_free(elemlist); return; } // 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 , dbname); resetStringInfo(&buf); appendStringInfo(&buf, "SELECT n.nspname FROM pg_catalog.pg_extension e JOIN pg_catalog.pg_namespace n ON e.extnamespace = n.oid WHERE extname = 'pg_partman'"); pgstat_report_activity(STATE_RUNNING, buf.data); ret = SPI_execute(buf.data, true, 1); if (ret != SPI_OK_SELECT) { elog(FATAL, "Cannot determine which schema pg_partman has been installed to: error code %d", ret); } if (SPI_processed > 0) { bool isnull; partman_schema = DatumGetCString(SPI_getbinval(SPI_tuptable->vals[0] , SPI_tuptable->tupdesc , 1 , &isnull)); if (isnull) elog(FATAL, "Query to determine pg_partman schema returned NULL."); } else { elog(FATAL, "Query to determine pg_partman schema returned zero rows."); } resetStringInfo(&buf); if (strcmp(pg_partman_bgw_analyze, "on") == 0) { analyze = "true"; } else { analyze = "false"; } if (strcmp(pg_partman_bgw_jobmon, "on") == 0) { jobmon = "true"; } else { jobmon = "false"; } appendStringInfo(&buf, "SELECT %s.run_maintenance(p_analyze := %s, p_jobmon := %s)", partman_schema, analyze, jobmon); pgstat_report_activity(STATE_RUNNING, buf.data); ret = SPI_execute(buf.data, false, 0); if (ret != SPI_OK_SELECT) elog(FATAL, "Cannot call pg_partman run_maintenance() function: error code %d", ret); elog(LOG, "%s: %s called by role %s on database %s" , MyBgworkerEntry->bgw_name , buf.data , pg_partman_bgw_role , dbname); SPI_finish(); PopActiveSnapshot(); CommitTransactionCommand(); pgstat_report_activity(STATE_IDLE, NULL); elog(DEBUG1, "pg_partman dynamic BGW shutting down gracefully for database %s.", dbname); pfree(rawstring); list_free(elemlist); return; } pg_partman-2.2.2/test/000077500000000000000000000000001262146621700146335ustar00rootroot00000000000000pg_partman-2.2.2/test/README_test.md000066400000000000000000000047511262146621700171600ustar00rootroot00000000000000pg_partman Test Suite ===================== The pgTAP testing suite is used to provide an extensive and easily maintainable set of unit tests. Please see the pgTAP home page for more details on its installation and use. http://pgTAP.org/ Tests assume that the required extensions have been installed in the following schemas: pg_partman: partman pgTAP: public If you've installed any of the above extensions in a different schema and would like to run the test suite, simply change the configuration option found at the top of each testing file to match your setup. If you've also installed pg_jobmon, be aware that the logging of the tests cannot be rolled back and any failures will be picked up by the monitoring in the jobmon extension. SELECT set_config('search_path','partman, public',false); Once that's done, it's best to use the **pg_prove** script that pgTAP comes with to run all the tests. I like using the -o -f -v options to get more useful feedback. pg_prove -ovf /path/to/partman/test/*.sql The tests must be run by a superuser or a user with complete create/write access to the database since roles & schemas are created & dropped as part of the test. There is a separate test script for each of the partitioning types. The tests are not required to run pg_partman, so if you don't feel safe doing this you don't need to run the tests. But if you are running into problems and report any issues without a clear explanation of what is wrong, I will ask that you run the test suite so you can try and narrow down where the problem may be. You are free to look through to tests to see exactly what they're doing. All tests in the top level of the test folder are run inside a transaction that is rolled back, so they should not change anything (except jobmon logging as mentioned). Tests for the time-custom partition type are in their own folder. The 30second test frequently errors out if run in certain 30 second blocks. Waiting for the next 30 second block and running it again should allow it to pass. Tests for the reindexing script can be found in the test/test_reindex folder. These tests cannot just be run all at once and are not run within rolled back transactions. They must be run in order, one at a time, and there are explicit instructions at the end of each test for what to do next. Tests for the Background Worker can be found in the test/test_bgw folder. Please read the header comments at the top of each test for the postgresql.conf settings required for that test. pg_partman-2.2.2/test/test-id-id-id-subpart.sql000066400000000000000000017735061262146621700214120ustar00rootroot00000000000000-- ########## ID PARENT / ID SUBPARENT / ID SUB-SUB-PARENT ########## -- May have to increase max_locks_per_transaction above the default 64 for this test to run properly \set ON_ERROR_ROLLBACK 1 \set ON_ERROR_STOP true BEGIN; SELECT set_config('search_path','partman, public',false); SELECT plan(3086); CREATE SCHEMA partman_test; CREATE TABLE partman_test.id_taptest_table (col1 int primary key, col2 text default 'stuff', col3 timestamptz NOT NULL DEFAULT now()); INSERT INTO partman_test.id_taptest_table (col1) VALUES (generate_series(1,100000)); SELECT create_parent('partman_test.id_taptest_table', 'col1', 'id', '10000', p_use_run_maintenance := true, p_jobmon := false, p_premake := 2); SELECT has_table('partman_test', 'id_taptest_table_p100000', 'Check id_taptest_table_p100000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p110000', 'Check id_taptest_table_p110000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p120000', 'Check id_taptest_table_p120000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p90000', 'Check id_taptest_table_p90000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p80000', 'Check id_taptest_table_p80000 exists'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table'', p_batch_count := 20)::int', ARRAY[100000], 'Check that partitioning function returns correct count of rows moved'); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table', 'Check that parent table has had data moved to partition'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table', ARRAY[100000], 'Check count from parent table'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0', ARRAY[9999], 'Check count from id_taptest_table_p0'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000', ARRAY[10000], 'Check count from id_taptest_table_p10000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000', ARRAY[10000], 'Check count from id_taptest_table_p20000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000', ARRAY[10000], 'Check count from id_taptest_table_p30000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000', ARRAY[10000], 'Check count from id_taptest_table_p40000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000', ARRAY[10000], 'Check count from id_taptest_table_p50000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000', ARRAY[10000], 'Check count from id_taptest_table_p60000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000', ARRAY[10000], 'Check count from id_taptest_table_p70000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000', ARRAY[10000], 'Check count from id_taptest_table_p80000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000', ARRAY[10000], 'Check count from id_taptest_table_p90000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000', ARRAY[1], 'Check count from id_taptest_table_p100000'); SELECT create_sub_parent('partman_test.id_taptest_table', 'col1', 'id', '1000', p_jobmon := false, p_premake := 2); SELECT results_eq('SELECT sub_parent FROM part_config_sub ORDER BY sub_parent', ARRAY['partman_test.id_taptest_table'], 'Check that part_config_sub has all tables configured as needed'); -- Test for normal partitions that should be made based on current max value of 100,000 SELECT has_table('partman_test', 'id_taptest_table_p90000_p98000', 'Check id_taptest_table_p90000_p98000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p90000_p99000', 'Check id_taptest_table_p90000_p99000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p100000_p100000', 'Check id_taptest_table_p100000_p100000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p100000_p101000', 'Check id_taptest_table_p100000_p101000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p100000_p102000', 'Check id_taptest_table_p100000_p102000 exists'); -- Tests that ensure minimal partition was made in all sets SELECT has_table('partman_test', 'id_taptest_table_p0_p0', 'Check id_taptest_table_p0_p0 exists'); SELECT has_table('partman_test', 'id_taptest_table_p10000_p10000', 'Check id_taptest_table_p10000_p10000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p20000_p20000', 'Check id_taptest_table_p20000_p20000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p30000_p30000', 'Check id_taptest_table_p30000_p30000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p40000_p40000', 'Check id_taptest_table_p40000_p40000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p50000_p50000', 'Check id_taptest_table_p50000_p50000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p60000_p60000', 'Check id_taptest_table_p60000_p60000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p70000_p70000', 'Check id_taptest_table_p70000_p70000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p80000_p80000', 'Check id_taptest_table_p80000_p80000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p110000_p110000', 'Check id_taptest_table_p110000_p110000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p120000_p120000', 'Check id_taptest_table_p120000_p120000 exists'); -- Partition all data again SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p0'', p_batch_count := 20)::int', ARRAY[9999], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p0'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p10000'', p_batch_count := 20)::int', ARRAY[10000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p10000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p20000'', p_batch_count := 20)::int', ARRAY[10000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p20000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p30000'', p_batch_count := 20)::int', ARRAY[10000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p30000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p40000'', p_batch_count := 20)::int', ARRAY[10000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p40000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p50000'', p_batch_count := 20)::int', ARRAY[10000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p50000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p60000'', p_batch_count := 20)::int', ARRAY[10000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p60000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p70000'', p_batch_count := 20)::int', ARRAY[10000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p70000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p80000'', p_batch_count := 20)::int', ARRAY[10000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p80000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p90000'', p_batch_count := 20)::int', ARRAY[10000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p90000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p100000'', p_batch_count := 20)::int', ARRAY[1], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p100000'); -- Test that all partitions have their data/exist SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table', 'Check that parent table has had data moved to partition'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table', ARRAY[100000], 'Check count from parent table'); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p0', 'Check that parent table id_taptest_table_p0 has had data moved to partition '); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p0', ARRAY[999], 'Check count from id_taptest_table_p0_p0'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p1000', ARRAY[1000], 'Check count from id_taptest_table_p0_p1000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p2000', ARRAY[1000], 'Check count from id_taptest_table_p0_p2000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p3000', ARRAY[1000], 'Check count from id_taptest_table_p0_p3000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p4000', ARRAY[1000], 'Check count from id_taptest_table_p0_p4000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p5000', ARRAY[1000], 'Check count from id_taptest_table_p0_p5000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p6000', ARRAY[1000], 'Check count from id_taptest_table_p0_p6000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p7000', ARRAY[1000], 'Check count from id_taptest_table_p0_p7000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p8000', ARRAY[1000], 'Check count from id_taptest_table_p0_p8000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p9000', ARRAY[1000], 'Check count from id_taptest_table_p0_p9000'); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p10000', 'Check that parent table id_taptest_table_p10000 has had data moved to partition '); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p10000', ARRAY[1000], 'Check count from id_taptest_table_p10000_p10000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p11000', ARRAY[1000], 'Check count from id_taptest_table_p10000_p11000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p12000', ARRAY[1000], 'Check count from id_taptest_table_p10000_p12000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p13000', ARRAY[1000], 'Check count from id_taptest_table_p10000_p13000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p14000', ARRAY[1000], 'Check count from id_taptest_table_p10000_p14000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p15000', ARRAY[1000], 'Check count from id_taptest_table_p10000_p15000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p16000', ARRAY[1000], 'Check count from id_taptest_table_p10000_p16000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p17000', ARRAY[1000], 'Check count from id_taptest_table_p10000_p17000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p18000', ARRAY[1000], 'Check count from id_taptest_table_p10000_p18000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p19000', ARRAY[1000], 'Check count from id_taptest_table_p10000_p19000'); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p20000', 'Check that parent table id_taptest_table_p20000 has had data moved to partition '); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p20000', ARRAY[1000], 'Check count from id_taptest_table_p20000_p20000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p21000', ARRAY[1000], 'Check count from id_taptest_table_p20000_p21000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p22000', ARRAY[1000], 'Check count from id_taptest_table_p20000_p22000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p23000', ARRAY[1000], 'Check count from id_taptest_table_p20000_p23000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p24000', ARRAY[1000], 'Check count from id_taptest_table_p20000_p24000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p25000', ARRAY[1000], 'Check count from id_taptest_table_p20000_p25000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p26000', ARRAY[1000], 'Check count from id_taptest_table_p20000_p26000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p27000', ARRAY[1000], 'Check count from id_taptest_table_p20000_p27000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p28000', ARRAY[1000], 'Check count from id_taptest_table_p20000_p28000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p29000', ARRAY[1000], 'Check count from id_taptest_table_p20000_p29000'); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p30000', 'Check that parent table id_taptest_table_p30000 has had data moved to partition '); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p30000', ARRAY[1000], 'Check count from id_taptest_table_p30000_p30000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p31000', ARRAY[1000], 'Check count from id_taptest_table_p30000_p31000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p32000', ARRAY[1000], 'Check count from id_taptest_table_p30000_p32000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p33000', ARRAY[1000], 'Check count from id_taptest_table_p30000_p33000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p34000', ARRAY[1000], 'Check count from id_taptest_table_p30000_p34000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p35000', ARRAY[1000], 'Check count from id_taptest_table_p30000_p35000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p36000', ARRAY[1000], 'Check count from id_taptest_table_p30000_p36000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p37000', ARRAY[1000], 'Check count from id_taptest_table_p30000_p37000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p38000', ARRAY[1000], 'Check count from id_taptest_table_p30000_p38000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p39000', ARRAY[1000], 'Check count from id_taptest_table_p30000_p39000'); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p40000', 'Check that parent table id_taptest_table_p40000 has had data moved to partition '); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p40000', ARRAY[1000], 'Check count from id_taptest_table_p40000_p40000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p41000', ARRAY[1000], 'Check count from id_taptest_table_p40000_p41000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p42000', ARRAY[1000], 'Check count from id_taptest_table_p40000_p42000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p43000', ARRAY[1000], 'Check count from id_taptest_table_p40000_p43000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p44000', ARRAY[1000], 'Check count from id_taptest_table_p40000_p44000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p45000', ARRAY[1000], 'Check count from id_taptest_table_p40000_p45000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p46000', ARRAY[1000], 'Check count from id_taptest_table_p40000_p46000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p47000', ARRAY[1000], 'Check count from id_taptest_table_p40000_p47000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p48000', ARRAY[1000], 'Check count from id_taptest_table_p40000_p48000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p49000', ARRAY[1000], 'Check count from id_taptest_table_p40000_p49000'); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p50000', 'Check that parent table id_taptest_table_p50000 has had data moved to partition '); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p50000', ARRAY[1000], 'Check count from id_taptest_table_p50000_p50000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p51000', ARRAY[1000], 'Check count from id_taptest_table_p50000_p51000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p52000', ARRAY[1000], 'Check count from id_taptest_table_p50000_p52000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p53000', ARRAY[1000], 'Check count from id_taptest_table_p50000_p53000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p54000', ARRAY[1000], 'Check count from id_taptest_table_p50000_p54000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p55000', ARRAY[1000], 'Check count from id_taptest_table_p50000_p55000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p56000', ARRAY[1000], 'Check count from id_taptest_table_p50000_p56000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p57000', ARRAY[1000], 'Check count from id_taptest_table_p50000_p57000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p58000', ARRAY[1000], 'Check count from id_taptest_table_p50000_p58000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p59000', ARRAY[1000], 'Check count from id_taptest_table_p50000_p59000'); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p60000', 'Check that parent table id_taptest_table_p60000 has had data moved to partition '); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p60000', ARRAY[1000], 'Check count from id_taptest_table_p60000_p60000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p61000', ARRAY[1000], 'Check count from id_taptest_table_p60000_p61000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p62000', ARRAY[1000], 'Check count from id_taptest_table_p60000_p62000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p63000', ARRAY[1000], 'Check count from id_taptest_table_p60000_p63000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p64000', ARRAY[1000], 'Check count from id_taptest_table_p60000_p64000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p65000', ARRAY[1000], 'Check count from id_taptest_table_p60000_p65000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p66000', ARRAY[1000], 'Check count from id_taptest_table_p60000_p66000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p67000', ARRAY[1000], 'Check count from id_taptest_table_p60000_p67000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p68000', ARRAY[1000], 'Check count from id_taptest_table_p60000_p68000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p69000', ARRAY[1000], 'Check count from id_taptest_table_p60000_p69000'); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p70000', 'Check that parent table id_taptest_table_p70000 has had data moved to partition '); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p70000', ARRAY[1000], 'Check count from id_taptest_table_p70000_p70000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p71000', ARRAY[1000], 'Check count from id_taptest_table_p70000_p71000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p72000', ARRAY[1000], 'Check count from id_taptest_table_p70000_p72000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p73000', ARRAY[1000], 'Check count from id_taptest_table_p70000_p73000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p74000', ARRAY[1000], 'Check count from id_taptest_table_p70000_p74000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p75000', ARRAY[1000], 'Check count from id_taptest_table_p70000_p75000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p76000', ARRAY[1000], 'Check count from id_taptest_table_p70000_p76000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p77000', ARRAY[1000], 'Check count from id_taptest_table_p70000_p77000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p78000', ARRAY[1000], 'Check count from id_taptest_table_p70000_p78000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p79000', ARRAY[1000], 'Check count from id_taptest_table_p70000_p79000'); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p80000', 'Check that parent table id_taptest_table_p80000 has had data moved to partition '); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p80000', ARRAY[1000], 'Check count from id_taptest_table_p80000_p80000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p81000', ARRAY[1000], 'Check count from id_taptest_table_p80000_p81000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p82000', ARRAY[1000], 'Check count from id_taptest_table_p80000_p82000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p83000', ARRAY[1000], 'Check count from id_taptest_table_p80000_p83000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p84000', ARRAY[1000], 'Check count from id_taptest_table_p80000_p84000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p85000', ARRAY[1000], 'Check count from id_taptest_table_p80000_p85000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p86000', ARRAY[1000], 'Check count from id_taptest_table_p80000_p86000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p87000', ARRAY[1000], 'Check count from id_taptest_table_p80000_p87000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p88000', ARRAY[1000], 'Check count from id_taptest_table_p80000_p88000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p89000', ARRAY[1000], 'Check count from id_taptest_table_p80000_p89000'); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p90000', 'Check that parent table id_taptest_table_p90000 has had data moved to partition '); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p90000', ARRAY[1000], 'Check count from id_taptest_table_p90000_p90000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p91000', ARRAY[1000], 'Check count from id_taptest_table_p90000_p91000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p92000', ARRAY[1000], 'Check count from id_taptest_table_p90000_p92000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p93000', ARRAY[1000], 'Check count from id_taptest_table_p90000_p93000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p94000', ARRAY[1000], 'Check count from id_taptest_table_p90000_p94000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p95000', ARRAY[1000], 'Check count from id_taptest_table_p90000_p95000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p96000', ARRAY[1000], 'Check count from id_taptest_table_p90000_p96000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p97000', ARRAY[1000], 'Check count from id_taptest_table_p90000_p97000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p98000', ARRAY[1000], 'Check count from id_taptest_table_p90000_p98000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p99000', ARRAY[1000], 'Check count from id_taptest_table_p90000_p99000'); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p100000', 'Check that parent table id_taptest_table_p100000 has had data moved to partition '); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p100000', ARRAY[1], 'Check count from id_taptest_table_p100000_p100000'); -- Create next level of subpartitions SELECT create_sub_parent('partman_test.id_taptest_table_p0', 'col1', 'id', '100', p_jobmon := false, p_premake := 2); SELECT create_sub_parent('partman_test.id_taptest_table_p10000', 'col1', 'id', '100', p_jobmon := false, p_premake := 2); SELECT create_sub_parent('partman_test.id_taptest_table_p20000', 'col1', 'id', '100', p_jobmon := false, p_premake := 2); SELECT create_sub_parent('partman_test.id_taptest_table_p30000', 'col1', 'id', '100', p_jobmon := false, p_premake := 2); SELECT create_sub_parent('partman_test.id_taptest_table_p40000', 'col1', 'id', '100', p_jobmon := false, p_premake := 2); SELECT create_sub_parent('partman_test.id_taptest_table_p50000', 'col1', 'id', '100', p_jobmon := false, p_premake := 2); SELECT create_sub_parent('partman_test.id_taptest_table_p60000', 'col1', 'id', '100', p_jobmon := false, p_premake := 2); SELECT create_sub_parent('partman_test.id_taptest_table_p70000', 'col1', 'id', '100', p_jobmon := false, p_premake := 2); SELECT create_sub_parent('partman_test.id_taptest_table_p80000', 'col1', 'id', '100', p_jobmon := false, p_premake := 2); SELECT create_sub_parent('partman_test.id_taptest_table_p90000', 'col1', 'id', '100', p_jobmon := false, p_premake := 2); SELECT create_sub_parent('partman_test.id_taptest_table_p100000', 'col1', 'id', '100', p_jobmon := false, p_premake := 2); SELECT create_sub_parent('partman_test.id_taptest_table_p110000', 'col1', 'id', '100', p_jobmon := false, p_premake := 2); SELECT create_sub_parent('partman_test.id_taptest_table_p120000', 'col1', 'id', '100', p_jobmon := false, p_premake := 2); SELECT results_eq('SELECT sub_parent FROM part_config_sub ORDER BY sub_parent', ARRAY['partman_test.id_taptest_table', 'partman_test.id_taptest_table_p0', 'partman_test.id_taptest_table_p10000', 'partman_test.id_taptest_table_p100000', 'partman_test.id_taptest_table_p110000', 'partman_test.id_taptest_table_p120000', 'partman_test.id_taptest_table_p20000', 'partman_test.id_taptest_table_p30000', 'partman_test.id_taptest_table_p40000', 'partman_test.id_taptest_table_p50000', 'partman_test.id_taptest_table_p60000', 'partman_test.id_taptest_table_p70000', 'partman_test.id_taptest_table_p80000', 'partman_test.id_taptest_table_p90000' ], 'Check that part_config_sub has all tables configured as needed'); -- Test for normal partitions that should be made based on current max value of 100,000 SELECT has_table('partman_test', 'id_taptest_table_p90000_p99000_p99800', 'Check id_taptest_table_p90000_p99000_p99800 exists'); SELECT has_table('partman_test', 'id_taptest_table_p90000_p99000_p99900', 'Check id_taptest_table_p90000_p99000_p99800 exists'); SELECT has_table('partman_test', 'id_taptest_table_p100000_p100000_p100000', 'Check id_taptest_table_p100000_p100000_p100000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p100000_p100000_p100100', 'Check id_taptest_table_p100000_p100000_p100100 exists'); SELECT has_table('partman_test', 'id_taptest_table_p100000_p100000_p100200', 'Check id_taptest_table_p100000_p100000_p100200 exists'); -- Tests that ensure minimal partition was made in all sets SELECT has_table('partman_test', 'id_taptest_table_p0_p0_p0', 'Check id_taptest_table_p0_p0_p0 exists'); SELECT has_table('partman_test', 'id_taptest_table_p0_p1000_p1000', 'Check id_taptest_table_p0_p1000_p1000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p0_p2000_p2000', 'Check id_taptest_table_p0_p2000_p2000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p0_p3000_p3000', 'Check id_taptest_table_p0_p3000_p3000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p0_p4000_p4000', 'Check id_taptest_table_p0_p4000_p4000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p0_p5000_p5000', 'Check id_taptest_table_p0_p5000_p5000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p0_p6000_p6000', 'Check id_taptest_table_p0_p6000_p6000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p0_p7000_p7000', 'Check id_taptest_table_p0_p7000_p7000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p0_p8000_p8000', 'Check id_taptest_table_p0_p8000_p8000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p0_p9000_p9000', 'Check id_taptest_table_p0_p9000_p1000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p10000_p10000_p10000', 'Check id_taptest_table_p10000_p10000_p10000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p10000_p11000_p11000', 'Check id_taptest_table_p10000_p11000_p11000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p10000_p12000_p12000', 'Check id_taptest_table_p10000_p12000_p12000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p10000_p13000_p13000', 'Check id_taptest_table_p10000_p13000_p13000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p10000_p14000_p14000', 'Check id_taptest_table_p10000_p14000_p14000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p10000_p15000_p15000', 'Check id_taptest_table_p10000_p15000_p15000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p10000_p16000_p16000', 'Check id_taptest_table_p10000_p16000_p16000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p10000_p17000_p17000', 'Check id_taptest_table_p10000_p17000_p17000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p10000_p18000_p18000', 'Check id_taptest_table_p10000_p18000_p18000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p10000_p19000_p19000', 'Check id_taptest_table_p10000_p19000_p19000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p20000_p20000_p20000', 'Check id_taptest_table_p20000_p20000_p20000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p20000_p21000_p21000', 'Check id_taptest_table_p20000_p21000_p21000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p20000_p22000_p22000', 'Check id_taptest_table_p20000_p22000_p22000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p20000_p23000_p23000', 'Check id_taptest_table_p20000_p23000_p23000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p20000_p24000_p24000', 'Check id_taptest_table_p10000_p24000_p24000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p20000_p25000_p25000', 'Check id_taptest_table_p20000_p25000_p25000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p20000_p26000_p26000', 'Check id_taptest_table_p20000_p26000_p26000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p20000_p27000_p27000', 'Check id_taptest_table_p20000_p27000_p27000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p20000_p28000_p28000', 'Check id_taptest_table_p20000_p28000_p28000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p20000_p29000_p29000', 'Check id_taptest_table_p20000_p29000_p29000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p30000_p30000_p30000', 'Check id_taptest_table_p30000_p30000_p30000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p30000_p31000_p31000', 'Check id_taptest_table_p30000_p31000_p31000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p30000_p32000_p32000', 'Check id_taptest_table_p30000_p32000_p32000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p30000_p33000_p33000', 'Check id_taptest_table_p30000_p33000_p33000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p30000_p34000_p34000', 'Check id_taptest_table_p30000_p34000_p34000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p30000_p35000_p35000', 'Check id_taptest_table_p30000_p35000_p35000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p30000_p36000_p36000', 'Check id_taptest_table_p30000_p36000_p36000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p30000_p37000_p37000', 'Check id_taptest_table_p30000_p37000_p37000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p30000_p38000_p38000', 'Check id_taptest_table_p30000_p38000_p38000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p30000_p39000_p39000', 'Check id_taptest_table_p30000_p39000_p39000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p40000_p40000_p40000', 'Check id_taptest_table_p40000_p40000_p40000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p40000_p41000_p41000', 'Check id_taptest_table_p40000_p41000_p41000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p40000_p42000_p42000', 'Check id_taptest_table_p40000_p42000_p42000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p40000_p43000_p43000', 'Check id_taptest_table_p40000_p43000_p43000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p40000_p44000_p44000', 'Check id_taptest_table_p40000_p44000_p44000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p40000_p45000_p45000', 'Check id_taptest_table_p40000_p45000_p45000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p40000_p46000_p46000', 'Check id_taptest_table_p40000_p46000_p46000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p40000_p47000_p47000', 'Check id_taptest_table_p40000_p47000_p47000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p40000_p48000_p48000', 'Check id_taptest_table_p40000_p48000_p48000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p40000_p49000_p49000', 'Check id_taptest_table_p40000_p49000_p49000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p50000_p50000_p50000', 'Check id_taptest_table_p50000_p50000_p50000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p50000_p51000_p51000', 'Check id_taptest_table_p50000_p51000_p51000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p50000_p52000_p52000', 'Check id_taptest_table_p50000_p52000_p52000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p50000_p53000_p53000', 'Check id_taptest_table_p50000_p53000_p53000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p50000_p54000_p54000', 'Check id_taptest_table_p50000_p54000_p54000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p50000_p55000_p55000', 'Check id_taptest_table_p50000_p55000_p55000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p50000_p56000_p56000', 'Check id_taptest_table_p50000_p56000_p56000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p50000_p57000_p57000', 'Check id_taptest_table_p50000_p57000_p57000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p50000_p58000_p58000', 'Check id_taptest_table_p50000_p58000_p58000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p50000_p59000_p59000', 'Check id_taptest_table_p50000_p59000_p59000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p60000_p60000_p60000', 'Check id_taptest_table_p60000_p60000_p60000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p60000_p61000_p61000', 'Check id_taptest_table_p60000_p61000_p61000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p60000_p62000_p62000', 'Check id_taptest_table_p60000_p62000_p62000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p60000_p63000_p63000', 'Check id_taptest_table_p60000_p63000_p63000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p60000_p64000_p64000', 'Check id_taptest_table_p60000_p64000_p64000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p60000_p65000_p65000', 'Check id_taptest_table_p60000_p65000_p65000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p60000_p66000_p66000', 'Check id_taptest_table_p60000_p66000_p66000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p60000_p67000_p67000', 'Check id_taptest_table_p60000_p67000_p67000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p60000_p68000_p68000', 'Check id_taptest_table_p60000_p68000_p68000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p60000_p69000_p69000', 'Check id_taptest_table_p60000_p69000_p69000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p70000_p70000_p70000', 'Check id_taptest_table_p70000_p70000_p70000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p70000_p71000_p71000', 'Check id_taptest_table_p70000_p71000_p71000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p70000_p72000_p72000', 'Check id_taptest_table_p70000_p72000_p72000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p70000_p73000_p73000', 'Check id_taptest_table_p70000_p73000_p73000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p70000_p74000_p74000', 'Check id_taptest_table_p70000_p74000_p74000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p70000_p75000_p75000', 'Check id_taptest_table_p70000_p75000_p75000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p70000_p76000_p76000', 'Check id_taptest_table_p70000_p76000_p76000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p70000_p77000_p77000', 'Check id_taptest_table_p70000_p77000_p77000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p70000_p78000_p78000', 'Check id_taptest_table_p70000_p78000_p78000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p70000_p79000_p79000', 'Check id_taptest_table_p70000_p79000_p79000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p80000_p80000_p80000', 'Check id_taptest_table_p80000_p80000_p80000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p80000_p81000_p81000', 'Check id_taptest_table_p80000_p81000_p80000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p80000_p82000_p82000', 'Check id_taptest_table_p80000_p82000_p80000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p80000_p83000_p83000', 'Check id_taptest_table_p80000_p83000_p80000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p80000_p84000_p84000', 'Check id_taptest_table_p80000_p84000_p80000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p80000_p85000_p85000', 'Check id_taptest_table_p80000_p85000_p80000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p80000_p86000_p86000', 'Check id_taptest_table_p80000_p86000_p80000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p80000_p87000_p87000', 'Check id_taptest_table_p80000_p87000_p80000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p80000_p88000_p88000', 'Check id_taptest_table_p80000_p88000_p80000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p80000_p89000_p89000', 'Check id_taptest_table_p80000_p89000_p80000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p90000_p90000_p90000', 'Check id_taptest_table_p90000_p90000_p90000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p90000_p91000_p91000', 'Check id_taptest_table_p90000_p91000_p91000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p90000_p92000_p92000', 'Check id_taptest_table_p90000_p92000_p92000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p90000_p93000_p93000', 'Check id_taptest_table_p90000_p93000_p93000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p90000_p94000_p94000', 'Check id_taptest_table_p90000_p94000_p94000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p90000_p95000_p95000', 'Check id_taptest_table_p90000_p95000_p95000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p90000_p96000_p96000', 'Check id_taptest_table_p90000_p96000_p96000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p90000_p97000_p97000', 'Check id_taptest_table_p90000_p97000_p97000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p90000_p98000_p98000', 'Check id_taptest_table_p90000_p98000_p98000 exists'); -- All the extra partitions above exist because there is data in that range and it was partitioned out -- Do a hasnt check just for sanity SELECT has_table('partman_test', 'id_taptest_table_p100000_p100000_p100000', 'Check id_taptest_table_p100000_p100000_p100000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p100000_p101000_p101000', 'Check id_taptest_table_p100000_p101000_p101000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p100000_p102000_p102000', 'Check id_taptest_table_p100000_p102000_p102000 exists'); SELECT hasnt_table('partman_test', 'id_taptest_table_p100000_p103000_p103000', 'Check id_taptest_table_p100000_p103000_p103000 exists'); SELECT has_table('partman_test', 'id_taptest_table_p110000_p110000_p110000', 'Check id_taptest_table_p110000_p110000_p110000 exists'); SELECT hasnt_table('partman_test', 'id_taptest_table_p110000_p111000_p111000', 'Check id_taptest_table_p110000_p111000_p111000 does not exist'); SELECT has_table('partman_test', 'id_taptest_table_p120000_p120000_p120000', 'Check id_taptest_table_p120000_p120000_p120000 exists'); SELECT hasnt_table('partman_test', 'id_taptest_table_p120000_p121000_p121000', 'Check id_taptest_table_p120000_p121000_p121000 does not exist'); -- Partition all data yet again SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p0_p0'', p_batch_count := 20)::int', ARRAY[999], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p0_p0'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p0_p1000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p0_p1000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p0_p2000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p0_p2000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p0_p3000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p0_p3000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p0_p4000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p0_p4000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p0_p5000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p0_p5000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p0_p6000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p0_p6000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p0_p7000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p0_p7000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p0_p8000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p0_p8000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p0_p9000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p0_p9000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p10000_p10000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p10000_p10000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p10000_p11000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p10000_p11000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p10000_p12000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p10000_p12000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p10000_p13000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p10000_p13000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p10000_p14000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p10000_p14000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p10000_p15000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p10000_p15000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p10000_p16000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p10000_p16000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p10000_p17000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p10000_p17000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p10000_p18000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p10000_p18000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p10000_p19000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p10000_p19000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p20000_p20000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p20000_p20000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p20000_p21000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p20000_p21000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p20000_p22000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p20000_p22000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p20000_p23000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p20000_p23000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p20000_p24000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p20000_p24000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p20000_p25000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p20000_p25000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p20000_p26000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p20000_p26000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p20000_p27000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p20000_p27000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p20000_p28000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p20000_p28000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p20000_p29000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p20000_p29000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p30000_p30000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p30000_p30000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p30000_p31000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p30000_p31000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p30000_p32000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p30000_p32000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p30000_p33000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p30000_p33000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p30000_p34000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p30000_p34000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p30000_p35000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p30000_p35000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p30000_p36000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p30000_p36000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p30000_p37000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p30000_p37000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p30000_p38000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p30000_p38000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p30000_p39000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p30000_p39000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p40000_p40000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p40000_p40000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p40000_p41000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p40000_p41000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p40000_p42000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p40000_p42000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p40000_p43000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p40000_p43000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p40000_p44000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p40000_p44000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p40000_p45000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p40000_p45000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p40000_p46000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p40000_p46000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p40000_p47000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p40000_p47000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p40000_p48000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p40000_p48000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p40000_p49000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p40000_p49000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p50000_p50000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p50000_p50000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p50000_p51000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p50000_p51000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p50000_p52000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p50000_p52000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p50000_p53000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p50000_p53000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p50000_p54000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p50000_p54000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p50000_p55000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p50000_p55000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p50000_p56000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p50000_p56000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p50000_p57000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p50000_p57000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p50000_p58000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p50000_p58000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p50000_p59000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p50000_p59000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p60000_p60000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p60000_p60000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p60000_p61000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p60000_p61000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p60000_p62000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p60000_p62000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p60000_p63000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p60000_p63000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p60000_p64000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p60000_p64000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p60000_p65000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p60000_p65000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p60000_p66000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p60000_p66000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p60000_p67000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p60000_p67000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p60000_p68000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p60000_p68000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p60000_p69000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p60000_p69000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p70000_p70000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p70000_p70000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p70000_p71000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p70000_p71000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p70000_p72000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p70000_p72000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p70000_p73000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p70000_p73000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p70000_p74000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p70000_p74000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p70000_p75000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p70000_p75000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p70000_p76000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p70000_p76000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p70000_p77000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p70000_p77000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p70000_p78000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p70000_p78000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p70000_p79000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p70000_p79000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p80000_p80000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p80000_p80000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p80000_p81000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p80000_p81000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p80000_p82000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p80000_p82000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p80000_p83000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p80000_p83000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p80000_p84000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p80000_p84000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p80000_p85000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p80000_p85000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p80000_p86000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p80000_p86000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p80000_p87000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p80000_p87000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p80000_p88000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p80000_p88000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p80000_p89000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p80000_p89000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p90000_p90000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p90000_p90000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p90000_p91000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p90000_p91000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p90000_p92000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p90000_p92000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p90000_p93000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p90000_p93000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p90000_p94000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p90000_p94000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p90000_p95000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p90000_p95000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p90000_p96000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p90000_p96000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p90000_p97000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p90000_p97000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p90000_p98000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p90000_p98000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p90000_p99000'', p_batch_count := 20)::int', ARRAY[1000], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p90000_p99000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p100000_p100000'', p_batch_count := 20)::int', ARRAY[1], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p100000_p100000'); -- Test that all parent tables partitions have no data SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table', 'Check that parent table has had data moved to partition'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table', ARRAY[100000], 'Check count from parent table'); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p0', 'Check that parent table id_taptest_table_p0 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p10000', 'Check that parent table id_taptest_table_p10000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p20000', 'Check that parent table id_taptest_table_p20000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p30000', 'Check that parent table id_taptest_table_p30000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p40000', 'Check that parent table id_taptest_table_p40000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p50000', 'Check that parent table id_taptest_table_p50000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p60000', 'Check that parent table id_taptest_table_p60000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p70000', 'Check that parent table id_taptest_table_p70000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p80000', 'Check that parent table id_taptest_table_p80000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p90000', 'Check that parent table id_taptest_table_p90000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p100000', 'Check that parent table id_taptest_table_p100000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p0_p0', 'Check that parent table id_taptest_table_p0_p0 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p0_p1000', 'Check that parent table id_taptest_table_p0_p1000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p0_p2000', 'Check that parent table id_taptest_table_p0_p2000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p0_p3000', 'Check that parent table id_taptest_table_p0_p3000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p0_p4000', 'Check that parent table id_taptest_table_p0_p4000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p0_p5000', 'Check that parent table id_taptest_table_p0_p5000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p0_p6000', 'Check that parent table id_taptest_table_p0_p6000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p0_p7000', 'Check that parent table id_taptest_table_p0_p7000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p0_p8000', 'Check that parent table id_taptest_table_p0_p8000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p0_p9000', 'Check that parent table id_taptest_table_p0_p9000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p10000_p10000', 'Check that parent table id_taptest_table_p10000_p10000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p10000_p11000', 'Check that parent table id_taptest_table_p10000_p11000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p10000_p12000', 'Check that parent table id_taptest_table_p10000_p12000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p10000_p13000', 'Check that parent table id_taptest_table_p10000_p13000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p10000_p14000', 'Check that parent table id_taptest_table_p10000_p14000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p10000_p15000', 'Check that parent table id_taptest_table_p10000_p15000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p10000_p16000', 'Check that parent table id_taptest_table_p10000_p16000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p10000_p17000', 'Check that parent table id_taptest_table_p10000_p17000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p10000_p18000', 'Check that parent table id_taptest_table_p10000_p18000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p10000_p19000', 'Check that parent table id_taptest_table_p10000_p19000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p20000_p20000', 'Check that parent table id_taptest_table_p20000_p20000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p20000_p22000', 'Check that parent table id_taptest_table_p20000_p22000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p20000_p23000', 'Check that parent table id_taptest_table_p20000_p23000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p20000_p24000', 'Check that parent table id_taptest_table_p20000_p24000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p20000_p25000', 'Check that parent table id_taptest_table_p20000_p25000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p20000_p26000', 'Check that parent table id_taptest_table_p20000_p26000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p20000_p27000', 'Check that parent table id_taptest_table_p20000_p27000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p20000_p28000', 'Check that parent table id_taptest_table_p20000_p28000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p20000_p29000', 'Check that parent table id_taptest_table_p20000_p29000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p30000_p30000', 'Check that parent table id_taptest_table_p30000_p30000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p30000_p31000', 'Check that parent table id_taptest_table_p30000_p31000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p30000_p32000', 'Check that parent table id_taptest_table_p30000_p32000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p30000_p33000', 'Check that parent table id_taptest_table_p30000_p33000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p30000_p34000', 'Check that parent table id_taptest_table_p30000_p34000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p30000_p35000', 'Check that parent table id_taptest_table_p30000_p35000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p30000_p36000', 'Check that parent table id_taptest_table_p30000_p36000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p30000_p37000', 'Check that parent table id_taptest_table_p30000_p37000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p30000_p38000', 'Check that parent table id_taptest_table_p30000_p38000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p30000_p39000', 'Check that parent table id_taptest_table_p30000_p39000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p40000_p40000', 'Check that parent table id_taptest_table_p40000_p40000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p40000_p41000', 'Check that parent table id_taptest_table_p40000_p41000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p40000_p42000', 'Check that parent table id_taptest_table_p40000_p42000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p40000_p43000', 'Check that parent table id_taptest_table_p40000_p43000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p40000_p44000', 'Check that parent table id_taptest_table_p40000_p44000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p40000_p45000', 'Check that parent table id_taptest_table_p40000_p45000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p40000_p46000', 'Check that parent table id_taptest_table_p40000_p46000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p40000_p47000', 'Check that parent table id_taptest_table_p40000_p47000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p40000_p48000', 'Check that parent table id_taptest_table_p40000_p48000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p40000_p49000', 'Check that parent table id_taptest_table_p40000_p49000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p50000_p50000', 'Check that parent table id_taptest_table_p50000_p50000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p50000_p51000', 'Check that parent table id_taptest_table_p50000_p51000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p50000_p52000', 'Check that parent table id_taptest_table_p50000_p52000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p50000_p53000', 'Check that parent table id_taptest_table_p50000_p53000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p50000_p54000', 'Check that parent table id_taptest_table_p50000_p54000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p50000_p55000', 'Check that parent table id_taptest_table_p50000_p55000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p50000_p56000', 'Check that parent table id_taptest_table_p50000_p56000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p50000_p57000', 'Check that parent table id_taptest_table_p50000_p57000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p50000_p58000', 'Check that parent table id_taptest_table_p50000_p58000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p50000_p59000', 'Check that parent table id_taptest_table_p50000_p59000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p60000_p60000', 'Check that parent table id_taptest_table_p60000_p60000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p60000_p61000', 'Check that parent table id_taptest_table_p60000_p61000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p60000_p62000', 'Check that parent table id_taptest_table_p60000_p62000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p60000_p63000', 'Check that parent table id_taptest_table_p60000_p63000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p60000_p64000', 'Check that parent table id_taptest_table_p60000_p64000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p60000_p65000', 'Check that parent table id_taptest_table_p60000_p65000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p60000_p66000', 'Check that parent table id_taptest_table_p60000_p66000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p60000_p67000', 'Check that parent table id_taptest_table_p60000_p67000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p60000_p68000', 'Check that parent table id_taptest_table_p60000_p68000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p60000_p69000', 'Check that parent table id_taptest_table_p60000_p69000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p70000_p70000', 'Check that parent table id_taptest_table_p70000_p70000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p70000_p71000', 'Check that parent table id_taptest_table_p70000_p71000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p70000_p72000', 'Check that parent table id_taptest_table_p70000_p72000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p70000_p73000', 'Check that parent table id_taptest_table_p70000_p73000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p70000_p74000', 'Check that parent table id_taptest_table_p70000_p74000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p70000_p75000', 'Check that parent table id_taptest_table_p70000_p75000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p70000_p76000', 'Check that parent table id_taptest_table_p70000_p76000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p70000_p77000', 'Check that parent table id_taptest_table_p70000_p77000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p70000_p78000', 'Check that parent table id_taptest_table_p70000_p78000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p70000_p79000', 'Check that parent table id_taptest_table_p70000_p79000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p80000_p80000', 'Check that parent table id_taptest_table_p80000_p80000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p80000_p81000', 'Check that parent table id_taptest_table_p80000_p81000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p80000_p82000', 'Check that parent table id_taptest_table_p80000_p82000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p80000_p83000', 'Check that parent table id_taptest_table_p80000_p83000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p80000_p84000', 'Check that parent table id_taptest_table_p80000_p84000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p80000_p85000', 'Check that parent table id_taptest_table_p80000_p85000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p80000_p86000', 'Check that parent table id_taptest_table_p80000_p86000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p80000_p87000', 'Check that parent table id_taptest_table_p80000_p87000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p80000_p88000', 'Check that parent table id_taptest_table_p80000_p88000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p80000_p89000', 'Check that parent table id_taptest_table_p80000_p89000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p90000_p90000', 'Check that parent table id_taptest_table_p90000_p90000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p90000_p91000', 'Check that parent table id_taptest_table_p90000_p91000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p90000_p92000', 'Check that parent table id_taptest_table_p90000_p92000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p90000_p93000', 'Check that parent table id_taptest_table_p90000_p93000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p90000_p94000', 'Check that parent table id_taptest_table_p90000_p94000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p90000_p95000', 'Check that parent table id_taptest_table_p90000_p95000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p90000_p96000', 'Check that parent table id_taptest_table_p90000_p96000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p90000_p97000', 'Check that parent table id_taptest_table_p90000_p97000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p90000_p98000', 'Check that parent table id_taptest_table_p90000_p98000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p90000_p99000', 'Check that parent table id_taptest_table_p90000_p99000 has had data moved to partition '); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p100000_p100000', 'Check that parent table id_taptest_table_p100000_p100000 has had data moved to partition '); -- Test that all child partitions have their data/exist SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p0_p0', ARRAY[99], 'Check count from id_taptest_table_p0_p0_p0'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p0_p100', ARRAY[100], 'Check count from id_taptest_table_p0_p0_p100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p0_p200', ARRAY[100], 'Check count from id_taptest_table_p0_p0_p200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p0_p300', ARRAY[100], 'Check count from id_taptest_table_p0_p0_p300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p0_p400', ARRAY[100], 'Check count from id_taptest_table_p0_p0_p400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p0_p500', ARRAY[100], 'Check count from id_taptest_table_p0_p0_p500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p0_p600', ARRAY[100], 'Check count from id_taptest_table_p0_p0_p600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p0_p700', ARRAY[100], 'Check count from id_taptest_table_p0_p0_p700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p0_p800', ARRAY[100], 'Check count from id_taptest_table_p0_p0_p800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p0_p900', ARRAY[100], 'Check count from id_taptest_table_p0_p0_p900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p1000_p1000', ARRAY[100], 'Check count from id_taptest_table_p0_p1000_p1000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p1000_p1100', ARRAY[100], 'Check count from id_taptest_table_p0_p1000_p1100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p1000_p1200', ARRAY[100], 'Check count from id_taptest_table_p0_p1000_p1200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p1000_p1300', ARRAY[100], 'Check count from id_taptest_table_p0_p1000_p1300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p1000_p1400', ARRAY[100], 'Check count from id_taptest_table_p0_p1000_p1400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p1000_p1500', ARRAY[100], 'Check count from id_taptest_table_p0_p1000_p1500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p1000_p1600', ARRAY[100], 'Check count from id_taptest_table_p0_p1000_p1600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p1000_p1700', ARRAY[100], 'Check count from id_taptest_table_p0_p1000_p1700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p1000_p1800', ARRAY[100], 'Check count from id_taptest_table_p0_p1000_p1800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p1000_p1900', ARRAY[100], 'Check count from id_taptest_table_p0_p1000_p1900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p2000_p2000', ARRAY[100], 'Check count from id_taptest_table_p0_p2000_p2000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p2000_p2100', ARRAY[100], 'Check count from id_taptest_table_p0_p2000_p2100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p2000_p2200', ARRAY[100], 'Check count from id_taptest_table_p0_p2000_p2200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p2000_p2300', ARRAY[100], 'Check count from id_taptest_table_p0_p2000_p2300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p2000_p2400', ARRAY[100], 'Check count from id_taptest_table_p0_p2000_p2400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p2000_p2500', ARRAY[100], 'Check count from id_taptest_table_p0_p2000_p2500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p2000_p2600', ARRAY[100], 'Check count from id_taptest_table_p0_p2000_p2600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p2000_p2700', ARRAY[100], 'Check count from id_taptest_table_p0_p2000_p2700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p2000_p2800', ARRAY[100], 'Check count from id_taptest_table_p0_p2000_p2800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p2000_p2900', ARRAY[100], 'Check count from id_taptest_table_p0_p2000_p2900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p3000_p3000', ARRAY[100], 'Check count from id_taptest_table_p0_p3000_p3000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p3000_p3100', ARRAY[100], 'Check count from id_taptest_table_p0_p3000_p3100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p3000_p3200', ARRAY[100], 'Check count from id_taptest_table_p0_p3000_p3200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p3000_p3300', ARRAY[100], 'Check count from id_taptest_table_p0_p3000_p3300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p3000_p3400', ARRAY[100], 'Check count from id_taptest_table_p0_p3000_p3400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p3000_p3500', ARRAY[100], 'Check count from id_taptest_table_p0_p3000_p3500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p3000_p3600', ARRAY[100], 'Check count from id_taptest_table_p0_p3000_p3600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p3000_p3700', ARRAY[100], 'Check count from id_taptest_table_p0_p3000_p3700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p3000_p3800', ARRAY[100], 'Check count from id_taptest_table_p0_p3000_p3800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p3000_p3900', ARRAY[100], 'Check count from id_taptest_table_p0_p3000_p3900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p4000_p4000', ARRAY[100], 'Check count from id_taptest_table_p0_p4000_p4000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p4000_p4100', ARRAY[100], 'Check count from id_taptest_table_p0_p4000_p4100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p4000_p4200', ARRAY[100], 'Check count from id_taptest_table_p0_p4000_p4200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p4000_p4300', ARRAY[100], 'Check count from id_taptest_table_p0_p4000_p4300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p4000_p4400', ARRAY[100], 'Check count from id_taptest_table_p0_p4000_p4400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p4000_p4500', ARRAY[100], 'Check count from id_taptest_table_p0_p4000_p4500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p4000_p4600', ARRAY[100], 'Check count from id_taptest_table_p0_p4000_p4600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p4000_p4700', ARRAY[100], 'Check count from id_taptest_table_p0_p4000_p4700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p4000_p4800', ARRAY[100], 'Check count from id_taptest_table_p0_p4000_p4800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p4000_p4900', ARRAY[100], 'Check count from id_taptest_table_p0_p4000_p4900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p5000_p5000', ARRAY[100], 'Check count from id_taptest_table_p0_p5000_p5000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p5000_p5100', ARRAY[100], 'Check count from id_taptest_table_p0_p5000_p5100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p5000_p5200', ARRAY[100], 'Check count from id_taptest_table_p0_p5000_p5200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p5000_p5300', ARRAY[100], 'Check count from id_taptest_table_p0_p5000_p5300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p5000_p5400', ARRAY[100], 'Check count from id_taptest_table_p0_p5000_p5400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p5000_p5500', ARRAY[100], 'Check count from id_taptest_table_p0_p5000_p5500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p5000_p5600', ARRAY[100], 'Check count from id_taptest_table_p0_p5000_p5600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p5000_p5700', ARRAY[100], 'Check count from id_taptest_table_p0_p5000_p5700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p5000_p5800', ARRAY[100], 'Check count from id_taptest_table_p0_p5000_p5800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p5000_p5900', ARRAY[100], 'Check count from id_taptest_table_p0_p5000_p5900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p6000_p6000', ARRAY[100], 'Check count from id_taptest_table_p0_p6000_p6000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p6000_p6100', ARRAY[100], 'Check count from id_taptest_table_p0_p6000_p6100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p6000_p6200', ARRAY[100], 'Check count from id_taptest_table_p0_p6000_p6200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p6000_p6300', ARRAY[100], 'Check count from id_taptest_table_p0_p6000_p6300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p6000_p6400', ARRAY[100], 'Check count from id_taptest_table_p0_p6000_p6400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p6000_p6500', ARRAY[100], 'Check count from id_taptest_table_p0_p6000_p6500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p6000_p6600', ARRAY[100], 'Check count from id_taptest_table_p0_p6000_p6600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p6000_p6700', ARRAY[100], 'Check count from id_taptest_table_p0_p6000_p6700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p6000_p6800', ARRAY[100], 'Check count from id_taptest_table_p0_p6000_p6800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p6000_p6900', ARRAY[100], 'Check count from id_taptest_table_p0_p6000_p6900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p7000_p7000', ARRAY[100], 'Check count from id_taptest_table_p0_p7000_p7000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p7000_p7100', ARRAY[100], 'Check count from id_taptest_table_p0_p7000_p7100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p7000_p7200', ARRAY[100], 'Check count from id_taptest_table_p0_p7000_p7200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p7000_p7300', ARRAY[100], 'Check count from id_taptest_table_p0_p7000_p7300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p7000_p7400', ARRAY[100], 'Check count from id_taptest_table_p0_p7000_p7400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p7000_p7500', ARRAY[100], 'Check count from id_taptest_table_p0_p7000_p7500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p7000_p7600', ARRAY[100], 'Check count from id_taptest_table_p0_p7000_p7600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p7000_p7700', ARRAY[100], 'Check count from id_taptest_table_p0_p7000_p7700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p7000_p7800', ARRAY[100], 'Check count from id_taptest_table_p0_p7000_p7800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p7000_p7900', ARRAY[100], 'Check count from id_taptest_table_p0_p7000_p7900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p8000_p8000', ARRAY[100], 'Check count from id_taptest_table_p0_p8000_p8000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p8000_p8100', ARRAY[100], 'Check count from id_taptest_table_p0_p8000_p8100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p8000_p8200', ARRAY[100], 'Check count from id_taptest_table_p0_p8000_p8200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p8000_p8300', ARRAY[100], 'Check count from id_taptest_table_p0_p8000_p8300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p8000_p8400', ARRAY[100], 'Check count from id_taptest_table_p0_p8000_p8400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p8000_p8500', ARRAY[100], 'Check count from id_taptest_table_p0_p8000_p8500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p8000_p8600', ARRAY[100], 'Check count from id_taptest_table_p0_p8000_p8600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p8000_p8700', ARRAY[100], 'Check count from id_taptest_table_p0_p8000_p8700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p8000_p8800', ARRAY[100], 'Check count from id_taptest_table_p0_p8000_p8800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p8000_p8900', ARRAY[100], 'Check count from id_taptest_table_p0_p8000_p8900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p9000_p9000', ARRAY[100], 'Check count from id_taptest_table_p0_p9000_p9000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p9000_p9100', ARRAY[100], 'Check count from id_taptest_table_p0_p9000_p9100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p9000_p9200', ARRAY[100], 'Check count from id_taptest_table_p0_p9000_p9200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p9000_p9300', ARRAY[100], 'Check count from id_taptest_table_p0_p9000_p9300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p9000_p9400', ARRAY[100], 'Check count from id_taptest_table_p0_p9000_p9400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p9000_p9500', ARRAY[100], 'Check count from id_taptest_table_p0_p9000_p9500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p9000_p9600', ARRAY[100], 'Check count from id_taptest_table_p0_p9000_p9600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p9000_p9700', ARRAY[100], 'Check count from id_taptest_table_p0_p9000_p9700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p9000_p9800', ARRAY[100], 'Check count from id_taptest_table_p0_p9000_p9800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p9000_p9900', ARRAY[100], 'Check count from id_taptest_table_p0_p9000_p9900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p10000_p10000', ARRAY[100], 'Check count from id_taptest_table_p10000_p10000_p10000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p10000_p10100', ARRAY[100], 'Check count from id_taptest_table_p10000_p10000_p10100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p10000_p10200', ARRAY[100], 'Check count from id_taptest_table_p10000_p10000_p10200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p10000_p10300', ARRAY[100], 'Check count from id_taptest_table_p10000_p10000_p10300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p10000_p10400', ARRAY[100], 'Check count from id_taptest_table_p10000_p10000_p10400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p10000_p10500', ARRAY[100], 'Check count from id_taptest_table_p10000_p10000_p10500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p10000_p10600', ARRAY[100], 'Check count from id_taptest_table_p10000_p10000_p10600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p10000_p10700', ARRAY[100], 'Check count from id_taptest_table_p10000_p10000_p10700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p10000_p10800', ARRAY[100], 'Check count from id_taptest_table_p10000_p10000_p10800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p10000_p10900', ARRAY[100], 'Check count from id_taptest_table_p10000_p10000_p10900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p11000_p11000', ARRAY[100], 'Check count from id_taptest_table_p10000_p11000_p11000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p11000_p11100', ARRAY[100], 'Check count from id_taptest_table_p10000_p11000_p11100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p11000_p11200', ARRAY[100], 'Check count from id_taptest_table_p10000_p11000_p11200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p11000_p11300', ARRAY[100], 'Check count from id_taptest_table_p10000_p11000_p11300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p11000_p11400', ARRAY[100], 'Check count from id_taptest_table_p10000_p11000_p11400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p11000_p11500', ARRAY[100], 'Check count from id_taptest_table_p10000_p11000_p11500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p11000_p11600', ARRAY[100], 'Check count from id_taptest_table_p10000_p11000_p11600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p11000_p11700', ARRAY[100], 'Check count from id_taptest_table_p10000_p11000_p11700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p11000_p11800', ARRAY[100], 'Check count from id_taptest_table_p10000_p11000_p11800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p11000_p11900', ARRAY[100], 'Check count from id_taptest_table_p10000_p11000_p11900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p12000_p12000', ARRAY[100], 'Check count from id_taptest_table_p10000_p12000_p12000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p12000_p12100', ARRAY[100], 'Check count from id_taptest_table_p10000_p12000_p12100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p12000_p12200', ARRAY[100], 'Check count from id_taptest_table_p10000_p12000_p12200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p12000_p12300', ARRAY[100], 'Check count from id_taptest_table_p10000_p12000_p12300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p12000_p12400', ARRAY[100], 'Check count from id_taptest_table_p10000_p12000_p12400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p12000_p12500', ARRAY[100], 'Check count from id_taptest_table_p10000_p12000_p12500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p12000_p12600', ARRAY[100], 'Check count from id_taptest_table_p10000_p12000_p12600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p12000_p12700', ARRAY[100], 'Check count from id_taptest_table_p10000_p12000_p12700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p12000_p12800', ARRAY[100], 'Check count from id_taptest_table_p10000_p12000_p12800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p12000_p12900', ARRAY[100], 'Check count from id_taptest_table_p10000_p12000_p12900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p13000_p13000', ARRAY[100], 'Check count from id_taptest_table_p10000_p13000_p13000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p13000_p13100', ARRAY[100], 'Check count from id_taptest_table_p10000_p13000_p13100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p13000_p13200', ARRAY[100], 'Check count from id_taptest_table_p10000_p13000_p13200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p13000_p13300', ARRAY[100], 'Check count from id_taptest_table_p10000_p13000_p13300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p13000_p13400', ARRAY[100], 'Check count from id_taptest_table_p10000_p13000_p13400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p13000_p13500', ARRAY[100], 'Check count from id_taptest_table_p10000_p13000_p13500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p13000_p13600', ARRAY[100], 'Check count from id_taptest_table_p10000_p13000_p13600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p13000_p13700', ARRAY[100], 'Check count from id_taptest_table_p10000_p13000_p13700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p13000_p13800', ARRAY[100], 'Check count from id_taptest_table_p10000_p13000_p13800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p13000_p13900', ARRAY[100], 'Check count from id_taptest_table_p10000_p13000_p13900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p14000_p14000', ARRAY[100], 'Check count from id_taptest_table_p10000_p14000_p14000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p14000_p14100', ARRAY[100], 'Check count from id_taptest_table_p10000_p14000_p14100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p14000_p14200', ARRAY[100], 'Check count from id_taptest_table_p10000_p14000_p14200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p14000_p14300', ARRAY[100], 'Check count from id_taptest_table_p10000_p14000_p14300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p14000_p14400', ARRAY[100], 'Check count from id_taptest_table_p10000_p14000_p14400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p14000_p14500', ARRAY[100], 'Check count from id_taptest_table_p10000_p14000_p14500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p14000_p14600', ARRAY[100], 'Check count from id_taptest_table_p10000_p14000_p14600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p14000_p14700', ARRAY[100], 'Check count from id_taptest_table_p10000_p14000_p14700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p14000_p14800', ARRAY[100], 'Check count from id_taptest_table_p10000_p14000_p14800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p14000_p14900', ARRAY[100], 'Check count from id_taptest_table_p10000_p14000_p14900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p15000_p15000', ARRAY[100], 'Check count from id_taptest_table_p10000_p15000_p15000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p15000_p15100', ARRAY[100], 'Check count from id_taptest_table_p10000_p15000_p15100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p15000_p15200', ARRAY[100], 'Check count from id_taptest_table_p10000_p15000_p15200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p15000_p15300', ARRAY[100], 'Check count from id_taptest_table_p10000_p15000_p15300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p15000_p15400', ARRAY[100], 'Check count from id_taptest_table_p10000_p15000_p15400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p15000_p15500', ARRAY[100], 'Check count from id_taptest_table_p10000_p15000_p15500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p15000_p15600', ARRAY[100], 'Check count from id_taptest_table_p10000_p15000_p15600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p15000_p15700', ARRAY[100], 'Check count from id_taptest_table_p10000_p15000_p15700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p15000_p15800', ARRAY[100], 'Check count from id_taptest_table_p10000_p15000_p15800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p15000_p15900', ARRAY[100], 'Check count from id_taptest_table_p10000_p15000_p15900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p16000_p16000', ARRAY[100], 'Check count from id_taptest_table_p10000_p16000_p16000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p16000_p16100', ARRAY[100], 'Check count from id_taptest_table_p10000_p16000_p16100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p16000_p16200', ARRAY[100], 'Check count from id_taptest_table_p10000_p16000_p16200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p16000_p16300', ARRAY[100], 'Check count from id_taptest_table_p10000_p16000_p16300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p16000_p16400', ARRAY[100], 'Check count from id_taptest_table_p10000_p16000_p16400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p16000_p16500', ARRAY[100], 'Check count from id_taptest_table_p10000_p16000_p16500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p16000_p16600', ARRAY[100], 'Check count from id_taptest_table_p10000_p16000_p16600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p16000_p16700', ARRAY[100], 'Check count from id_taptest_table_p10000_p16000_p16700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p16000_p16800', ARRAY[100], 'Check count from id_taptest_table_p10000_p16000_p16800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p16000_p16900', ARRAY[100], 'Check count from id_taptest_table_p10000_p16000_p16900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p17000_p17000', ARRAY[100], 'Check count from id_taptest_table_p10000_p17000_p17000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p17000_p17100', ARRAY[100], 'Check count from id_taptest_table_p10000_p17000_p17100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p17000_p17200', ARRAY[100], 'Check count from id_taptest_table_p10000_p17000_p17200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p17000_p17300', ARRAY[100], 'Check count from id_taptest_table_p10000_p17000_p17300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p17000_p17400', ARRAY[100], 'Check count from id_taptest_table_p10000_p17000_p17400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p17000_p17500', ARRAY[100], 'Check count from id_taptest_table_p10000_p17000_p17500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p17000_p17600', ARRAY[100], 'Check count from id_taptest_table_p10000_p17000_p17600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p17000_p17700', ARRAY[100], 'Check count from id_taptest_table_p10000_p17000_p17700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p17000_p17800', ARRAY[100], 'Check count from id_taptest_table_p10000_p17000_p17800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p17000_p17900', ARRAY[100], 'Check count from id_taptest_table_p10000_p17000_p17900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p18000_p18000', ARRAY[100], 'Check count from id_taptest_table_p10000_p18000_p18000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p18000_p18100', ARRAY[100], 'Check count from id_taptest_table_p10000_p18000_p18100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p18000_p18200', ARRAY[100], 'Check count from id_taptest_table_p10000_p18000_p18200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p18000_p18300', ARRAY[100], 'Check count from id_taptest_table_p10000_p18000_p18300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p18000_p18400', ARRAY[100], 'Check count from id_taptest_table_p10000_p18000_p18400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p18000_p18500', ARRAY[100], 'Check count from id_taptest_table_p10000_p18000_p18500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p18000_p18600', ARRAY[100], 'Check count from id_taptest_table_p10000_p18000_p18600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p18000_p18700', ARRAY[100], 'Check count from id_taptest_table_p10000_p18000_p18700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p18000_p18800', ARRAY[100], 'Check count from id_taptest_table_p10000_p18000_p18800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p18000_p18900', ARRAY[100], 'Check count from id_taptest_table_p10000_p18000_p18900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p19000_p19000', ARRAY[100], 'Check count from id_taptest_table_p10000_p19000_p19000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p19000_p19100', ARRAY[100], 'Check count from id_taptest_table_p10000_p19000_p19100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p19000_p19200', ARRAY[100], 'Check count from id_taptest_table_p10000_p19000_p19200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p19000_p19300', ARRAY[100], 'Check count from id_taptest_table_p10000_p19000_p19300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p19000_p19400', ARRAY[100], 'Check count from id_taptest_table_p10000_p19000_p19400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p19000_p19500', ARRAY[100], 'Check count from id_taptest_table_p10000_p19000_p19500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p19000_p19600', ARRAY[100], 'Check count from id_taptest_table_p10000_p19000_p19600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p19000_p19700', ARRAY[100], 'Check count from id_taptest_table_p10000_p19000_p19700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p19000_p19800', ARRAY[100], 'Check count from id_taptest_table_p10000_p19000_p19800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10000_p19000_p19900', ARRAY[100], 'Check count from id_taptest_table_p10000_p19000_p19900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p20000_p20000', ARRAY[100], 'Check count from id_taptest_table_p20000_p20000_p20000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p20000_p20100', ARRAY[100], 'Check count from id_taptest_table_p20000_p20000_p20100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p20000_p20200', ARRAY[100], 'Check count from id_taptest_table_p20000_p20000_p20200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p20000_p20300', ARRAY[100], 'Check count from id_taptest_table_p20000_p20000_p20300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p20000_p20400', ARRAY[100], 'Check count from id_taptest_table_p20000_p20000_p20400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p20000_p20500', ARRAY[100], 'Check count from id_taptest_table_p20000_p20000_p20500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p20000_p20600', ARRAY[100], 'Check count from id_taptest_table_p20000_p20000_p20600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p20000_p20700', ARRAY[100], 'Check count from id_taptest_table_p20000_p20000_p20700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p20000_p20800', ARRAY[100], 'Check count from id_taptest_table_p20000_p20000_p20800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p20000_p20900', ARRAY[100], 'Check count from id_taptest_table_p20000_p20000_p20900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p21000_p21000', ARRAY[100], 'Check count from id_taptest_table_p20000_p21000_p21000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p21000_p21100', ARRAY[100], 'Check count from id_taptest_table_p20000_p21000_p21100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p21000_p21200', ARRAY[100], 'Check count from id_taptest_table_p20000_p21000_p21200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p21000_p21300', ARRAY[100], 'Check count from id_taptest_table_p20000_p21000_p21300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p21000_p21400', ARRAY[100], 'Check count from id_taptest_table_p20000_p21000_p21400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p21000_p21500', ARRAY[100], 'Check count from id_taptest_table_p20000_p21000_p21500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p21000_p21600', ARRAY[100], 'Check count from id_taptest_table_p20000_p21000_p21600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p21000_p21700', ARRAY[100], 'Check count from id_taptest_table_p20000_p21000_p21700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p21000_p21800', ARRAY[100], 'Check count from id_taptest_table_p20000_p21000_p21800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p21000_p21900', ARRAY[100], 'Check count from id_taptest_table_p20000_p21000_p21900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p22000_p22000', ARRAY[100], 'Check count from id_taptest_table_p20000_p22000_p22000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p22000_p22100', ARRAY[100], 'Check count from id_taptest_table_p20000_p22000_p22100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p22000_p22200', ARRAY[100], 'Check count from id_taptest_table_p20000_p22000_p22200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p22000_p22300', ARRAY[100], 'Check count from id_taptest_table_p20000_p22000_p22300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p22000_p22400', ARRAY[100], 'Check count from id_taptest_table_p20000_p22000_p22400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p22000_p22500', ARRAY[100], 'Check count from id_taptest_table_p20000_p22000_p22500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p22000_p22600', ARRAY[100], 'Check count from id_taptest_table_p20000_p22000_p22600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p22000_p22700', ARRAY[100], 'Check count from id_taptest_table_p20000_p22000_p22700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p22000_p22800', ARRAY[100], 'Check count from id_taptest_table_p20000_p22000_p22800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p22000_p22900', ARRAY[100], 'Check count from id_taptest_table_p20000_p22000_p22900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p23000_p23000', ARRAY[100], 'Check count from id_taptest_table_p20000_p23000_p23000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p23000_p23100', ARRAY[100], 'Check count from id_taptest_table_p20000_p23000_p23100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p23000_p23200', ARRAY[100], 'Check count from id_taptest_table_p20000_p23000_p23200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p23000_p23300', ARRAY[100], 'Check count from id_taptest_table_p20000_p23000_p23300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p23000_p23400', ARRAY[100], 'Check count from id_taptest_table_p20000_p23000_p23400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p23000_p23500', ARRAY[100], 'Check count from id_taptest_table_p20000_p23000_p23500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p23000_p23600', ARRAY[100], 'Check count from id_taptest_table_p20000_p23000_p23600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p23000_p23700', ARRAY[100], 'Check count from id_taptest_table_p20000_p23000_p23700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p23000_p23800', ARRAY[100], 'Check count from id_taptest_table_p20000_p23000_p23800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p23000_p23900', ARRAY[100], 'Check count from id_taptest_table_p20000_p23000_p23900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p24000_p24000', ARRAY[100], 'Check count from id_taptest_table_p20000_p24000_p24000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p24000_p24100', ARRAY[100], 'Check count from id_taptest_table_p20000_p24000_p24100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p24000_p24200', ARRAY[100], 'Check count from id_taptest_table_p20000_p24000_p24200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p24000_p24300', ARRAY[100], 'Check count from id_taptest_table_p20000_p24000_p24300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p24000_p24400', ARRAY[100], 'Check count from id_taptest_table_p20000_p24000_p24400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p24000_p24500', ARRAY[100], 'Check count from id_taptest_table_p20000_p24000_p24500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p24000_p24600', ARRAY[100], 'Check count from id_taptest_table_p20000_p24000_p24600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p24000_p24700', ARRAY[100], 'Check count from id_taptest_table_p20000_p24000_p24700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p24000_p24800', ARRAY[100], 'Check count from id_taptest_table_p20000_p24000_p24800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p24000_p24900', ARRAY[100], 'Check count from id_taptest_table_p20000_p24000_p24900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p25000_p25000', ARRAY[100], 'Check count from id_taptest_table_p20000_p25000_p25000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p25000_p25100', ARRAY[100], 'Check count from id_taptest_table_p20000_p25000_p25100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p25000_p25200', ARRAY[100], 'Check count from id_taptest_table_p20000_p25000_p25200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p25000_p25300', ARRAY[100], 'Check count from id_taptest_table_p20000_p25000_p25300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p25000_p25400', ARRAY[100], 'Check count from id_taptest_table_p20000_p25000_p25400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p25000_p25500', ARRAY[100], 'Check count from id_taptest_table_p20000_p25000_p25500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p25000_p25600', ARRAY[100], 'Check count from id_taptest_table_p20000_p25000_p25600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p25000_p25700', ARRAY[100], 'Check count from id_taptest_table_p20000_p25000_p25700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p25000_p25800', ARRAY[100], 'Check count from id_taptest_table_p20000_p25000_p25800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p25000_p25900', ARRAY[100], 'Check count from id_taptest_table_p20000_p25000_p25900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p26000_p26000', ARRAY[100], 'Check count from id_taptest_table_p20000_p26000_p26000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p26000_p26100', ARRAY[100], 'Check count from id_taptest_table_p20000_p26000_p26100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p26000_p26200', ARRAY[100], 'Check count from id_taptest_table_p20000_p26000_p26200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p26000_p26300', ARRAY[100], 'Check count from id_taptest_table_p20000_p26000_p26300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p26000_p26400', ARRAY[100], 'Check count from id_taptest_table_p20000_p26000_p26400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p26000_p26500', ARRAY[100], 'Check count from id_taptest_table_p20000_p26000_p26500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p26000_p26600', ARRAY[100], 'Check count from id_taptest_table_p20000_p26000_p26600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p26000_p26700', ARRAY[100], 'Check count from id_taptest_table_p20000_p26000_p26700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p26000_p26800', ARRAY[100], 'Check count from id_taptest_table_p20000_p26000_p26800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p26000_p26900', ARRAY[100], 'Check count from id_taptest_table_p20000_p26000_p26900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p27000_p27000', ARRAY[100], 'Check count from id_taptest_table_p20000_p27000_p27000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p27000_p27100', ARRAY[100], 'Check count from id_taptest_table_p20000_p27000_p27100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p27000_p27200', ARRAY[100], 'Check count from id_taptest_table_p20000_p27000_p27200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p27000_p27300', ARRAY[100], 'Check count from id_taptest_table_p20000_p27000_p27300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p27000_p27400', ARRAY[100], 'Check count from id_taptest_table_p20000_p27000_p27400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p27000_p27500', ARRAY[100], 'Check count from id_taptest_table_p20000_p27000_p27500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p27000_p27600', ARRAY[100], 'Check count from id_taptest_table_p20000_p27000_p27600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p27000_p27700', ARRAY[100], 'Check count from id_taptest_table_p20000_p27000_p27700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p27000_p27800', ARRAY[100], 'Check count from id_taptest_table_p20000_p27000_p27800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p27000_p27900', ARRAY[100], 'Check count from id_taptest_table_p20000_p27000_p27900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p28000_p28000', ARRAY[100], 'Check count from id_taptest_table_p20000_p28000_p28000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p28000_p28100', ARRAY[100], 'Check count from id_taptest_table_p20000_p28000_p28100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p28000_p28200', ARRAY[100], 'Check count from id_taptest_table_p20000_p28000_p28200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p28000_p28300', ARRAY[100], 'Check count from id_taptest_table_p20000_p28000_p28300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p28000_p28400', ARRAY[100], 'Check count from id_taptest_table_p20000_p28000_p28400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p28000_p28500', ARRAY[100], 'Check count from id_taptest_table_p20000_p28000_p28500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p28000_p28600', ARRAY[100], 'Check count from id_taptest_table_p20000_p28000_p28600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p28000_p28700', ARRAY[100], 'Check count from id_taptest_table_p20000_p28000_p28700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p28000_p28800', ARRAY[100], 'Check count from id_taptest_table_p20000_p28000_p28800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p28000_p28900', ARRAY[100], 'Check count from id_taptest_table_p20000_p28000_p28900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p29000_p29000', ARRAY[100], 'Check count from id_taptest_table_p20000_p29000_p29000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p29000_p29100', ARRAY[100], 'Check count from id_taptest_table_p20000_p29000_p29100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p29000_p29200', ARRAY[100], 'Check count from id_taptest_table_p20000_p29000_p29200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p29000_p29300', ARRAY[100], 'Check count from id_taptest_table_p20000_p29000_p29300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p29000_p29400', ARRAY[100], 'Check count from id_taptest_table_p20000_p29000_p29400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p29000_p29500', ARRAY[100], 'Check count from id_taptest_table_p20000_p29000_p29500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p29000_p29600', ARRAY[100], 'Check count from id_taptest_table_p20000_p29000_p29600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p29000_p29700', ARRAY[100], 'Check count from id_taptest_table_p20000_p29000_p29700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p29000_p29800', ARRAY[100], 'Check count from id_taptest_table_p20000_p29000_p29800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20000_p29000_p29900', ARRAY[100], 'Check count from id_taptest_table_p20000_p29000_p29900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p30000_p30000', ARRAY[100], 'Check count from id_taptest_table_p30000_p30000_p30000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p30000_p30100', ARRAY[100], 'Check count from id_taptest_table_p30000_p30000_p30100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p30000_p30200', ARRAY[100], 'Check count from id_taptest_table_p30000_p30000_p30200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p30000_p30300', ARRAY[100], 'Check count from id_taptest_table_p30000_p30000_p30300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p30000_p30400', ARRAY[100], 'Check count from id_taptest_table_p30000_p30000_p30400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p30000_p30500', ARRAY[100], 'Check count from id_taptest_table_p30000_p30000_p30500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p30000_p30600', ARRAY[100], 'Check count from id_taptest_table_p30000_p30000_p30600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p30000_p30700', ARRAY[100], 'Check count from id_taptest_table_p30000_p30000_p30700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p30000_p30800', ARRAY[100], 'Check count from id_taptest_table_p30000_p30000_p30800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p30000_p30900', ARRAY[100], 'Check count from id_taptest_table_p30000_p30000_p30900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p31000_p31000', ARRAY[100], 'Check count from id_taptest_table_p30000_p31000_p31000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p31000_p31100', ARRAY[100], 'Check count from id_taptest_table_p30000_p31000_p31100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p31000_p31200', ARRAY[100], 'Check count from id_taptest_table_p30000_p31000_p31200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p31000_p31300', ARRAY[100], 'Check count from id_taptest_table_p30000_p31000_p31300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p31000_p31400', ARRAY[100], 'Check count from id_taptest_table_p30000_p31000_p31400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p31000_p31500', ARRAY[100], 'Check count from id_taptest_table_p30000_p31000_p31500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p31000_p31600', ARRAY[100], 'Check count from id_taptest_table_p30000_p31000_p31600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p31000_p31700', ARRAY[100], 'Check count from id_taptest_table_p30000_p31000_p31700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p31000_p31800', ARRAY[100], 'Check count from id_taptest_table_p30000_p31000_p31800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p31000_p31900', ARRAY[100], 'Check count from id_taptest_table_p30000_p31000_p31900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p32000_p32000', ARRAY[100], 'Check count from id_taptest_table_p30000_p32000_p32000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p32000_p32100', ARRAY[100], 'Check count from id_taptest_table_p30000_p32000_p32100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p32000_p32200', ARRAY[100], 'Check count from id_taptest_table_p30000_p32000_p32200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p32000_p32300', ARRAY[100], 'Check count from id_taptest_table_p30000_p32000_p32300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p32000_p32400', ARRAY[100], 'Check count from id_taptest_table_p30000_p32000_p32400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p32000_p32500', ARRAY[100], 'Check count from id_taptest_table_p30000_p32000_p32500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p32000_p32600', ARRAY[100], 'Check count from id_taptest_table_p30000_p32000_p32600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p32000_p32700', ARRAY[100], 'Check count from id_taptest_table_p30000_p32000_p32700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p32000_p32800', ARRAY[100], 'Check count from id_taptest_table_p30000_p32000_p32800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p32000_p32900', ARRAY[100], 'Check count from id_taptest_table_p30000_p32000_p32900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p33000_p33000', ARRAY[100], 'Check count from id_taptest_table_p30000_p33000_p33000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p33000_p33100', ARRAY[100], 'Check count from id_taptest_table_p30000_p33000_p33100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p33000_p33200', ARRAY[100], 'Check count from id_taptest_table_p30000_p33000_p33200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p33000_p33300', ARRAY[100], 'Check count from id_taptest_table_p30000_p33000_p33300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p33000_p33400', ARRAY[100], 'Check count from id_taptest_table_p30000_p33000_p33400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p33000_p33500', ARRAY[100], 'Check count from id_taptest_table_p30000_p33000_p33500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p33000_p33600', ARRAY[100], 'Check count from id_taptest_table_p30000_p33000_p33600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p33000_p33700', ARRAY[100], 'Check count from id_taptest_table_p30000_p33000_p33700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p33000_p33800', ARRAY[100], 'Check count from id_taptest_table_p30000_p33000_p33800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p33000_p33900', ARRAY[100], 'Check count from id_taptest_table_p30000_p33000_p33900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p34000_p34000', ARRAY[100], 'Check count from id_taptest_table_p30000_p34000_p34000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p34000_p34100', ARRAY[100], 'Check count from id_taptest_table_p30000_p34000_p34100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p34000_p34200', ARRAY[100], 'Check count from id_taptest_table_p30000_p34000_p34200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p34000_p34300', ARRAY[100], 'Check count from id_taptest_table_p30000_p34000_p34300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p34000_p34400', ARRAY[100], 'Check count from id_taptest_table_p30000_p34000_p34400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p34000_p34500', ARRAY[100], 'Check count from id_taptest_table_p30000_p34000_p34500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p34000_p34600', ARRAY[100], 'Check count from id_taptest_table_p30000_p34000_p34600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p34000_p34700', ARRAY[100], 'Check count from id_taptest_table_p30000_p34000_p34700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p34000_p34800', ARRAY[100], 'Check count from id_taptest_table_p30000_p34000_p34800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p34000_p34900', ARRAY[100], 'Check count from id_taptest_table_p30000_p34000_p34900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p35000_p35000', ARRAY[100], 'Check count from id_taptest_table_p30000_p35000_p35000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p35000_p35100', ARRAY[100], 'Check count from id_taptest_table_p30000_p35000_p35100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p35000_p35200', ARRAY[100], 'Check count from id_taptest_table_p30000_p35000_p35200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p35000_p35300', ARRAY[100], 'Check count from id_taptest_table_p30000_p35000_p35300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p35000_p35400', ARRAY[100], 'Check count from id_taptest_table_p30000_p35000_p35400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p35000_p35500', ARRAY[100], 'Check count from id_taptest_table_p30000_p35000_p35500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p35000_p35600', ARRAY[100], 'Check count from id_taptest_table_p30000_p35000_p35600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p35000_p35700', ARRAY[100], 'Check count from id_taptest_table_p30000_p35000_p35700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p35000_p35800', ARRAY[100], 'Check count from id_taptest_table_p30000_p35000_p35800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p35000_p35900', ARRAY[100], 'Check count from id_taptest_table_p30000_p35000_p35900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p36000_p36000', ARRAY[100], 'Check count from id_taptest_table_p30000_p36000_p36000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p36000_p36100', ARRAY[100], 'Check count from id_taptest_table_p30000_p36000_p36100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p36000_p36200', ARRAY[100], 'Check count from id_taptest_table_p30000_p36000_p36200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p36000_p36300', ARRAY[100], 'Check count from id_taptest_table_p30000_p36000_p36300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p36000_p36400', ARRAY[100], 'Check count from id_taptest_table_p30000_p36000_p36400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p36000_p36500', ARRAY[100], 'Check count from id_taptest_table_p30000_p36000_p36500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p36000_p36600', ARRAY[100], 'Check count from id_taptest_table_p30000_p36000_p36600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p36000_p36700', ARRAY[100], 'Check count from id_taptest_table_p30000_p36000_p36700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p36000_p36800', ARRAY[100], 'Check count from id_taptest_table_p30000_p36000_p36800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p36000_p36900', ARRAY[100], 'Check count from id_taptest_table_p30000_p36000_p36900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p37000_p37000', ARRAY[100], 'Check count from id_taptest_table_p30000_p37000_p37000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p37000_p37100', ARRAY[100], 'Check count from id_taptest_table_p30000_p37000_p37100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p37000_p37200', ARRAY[100], 'Check count from id_taptest_table_p30000_p37000_p37200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p37000_p37300', ARRAY[100], 'Check count from id_taptest_table_p30000_p37000_p37300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p37000_p37400', ARRAY[100], 'Check count from id_taptest_table_p30000_p37000_p37400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p37000_p37500', ARRAY[100], 'Check count from id_taptest_table_p30000_p37000_p37500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p37000_p37600', ARRAY[100], 'Check count from id_taptest_table_p30000_p37000_p37600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p37000_p37700', ARRAY[100], 'Check count from id_taptest_table_p30000_p37000_p37700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p37000_p37800', ARRAY[100], 'Check count from id_taptest_table_p30000_p37000_p37800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p37000_p37900', ARRAY[100], 'Check count from id_taptest_table_p30000_p37000_p37900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p38000_p38000', ARRAY[100], 'Check count from id_taptest_table_p30000_p38000_p38000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p38000_p38100', ARRAY[100], 'Check count from id_taptest_table_p30000_p38000_p38100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p38000_p38200', ARRAY[100], 'Check count from id_taptest_table_p30000_p38000_p38200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p38000_p38300', ARRAY[100], 'Check count from id_taptest_table_p30000_p38000_p38300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p38000_p38400', ARRAY[100], 'Check count from id_taptest_table_p30000_p38000_p38400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p38000_p38500', ARRAY[100], 'Check count from id_taptest_table_p30000_p38000_p38500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p38000_p38600', ARRAY[100], 'Check count from id_taptest_table_p30000_p38000_p38600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p38000_p38700', ARRAY[100], 'Check count from id_taptest_table_p30000_p38000_p38700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p38000_p38800', ARRAY[100], 'Check count from id_taptest_table_p30000_p38000_p38800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p38000_p38900', ARRAY[100], 'Check count from id_taptest_table_p30000_p38000_p38900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p39000_p39000', ARRAY[100], 'Check count from id_taptest_table_p30000_p39000_p39000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p39000_p39100', ARRAY[100], 'Check count from id_taptest_table_p30000_p39000_p39100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p39000_p39200', ARRAY[100], 'Check count from id_taptest_table_p30000_p39000_p39200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p39000_p39300', ARRAY[100], 'Check count from id_taptest_table_p30000_p39000_p39300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p39000_p39400', ARRAY[100], 'Check count from id_taptest_table_p30000_p39000_p39400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p39000_p39500', ARRAY[100], 'Check count from id_taptest_table_p30000_p39000_p39500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p39000_p39600', ARRAY[100], 'Check count from id_taptest_table_p30000_p39000_p39600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p39000_p39700', ARRAY[100], 'Check count from id_taptest_table_p30000_p39000_p39700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p39000_p39800', ARRAY[100], 'Check count from id_taptest_table_p30000_p39000_p39800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30000_p39000_p39900', ARRAY[100], 'Check count from id_taptest_table_p30000_p39000_p39900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p40000_p40000', ARRAY[100], 'Check count from id_taptest_table_p40000_p40000_p40000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p40000_p40100', ARRAY[100], 'Check count from id_taptest_table_p40000_p40000_p40100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p40000_p40200', ARRAY[100], 'Check count from id_taptest_table_p40000_p40000_p40200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p40000_p40300', ARRAY[100], 'Check count from id_taptest_table_p40000_p40000_p40300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p40000_p40400', ARRAY[100], 'Check count from id_taptest_table_p40000_p40000_p40400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p40000_p40500', ARRAY[100], 'Check count from id_taptest_table_p40000_p40000_p40500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p40000_p40600', ARRAY[100], 'Check count from id_taptest_table_p40000_p40000_p40600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p40000_p40700', ARRAY[100], 'Check count from id_taptest_table_p40000_p40000_p40700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p40000_p40800', ARRAY[100], 'Check count from id_taptest_table_p40000_p40000_p40800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p40000_p40900', ARRAY[100], 'Check count from id_taptest_table_p40000_p40000_p40900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p41000_p41000', ARRAY[100], 'Check count from id_taptest_table_p40000_p41000_p41000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p41000_p41100', ARRAY[100], 'Check count from id_taptest_table_p40000_p41000_p41100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p41000_p41200', ARRAY[100], 'Check count from id_taptest_table_p40000_p41000_p41200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p41000_p41300', ARRAY[100], 'Check count from id_taptest_table_p40000_p41000_p41300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p41000_p41400', ARRAY[100], 'Check count from id_taptest_table_p40000_p41000_p41400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p41000_p41500', ARRAY[100], 'Check count from id_taptest_table_p40000_p41000_p41500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p41000_p41600', ARRAY[100], 'Check count from id_taptest_table_p40000_p41000_p41600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p41000_p41700', ARRAY[100], 'Check count from id_taptest_table_p40000_p41000_p41700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p41000_p41800', ARRAY[100], 'Check count from id_taptest_table_p40000_p41000_p41800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p41000_p41900', ARRAY[100], 'Check count from id_taptest_table_p40000_p41000_p41900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p42000_p42000', ARRAY[100], 'Check count from id_taptest_table_p40000_p42000_p42000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p42000_p42100', ARRAY[100], 'Check count from id_taptest_table_p40000_p42000_p42100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p42000_p42200', ARRAY[100], 'Check count from id_taptest_table_p40000_p42000_p42200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p42000_p42300', ARRAY[100], 'Check count from id_taptest_table_p40000_p42000_p42300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p42000_p42400', ARRAY[100], 'Check count from id_taptest_table_p40000_p42000_p42400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p42000_p42500', ARRAY[100], 'Check count from id_taptest_table_p40000_p42000_p42500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p42000_p42600', ARRAY[100], 'Check count from id_taptest_table_p40000_p42000_p42600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p42000_p42700', ARRAY[100], 'Check count from id_taptest_table_p40000_p42000_p42700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p42000_p42800', ARRAY[100], 'Check count from id_taptest_table_p40000_p42000_p42800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p42000_p42900', ARRAY[100], 'Check count from id_taptest_table_p40000_p42000_p42900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p43000_p43000', ARRAY[100], 'Check count from id_taptest_table_p40000_p43000_p43000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p43000_p43100', ARRAY[100], 'Check count from id_taptest_table_p40000_p43000_p43100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p43000_p43200', ARRAY[100], 'Check count from id_taptest_table_p40000_p43000_p43200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p43000_p43300', ARRAY[100], 'Check count from id_taptest_table_p40000_p43000_p43300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p43000_p43400', ARRAY[100], 'Check count from id_taptest_table_p40000_p43000_p43400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p43000_p43500', ARRAY[100], 'Check count from id_taptest_table_p40000_p43000_p43500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p43000_p43600', ARRAY[100], 'Check count from id_taptest_table_p40000_p43000_p43600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p43000_p43700', ARRAY[100], 'Check count from id_taptest_table_p40000_p43000_p43700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p43000_p43800', ARRAY[100], 'Check count from id_taptest_table_p40000_p43000_p43800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p43000_p43900', ARRAY[100], 'Check count from id_taptest_table_p40000_p43000_p43900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p44000_p44000', ARRAY[100], 'Check count from id_taptest_table_p40000_p44000_p44000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p44000_p44100', ARRAY[100], 'Check count from id_taptest_table_p40000_p44000_p44100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p44000_p44200', ARRAY[100], 'Check count from id_taptest_table_p40000_p44000_p44200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p44000_p44300', ARRAY[100], 'Check count from id_taptest_table_p40000_p44000_p44300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p44000_p44400', ARRAY[100], 'Check count from id_taptest_table_p40000_p44000_p44400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p44000_p44500', ARRAY[100], 'Check count from id_taptest_table_p40000_p44000_p44500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p44000_p44600', ARRAY[100], 'Check count from id_taptest_table_p40000_p44000_p44600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p44000_p44700', ARRAY[100], 'Check count from id_taptest_table_p40000_p44000_p44700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p44000_p44800', ARRAY[100], 'Check count from id_taptest_table_p40000_p44000_p44800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p44000_p44900', ARRAY[100], 'Check count from id_taptest_table_p40000_p44000_p44900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p45000_p45000', ARRAY[100], 'Check count from id_taptest_table_p40000_p45000_p45000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p45000_p45100', ARRAY[100], 'Check count from id_taptest_table_p40000_p45000_p45100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p45000_p45200', ARRAY[100], 'Check count from id_taptest_table_p40000_p45000_p45200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p45000_p45300', ARRAY[100], 'Check count from id_taptest_table_p40000_p45000_p45300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p45000_p45400', ARRAY[100], 'Check count from id_taptest_table_p40000_p45000_p45400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p45000_p45500', ARRAY[100], 'Check count from id_taptest_table_p40000_p45000_p45500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p45000_p45600', ARRAY[100], 'Check count from id_taptest_table_p40000_p45000_p45600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p45000_p45700', ARRAY[100], 'Check count from id_taptest_table_p40000_p45000_p45700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p45000_p45800', ARRAY[100], 'Check count from id_taptest_table_p40000_p45000_p45800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p45000_p45900', ARRAY[100], 'Check count from id_taptest_table_p40000_p45000_p45900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p46000_p46000', ARRAY[100], 'Check count from id_taptest_table_p40000_p46000_p46000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p46000_p46100', ARRAY[100], 'Check count from id_taptest_table_p40000_p46000_p46100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p46000_p46200', ARRAY[100], 'Check count from id_taptest_table_p40000_p46000_p46200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p46000_p46300', ARRAY[100], 'Check count from id_taptest_table_p40000_p46000_p46300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p46000_p46400', ARRAY[100], 'Check count from id_taptest_table_p40000_p46000_p46400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p46000_p46500', ARRAY[100], 'Check count from id_taptest_table_p40000_p46000_p46500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p46000_p46600', ARRAY[100], 'Check count from id_taptest_table_p40000_p46000_p46600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p46000_p46700', ARRAY[100], 'Check count from id_taptest_table_p40000_p46000_p46700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p46000_p46800', ARRAY[100], 'Check count from id_taptest_table_p40000_p46000_p46800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p46000_p46900', ARRAY[100], 'Check count from id_taptest_table_p40000_p46000_p46900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p47000_p47000', ARRAY[100], 'Check count from id_taptest_table_p40000_p47000_p47000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p47000_p47100', ARRAY[100], 'Check count from id_taptest_table_p40000_p47000_p47100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p47000_p47200', ARRAY[100], 'Check count from id_taptest_table_p40000_p47000_p47200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p47000_p47300', ARRAY[100], 'Check count from id_taptest_table_p40000_p47000_p47300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p47000_p47400', ARRAY[100], 'Check count from id_taptest_table_p40000_p47000_p47400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p47000_p47500', ARRAY[100], 'Check count from id_taptest_table_p40000_p47000_p47500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p47000_p47600', ARRAY[100], 'Check count from id_taptest_table_p40000_p47000_p47600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p47000_p47700', ARRAY[100], 'Check count from id_taptest_table_p40000_p47000_p47700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p47000_p47800', ARRAY[100], 'Check count from id_taptest_table_p40000_p47000_p47800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p47000_p47900', ARRAY[100], 'Check count from id_taptest_table_p40000_p47000_p47900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p48000_p48000', ARRAY[100], 'Check count from id_taptest_table_p40000_p48000_p48000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p48000_p48100', ARRAY[100], 'Check count from id_taptest_table_p40000_p48000_p48100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p48000_p48200', ARRAY[100], 'Check count from id_taptest_table_p40000_p48000_p48200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p48000_p48300', ARRAY[100], 'Check count from id_taptest_table_p40000_p48000_p48300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p48000_p48400', ARRAY[100], 'Check count from id_taptest_table_p40000_p48000_p48400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p48000_p48500', ARRAY[100], 'Check count from id_taptest_table_p40000_p48000_p48500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p48000_p48600', ARRAY[100], 'Check count from id_taptest_table_p40000_p48000_p48600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p48000_p48700', ARRAY[100], 'Check count from id_taptest_table_p40000_p48000_p48700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p48000_p48800', ARRAY[100], 'Check count from id_taptest_table_p40000_p48000_p48800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p48000_p48900', ARRAY[100], 'Check count from id_taptest_table_p40000_p48000_p48900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p49000_p49000', ARRAY[100], 'Check count from id_taptest_table_p40000_p49000_p49000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p49000_p49100', ARRAY[100], 'Check count from id_taptest_table_p40000_p49000_p49100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p49000_p49200', ARRAY[100], 'Check count from id_taptest_table_p40000_p49000_p49200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p49000_p49300', ARRAY[100], 'Check count from id_taptest_table_p40000_p49000_p49300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p49000_p49400', ARRAY[100], 'Check count from id_taptest_table_p40000_p49000_p49400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p49000_p49500', ARRAY[100], 'Check count from id_taptest_table_p40000_p49000_p49500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p49000_p49600', ARRAY[100], 'Check count from id_taptest_table_p40000_p49000_p49600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p49000_p49700', ARRAY[100], 'Check count from id_taptest_table_p40000_p49000_p49700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p49000_p49800', ARRAY[100], 'Check count from id_taptest_table_p40000_p49000_p49800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40000_p49000_p49900', ARRAY[100], 'Check count from id_taptest_table_p40000_p49000_p49900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p50000_p50000', ARRAY[100], 'Check count from id_taptest_table_p50000_p50000_p50000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p50000_p50100', ARRAY[100], 'Check count from id_taptest_table_p50000_p50000_p50100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p50000_p50200', ARRAY[100], 'Check count from id_taptest_table_p50000_p50000_p50200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p50000_p50300', ARRAY[100], 'Check count from id_taptest_table_p50000_p50000_p50300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p50000_p50400', ARRAY[100], 'Check count from id_taptest_table_p50000_p50000_p50400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p50000_p50500', ARRAY[100], 'Check count from id_taptest_table_p50000_p50000_p50500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p50000_p50600', ARRAY[100], 'Check count from id_taptest_table_p50000_p50000_p50600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p50000_p50700', ARRAY[100], 'Check count from id_taptest_table_p50000_p50000_p50700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p50000_p50800', ARRAY[100], 'Check count from id_taptest_table_p50000_p50000_p50800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p50000_p50900', ARRAY[100], 'Check count from id_taptest_table_p50000_p50000_p50900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p51000_p51000', ARRAY[100], 'Check count from id_taptest_table_p50000_p51000_p51000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p51000_p51100', ARRAY[100], 'Check count from id_taptest_table_p50000_p51000_p51100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p51000_p51200', ARRAY[100], 'Check count from id_taptest_table_p50000_p51000_p51200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p51000_p51300', ARRAY[100], 'Check count from id_taptest_table_p50000_p51000_p51300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p51000_p51400', ARRAY[100], 'Check count from id_taptest_table_p50000_p51000_p51400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p51000_p51500', ARRAY[100], 'Check count from id_taptest_table_p50000_p51000_p51500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p51000_p51600', ARRAY[100], 'Check count from id_taptest_table_p50000_p51000_p51600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p51000_p51700', ARRAY[100], 'Check count from id_taptest_table_p50000_p51000_p51700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p51000_p51800', ARRAY[100], 'Check count from id_taptest_table_p50000_p51000_p51800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p51000_p51900', ARRAY[100], 'Check count from id_taptest_table_p50000_p51000_p51900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p52000_p52000', ARRAY[100], 'Check count from id_taptest_table_p50000_p52000_p52000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p52000_p52100', ARRAY[100], 'Check count from id_taptest_table_p50000_p52000_p52100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p52000_p52200', ARRAY[100], 'Check count from id_taptest_table_p50000_p52000_p52200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p52000_p52300', ARRAY[100], 'Check count from id_taptest_table_p50000_p52000_p52300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p52000_p52400', ARRAY[100], 'Check count from id_taptest_table_p50000_p52000_p52400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p52000_p52500', ARRAY[100], 'Check count from id_taptest_table_p50000_p52000_p52500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p52000_p52600', ARRAY[100], 'Check count from id_taptest_table_p50000_p52000_p52600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p52000_p52700', ARRAY[100], 'Check count from id_taptest_table_p50000_p52000_p52700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p52000_p52800', ARRAY[100], 'Check count from id_taptest_table_p50000_p52000_p52800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p52000_p52900', ARRAY[100], 'Check count from id_taptest_table_p50000_p52000_p52900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p53000_p53000', ARRAY[100], 'Check count from id_taptest_table_p50000_p53000_p53000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p53000_p53100', ARRAY[100], 'Check count from id_taptest_table_p50000_p53000_p53100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p53000_p53200', ARRAY[100], 'Check count from id_taptest_table_p50000_p53000_p53200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p53000_p53300', ARRAY[100], 'Check count from id_taptest_table_p50000_p53000_p53300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p53000_p53400', ARRAY[100], 'Check count from id_taptest_table_p50000_p53000_p53400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p53000_p53500', ARRAY[100], 'Check count from id_taptest_table_p50000_p53000_p53500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p53000_p53600', ARRAY[100], 'Check count from id_taptest_table_p50000_p53000_p53600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p53000_p53700', ARRAY[100], 'Check count from id_taptest_table_p50000_p53000_p53700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p53000_p53800', ARRAY[100], 'Check count from id_taptest_table_p50000_p53000_p53800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p53000_p53900', ARRAY[100], 'Check count from id_taptest_table_p50000_p53000_p53900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p54000_p54000', ARRAY[100], 'Check count from id_taptest_table_p50000_p54000_p54000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p54000_p54100', ARRAY[100], 'Check count from id_taptest_table_p50000_p54000_p54100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p54000_p54200', ARRAY[100], 'Check count from id_taptest_table_p50000_p54000_p54200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p54000_p54300', ARRAY[100], 'Check count from id_taptest_table_p50000_p54000_p54300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p54000_p54400', ARRAY[100], 'Check count from id_taptest_table_p50000_p54000_p54400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p54000_p54500', ARRAY[100], 'Check count from id_taptest_table_p50000_p54000_p54500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p54000_p54600', ARRAY[100], 'Check count from id_taptest_table_p50000_p54000_p54600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p54000_p54700', ARRAY[100], 'Check count from id_taptest_table_p50000_p54000_p54700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p54000_p54800', ARRAY[100], 'Check count from id_taptest_table_p50000_p54000_p54800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p54000_p54900', ARRAY[100], 'Check count from id_taptest_table_p50000_p54000_p54900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p55000_p55000', ARRAY[100], 'Check count from id_taptest_table_p50000_p55000_p55000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p55000_p55100', ARRAY[100], 'Check count from id_taptest_table_p50000_p55000_p55100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p55000_p55200', ARRAY[100], 'Check count from id_taptest_table_p50000_p55000_p55200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p55000_p55300', ARRAY[100], 'Check count from id_taptest_table_p50000_p55000_p55300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p55000_p55400', ARRAY[100], 'Check count from id_taptest_table_p50000_p55000_p55400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p55000_p55500', ARRAY[100], 'Check count from id_taptest_table_p50000_p55000_p55500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p55000_p55600', ARRAY[100], 'Check count from id_taptest_table_p50000_p55000_p55600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p55000_p55700', ARRAY[100], 'Check count from id_taptest_table_p50000_p55000_p55700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p55000_p55800', ARRAY[100], 'Check count from id_taptest_table_p50000_p55000_p55800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p55000_p55900', ARRAY[100], 'Check count from id_taptest_table_p50000_p55000_p55900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p56000_p56000', ARRAY[100], 'Check count from id_taptest_table_p50000_p56000_p56000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p56000_p56100', ARRAY[100], 'Check count from id_taptest_table_p50000_p56000_p56100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p56000_p56200', ARRAY[100], 'Check count from id_taptest_table_p50000_p56000_p56200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p56000_p56300', ARRAY[100], 'Check count from id_taptest_table_p50000_p56000_p56300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p56000_p56400', ARRAY[100], 'Check count from id_taptest_table_p50000_p56000_p56400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p56000_p56500', ARRAY[100], 'Check count from id_taptest_table_p50000_p56000_p56500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p56000_p56600', ARRAY[100], 'Check count from id_taptest_table_p50000_p56000_p56600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p56000_p56700', ARRAY[100], 'Check count from id_taptest_table_p50000_p56000_p56700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p56000_p56800', ARRAY[100], 'Check count from id_taptest_table_p50000_p56000_p56800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p56000_p56900', ARRAY[100], 'Check count from id_taptest_table_p50000_p56000_p56900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p57000_p57000', ARRAY[100], 'Check count from id_taptest_table_p50000_p57000_p57000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p57000_p57100', ARRAY[100], 'Check count from id_taptest_table_p50000_p57000_p57100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p57000_p57200', ARRAY[100], 'Check count from id_taptest_table_p50000_p57000_p57200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p57000_p57300', ARRAY[100], 'Check count from id_taptest_table_p50000_p57000_p57300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p57000_p57400', ARRAY[100], 'Check count from id_taptest_table_p50000_p57000_p57400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p57000_p57500', ARRAY[100], 'Check count from id_taptest_table_p50000_p57000_p57500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p57000_p57600', ARRAY[100], 'Check count from id_taptest_table_p50000_p57000_p57600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p57000_p57700', ARRAY[100], 'Check count from id_taptest_table_p50000_p57000_p57700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p57000_p57800', ARRAY[100], 'Check count from id_taptest_table_p50000_p57000_p57800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p57000_p57900', ARRAY[100], 'Check count from id_taptest_table_p50000_p57000_p57900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p58000_p58000', ARRAY[100], 'Check count from id_taptest_table_p50000_p58000_p58000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p58000_p58100', ARRAY[100], 'Check count from id_taptest_table_p50000_p58000_p58100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p58000_p58200', ARRAY[100], 'Check count from id_taptest_table_p50000_p58000_p58200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p58000_p58300', ARRAY[100], 'Check count from id_taptest_table_p50000_p58000_p58300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p58000_p58400', ARRAY[100], 'Check count from id_taptest_table_p50000_p58000_p58400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p58000_p58500', ARRAY[100], 'Check count from id_taptest_table_p50000_p58000_p58500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p58000_p58600', ARRAY[100], 'Check count from id_taptest_table_p50000_p58000_p58600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p58000_p58700', ARRAY[100], 'Check count from id_taptest_table_p50000_p58000_p58700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p58000_p58800', ARRAY[100], 'Check count from id_taptest_table_p50000_p58000_p58800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p58000_p58900', ARRAY[100], 'Check count from id_taptest_table_p50000_p58000_p58900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p59000_p59000', ARRAY[100], 'Check count from id_taptest_table_p50000_p59000_p59000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p59000_p59100', ARRAY[100], 'Check count from id_taptest_table_p50000_p59000_p59100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p59000_p59200', ARRAY[100], 'Check count from id_taptest_table_p50000_p59000_p59200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p59000_p59300', ARRAY[100], 'Check count from id_taptest_table_p50000_p59000_p59300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p59000_p59400', ARRAY[100], 'Check count from id_taptest_table_p50000_p59000_p59400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p59000_p59500', ARRAY[100], 'Check count from id_taptest_table_p50000_p59000_p59500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p59000_p59600', ARRAY[100], 'Check count from id_taptest_table_p50000_p59000_p59600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p59000_p59700', ARRAY[100], 'Check count from id_taptest_table_p50000_p59000_p59700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p59000_p59800', ARRAY[100], 'Check count from id_taptest_table_p50000_p59000_p59800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50000_p59000_p59900', ARRAY[100], 'Check count from id_taptest_table_p50000_p59000_p59900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p60000_p60000', ARRAY[100], 'Check count from id_taptest_table_p60000_p60000_p60000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p60000_p60100', ARRAY[100], 'Check count from id_taptest_table_p60000_p60000_p60100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p60000_p60200', ARRAY[100], 'Check count from id_taptest_table_p60000_p60000_p60200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p60000_p60300', ARRAY[100], 'Check count from id_taptest_table_p60000_p60000_p60300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p60000_p60400', ARRAY[100], 'Check count from id_taptest_table_p60000_p60000_p60400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p60000_p60500', ARRAY[100], 'Check count from id_taptest_table_p60000_p60000_p60500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p60000_p60600', ARRAY[100], 'Check count from id_taptest_table_p60000_p60000_p60600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p60000_p60700', ARRAY[100], 'Check count from id_taptest_table_p60000_p60000_p60700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p60000_p60800', ARRAY[100], 'Check count from id_taptest_table_p60000_p60000_p60800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p60000_p60900', ARRAY[100], 'Check count from id_taptest_table_p60000_p60000_p60900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p61000_p61000', ARRAY[100], 'Check count from id_taptest_table_p60000_p61000_p61000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p61000_p61100', ARRAY[100], 'Check count from id_taptest_table_p60000_p61000_p61100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p61000_p61200', ARRAY[100], 'Check count from id_taptest_table_p60000_p61000_p61200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p61000_p61300', ARRAY[100], 'Check count from id_taptest_table_p60000_p61000_p61300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p61000_p61400', ARRAY[100], 'Check count from id_taptest_table_p60000_p61000_p61400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p61000_p61500', ARRAY[100], 'Check count from id_taptest_table_p60000_p61000_p61500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p61000_p61600', ARRAY[100], 'Check count from id_taptest_table_p60000_p61000_p61600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p61000_p61700', ARRAY[100], 'Check count from id_taptest_table_p60000_p61000_p61700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p61000_p61800', ARRAY[100], 'Check count from id_taptest_table_p60000_p61000_p61800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p61000_p61900', ARRAY[100], 'Check count from id_taptest_table_p60000_p61000_p61900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p62000_p62000', ARRAY[100], 'Check count from id_taptest_table_p60000_p62000_p62000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p62000_p62100', ARRAY[100], 'Check count from id_taptest_table_p60000_p62000_p62100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p62000_p62200', ARRAY[100], 'Check count from id_taptest_table_p60000_p62000_p62200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p62000_p62300', ARRAY[100], 'Check count from id_taptest_table_p60000_p62000_p62300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p62000_p62400', ARRAY[100], 'Check count from id_taptest_table_p60000_p62000_p62400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p62000_p62500', ARRAY[100], 'Check count from id_taptest_table_p60000_p62000_p62500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p62000_p62600', ARRAY[100], 'Check count from id_taptest_table_p60000_p62000_p62600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p62000_p62700', ARRAY[100], 'Check count from id_taptest_table_p60000_p62000_p62700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p62000_p62800', ARRAY[100], 'Check count from id_taptest_table_p60000_p62000_p62800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p62000_p62900', ARRAY[100], 'Check count from id_taptest_table_p60000_p62000_p62900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p63000_p63000', ARRAY[100], 'Check count from id_taptest_table_p60000_p63000_p63000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p63000_p63100', ARRAY[100], 'Check count from id_taptest_table_p60000_p63000_p63100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p63000_p63200', ARRAY[100], 'Check count from id_taptest_table_p60000_p63000_p63200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p63000_p63300', ARRAY[100], 'Check count from id_taptest_table_p60000_p63000_p63300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p63000_p63400', ARRAY[100], 'Check count from id_taptest_table_p60000_p63000_p63400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p63000_p63500', ARRAY[100], 'Check count from id_taptest_table_p60000_p63000_p63500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p63000_p63600', ARRAY[100], 'Check count from id_taptest_table_p60000_p63000_p63600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p63000_p63700', ARRAY[100], 'Check count from id_taptest_table_p60000_p63000_p63700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p63000_p63800', ARRAY[100], 'Check count from id_taptest_table_p60000_p63000_p63800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p63000_p63900', ARRAY[100], 'Check count from id_taptest_table_p60000_p63000_p63900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p64000_p64000', ARRAY[100], 'Check count from id_taptest_table_p60000_p64000_p64000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p64000_p64100', ARRAY[100], 'Check count from id_taptest_table_p60000_p64000_p64100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p64000_p64200', ARRAY[100], 'Check count from id_taptest_table_p60000_p64000_p64200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p64000_p64300', ARRAY[100], 'Check count from id_taptest_table_p60000_p64000_p64300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p64000_p64400', ARRAY[100], 'Check count from id_taptest_table_p60000_p64000_p64400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p64000_p64500', ARRAY[100], 'Check count from id_taptest_table_p60000_p64000_p64500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p64000_p64600', ARRAY[100], 'Check count from id_taptest_table_p60000_p64000_p64600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p64000_p64700', ARRAY[100], 'Check count from id_taptest_table_p60000_p64000_p64700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p64000_p64800', ARRAY[100], 'Check count from id_taptest_table_p60000_p64000_p64800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p64000_p64900', ARRAY[100], 'Check count from id_taptest_table_p60000_p64000_p64900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p65000_p65000', ARRAY[100], 'Check count from id_taptest_table_p60000_p65000_p65000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p65000_p65100', ARRAY[100], 'Check count from id_taptest_table_p60000_p65000_p65100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p65000_p65200', ARRAY[100], 'Check count from id_taptest_table_p60000_p65000_p65200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p65000_p65300', ARRAY[100], 'Check count from id_taptest_table_p60000_p65000_p65300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p65000_p65400', ARRAY[100], 'Check count from id_taptest_table_p60000_p65000_p65400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p65000_p65500', ARRAY[100], 'Check count from id_taptest_table_p60000_p65000_p65500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p65000_p65600', ARRAY[100], 'Check count from id_taptest_table_p60000_p65000_p65600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p65000_p65700', ARRAY[100], 'Check count from id_taptest_table_p60000_p65000_p65700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p65000_p65800', ARRAY[100], 'Check count from id_taptest_table_p60000_p65000_p65800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p65000_p65900', ARRAY[100], 'Check count from id_taptest_table_p60000_p65000_p65900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p66000_p66000', ARRAY[100], 'Check count from id_taptest_table_p60000_p66000_p66000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p66000_p66100', ARRAY[100], 'Check count from id_taptest_table_p60000_p66000_p66100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p66000_p66200', ARRAY[100], 'Check count from id_taptest_table_p60000_p66000_p66200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p66000_p66300', ARRAY[100], 'Check count from id_taptest_table_p60000_p66000_p66300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p66000_p66400', ARRAY[100], 'Check count from id_taptest_table_p60000_p66000_p66400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p66000_p66500', ARRAY[100], 'Check count from id_taptest_table_p60000_p66000_p66500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p66000_p66600', ARRAY[100], 'Check count from id_taptest_table_p60000_p66000_p66600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p66000_p66700', ARRAY[100], 'Check count from id_taptest_table_p60000_p66000_p66700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p66000_p66800', ARRAY[100], 'Check count from id_taptest_table_p60000_p66000_p66800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p66000_p66900', ARRAY[100], 'Check count from id_taptest_table_p60000_p66000_p66900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p67000_p67000', ARRAY[100], 'Check count from id_taptest_table_p60000_p67000_p67000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p67000_p67100', ARRAY[100], 'Check count from id_taptest_table_p60000_p67000_p67100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p67000_p67200', ARRAY[100], 'Check count from id_taptest_table_p60000_p67000_p67200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p67000_p67300', ARRAY[100], 'Check count from id_taptest_table_p60000_p67000_p67300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p67000_p67400', ARRAY[100], 'Check count from id_taptest_table_p60000_p67000_p67400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p67000_p67500', ARRAY[100], 'Check count from id_taptest_table_p60000_p67000_p67500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p67000_p67600', ARRAY[100], 'Check count from id_taptest_table_p60000_p67000_p67600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p67000_p67700', ARRAY[100], 'Check count from id_taptest_table_p60000_p67000_p67700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p67000_p67800', ARRAY[100], 'Check count from id_taptest_table_p60000_p67000_p67800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p67000_p67900', ARRAY[100], 'Check count from id_taptest_table_p60000_p67000_p67900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p68000_p68000', ARRAY[100], 'Check count from id_taptest_table_p60000_p68000_p68000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p68000_p68100', ARRAY[100], 'Check count from id_taptest_table_p60000_p68000_p68100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p68000_p68200', ARRAY[100], 'Check count from id_taptest_table_p60000_p68000_p68200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p68000_p68300', ARRAY[100], 'Check count from id_taptest_table_p60000_p68000_p68300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p68000_p68400', ARRAY[100], 'Check count from id_taptest_table_p60000_p68000_p68400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p68000_p68500', ARRAY[100], 'Check count from id_taptest_table_p60000_p68000_p68500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p68000_p68600', ARRAY[100], 'Check count from id_taptest_table_p60000_p68000_p68600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p68000_p68700', ARRAY[100], 'Check count from id_taptest_table_p60000_p68000_p68700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p68000_p68800', ARRAY[100], 'Check count from id_taptest_table_p60000_p68000_p68800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p68000_p68900', ARRAY[100], 'Check count from id_taptest_table_p60000_p68000_p68900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p69000_p69000', ARRAY[100], 'Check count from id_taptest_table_p60000_p69000_p69000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p69000_p69100', ARRAY[100], 'Check count from id_taptest_table_p60000_p69000_p69100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p69000_p69200', ARRAY[100], 'Check count from id_taptest_table_p60000_p69000_p69200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p69000_p69300', ARRAY[100], 'Check count from id_taptest_table_p60000_p69000_p69300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p69000_p69400', ARRAY[100], 'Check count from id_taptest_table_p60000_p69000_p69400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p69000_p69500', ARRAY[100], 'Check count from id_taptest_table_p60000_p69000_p69500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p69000_p69600', ARRAY[100], 'Check count from id_taptest_table_p60000_p69000_p69600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p69000_p69700', ARRAY[100], 'Check count from id_taptest_table_p60000_p69000_p69700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p69000_p69800', ARRAY[100], 'Check count from id_taptest_table_p60000_p69000_p69800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60000_p69000_p69900', ARRAY[100], 'Check count from id_taptest_table_p60000_p69000_p69900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p70000_p70000', ARRAY[100], 'Check count from id_taptest_table_p70000_p70000_p70000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p70000_p70100', ARRAY[100], 'Check count from id_taptest_table_p70000_p70000_p70100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p70000_p70200', ARRAY[100], 'Check count from id_taptest_table_p70000_p70000_p70200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p70000_p70300', ARRAY[100], 'Check count from id_taptest_table_p70000_p70000_p70300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p70000_p70400', ARRAY[100], 'Check count from id_taptest_table_p70000_p70000_p70400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p70000_p70500', ARRAY[100], 'Check count from id_taptest_table_p70000_p70000_p70500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p70000_p70600', ARRAY[100], 'Check count from id_taptest_table_p70000_p70000_p70600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p70000_p70700', ARRAY[100], 'Check count from id_taptest_table_p70000_p70000_p70700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p70000_p70800', ARRAY[100], 'Check count from id_taptest_table_p70000_p70000_p70800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p70000_p70900', ARRAY[100], 'Check count from id_taptest_table_p70000_p70000_p70900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p71000_p71000', ARRAY[100], 'Check count from id_taptest_table_p70000_p71000_p71000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p71000_p71100', ARRAY[100], 'Check count from id_taptest_table_p70000_p71000_p71100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p71000_p71200', ARRAY[100], 'Check count from id_taptest_table_p70000_p71000_p71200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p71000_p71300', ARRAY[100], 'Check count from id_taptest_table_p70000_p71000_p71300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p71000_p71400', ARRAY[100], 'Check count from id_taptest_table_p70000_p71000_p71400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p71000_p71500', ARRAY[100], 'Check count from id_taptest_table_p70000_p71000_p71500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p71000_p71600', ARRAY[100], 'Check count from id_taptest_table_p70000_p71000_p71600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p71000_p71700', ARRAY[100], 'Check count from id_taptest_table_p70000_p71000_p71700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p71000_p71800', ARRAY[100], 'Check count from id_taptest_table_p70000_p71000_p71800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p71000_p71900', ARRAY[100], 'Check count from id_taptest_table_p70000_p71000_p71900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p72000_p72000', ARRAY[100], 'Check count from id_taptest_table_p70000_p72000_p72000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p72000_p72100', ARRAY[100], 'Check count from id_taptest_table_p70000_p72000_p72100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p72000_p72200', ARRAY[100], 'Check count from id_taptest_table_p70000_p72000_p72200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p72000_p72300', ARRAY[100], 'Check count from id_taptest_table_p70000_p72000_p72300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p72000_p72400', ARRAY[100], 'Check count from id_taptest_table_p70000_p72000_p72400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p72000_p72500', ARRAY[100], 'Check count from id_taptest_table_p70000_p72000_p72500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p72000_p72600', ARRAY[100], 'Check count from id_taptest_table_p70000_p72000_p72600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p72000_p72700', ARRAY[100], 'Check count from id_taptest_table_p70000_p72000_p72700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p72000_p72800', ARRAY[100], 'Check count from id_taptest_table_p70000_p72000_p72800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p72000_p72900', ARRAY[100], 'Check count from id_taptest_table_p70000_p72000_p72900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p73000_p73000', ARRAY[100], 'Check count from id_taptest_table_p70000_p73000_p73000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p73000_p73100', ARRAY[100], 'Check count from id_taptest_table_p70000_p73000_p73100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p73000_p73200', ARRAY[100], 'Check count from id_taptest_table_p70000_p73000_p73200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p73000_p73300', ARRAY[100], 'Check count from id_taptest_table_p70000_p73000_p73300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p73000_p73400', ARRAY[100], 'Check count from id_taptest_table_p70000_p73000_p73400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p73000_p73500', ARRAY[100], 'Check count from id_taptest_table_p70000_p73000_p73500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p73000_p73600', ARRAY[100], 'Check count from id_taptest_table_p70000_p73000_p73600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p73000_p73700', ARRAY[100], 'Check count from id_taptest_table_p70000_p73000_p73700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p73000_p73800', ARRAY[100], 'Check count from id_taptest_table_p70000_p73000_p73800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p73000_p73900', ARRAY[100], 'Check count from id_taptest_table_p70000_p73000_p73900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p74000_p74000', ARRAY[100], 'Check count from id_taptest_table_p70000_p74000_p74000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p74000_p74100', ARRAY[100], 'Check count from id_taptest_table_p70000_p74000_p74100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p74000_p74200', ARRAY[100], 'Check count from id_taptest_table_p70000_p74000_p74200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p74000_p74300', ARRAY[100], 'Check count from id_taptest_table_p70000_p74000_p74300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p74000_p74400', ARRAY[100], 'Check count from id_taptest_table_p70000_p74000_p74400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p74000_p74500', ARRAY[100], 'Check count from id_taptest_table_p70000_p74000_p74500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p74000_p74600', ARRAY[100], 'Check count from id_taptest_table_p70000_p74000_p74600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p74000_p74700', ARRAY[100], 'Check count from id_taptest_table_p70000_p74000_p74700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p74000_p74800', ARRAY[100], 'Check count from id_taptest_table_p70000_p74000_p74800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p74000_p74900', ARRAY[100], 'Check count from id_taptest_table_p70000_p74000_p74900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p75000_p75000', ARRAY[100], 'Check count from id_taptest_table_p70000_p75000_p75000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p75000_p75100', ARRAY[100], 'Check count from id_taptest_table_p70000_p75000_p75100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p75000_p75200', ARRAY[100], 'Check count from id_taptest_table_p70000_p75000_p75200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p75000_p75300', ARRAY[100], 'Check count from id_taptest_table_p70000_p75000_p75300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p75000_p75400', ARRAY[100], 'Check count from id_taptest_table_p70000_p75000_p75400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p75000_p75500', ARRAY[100], 'Check count from id_taptest_table_p70000_p75000_p75500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p75000_p75600', ARRAY[100], 'Check count from id_taptest_table_p70000_p75000_p75600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p75000_p75700', ARRAY[100], 'Check count from id_taptest_table_p70000_p75000_p75700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p75000_p75800', ARRAY[100], 'Check count from id_taptest_table_p70000_p75000_p75800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p75000_p75900', ARRAY[100], 'Check count from id_taptest_table_p70000_p75000_p75900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p76000_p76000', ARRAY[100], 'Check count from id_taptest_table_p70000_p76000_p76000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p76000_p76100', ARRAY[100], 'Check count from id_taptest_table_p70000_p76000_p76100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p76000_p76200', ARRAY[100], 'Check count from id_taptest_table_p70000_p76000_p76200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p76000_p76300', ARRAY[100], 'Check count from id_taptest_table_p70000_p76000_p76300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p76000_p76400', ARRAY[100], 'Check count from id_taptest_table_p70000_p76000_p76400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p76000_p76500', ARRAY[100], 'Check count from id_taptest_table_p70000_p76000_p76500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p76000_p76600', ARRAY[100], 'Check count from id_taptest_table_p70000_p76000_p76600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p76000_p76700', ARRAY[100], 'Check count from id_taptest_table_p70000_p76000_p76700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p76000_p76800', ARRAY[100], 'Check count from id_taptest_table_p70000_p76000_p76800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p76000_p76900', ARRAY[100], 'Check count from id_taptest_table_p70000_p76000_p76900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p77000_p77000', ARRAY[100], 'Check count from id_taptest_table_p70000_p77000_p77000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p77000_p77100', ARRAY[100], 'Check count from id_taptest_table_p70000_p77000_p77100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p77000_p77200', ARRAY[100], 'Check count from id_taptest_table_p70000_p77000_p77200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p77000_p77300', ARRAY[100], 'Check count from id_taptest_table_p70000_p77000_p77300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p77000_p77400', ARRAY[100], 'Check count from id_taptest_table_p70000_p77000_p77400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p77000_p77500', ARRAY[100], 'Check count from id_taptest_table_p70000_p77000_p77500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p77000_p77600', ARRAY[100], 'Check count from id_taptest_table_p70000_p77000_p77600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p77000_p77700', ARRAY[100], 'Check count from id_taptest_table_p70000_p77000_p77700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p77000_p77800', ARRAY[100], 'Check count from id_taptest_table_p70000_p77000_p77800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p77000_p77900', ARRAY[100], 'Check count from id_taptest_table_p70000_p77000_p77900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p78000_p78000', ARRAY[100], 'Check count from id_taptest_table_p70000_p78000_p78000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p78000_p78100', ARRAY[100], 'Check count from id_taptest_table_p70000_p78000_p78100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p78000_p78200', ARRAY[100], 'Check count from id_taptest_table_p70000_p78000_p78200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p78000_p78300', ARRAY[100], 'Check count from id_taptest_table_p70000_p78000_p78300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p78000_p78400', ARRAY[100], 'Check count from id_taptest_table_p70000_p78000_p78400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p78000_p78500', ARRAY[100], 'Check count from id_taptest_table_p70000_p78000_p78500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p78000_p78600', ARRAY[100], 'Check count from id_taptest_table_p70000_p78000_p78600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p78000_p78700', ARRAY[100], 'Check count from id_taptest_table_p70000_p78000_p78700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p78000_p78800', ARRAY[100], 'Check count from id_taptest_table_p70000_p78000_p78800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p78000_p78900', ARRAY[100], 'Check count from id_taptest_table_p70000_p78000_p78900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p79000_p79000', ARRAY[100], 'Check count from id_taptest_table_p70000_p79000_p79000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p79000_p79100', ARRAY[100], 'Check count from id_taptest_table_p70000_p79000_p79100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p79000_p79200', ARRAY[100], 'Check count from id_taptest_table_p70000_p79000_p79200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p79000_p79300', ARRAY[100], 'Check count from id_taptest_table_p70000_p79000_p79300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p79000_p79400', ARRAY[100], 'Check count from id_taptest_table_p70000_p79000_p79400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p79000_p79500', ARRAY[100], 'Check count from id_taptest_table_p70000_p79000_p79500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p79000_p79600', ARRAY[100], 'Check count from id_taptest_table_p70000_p79000_p79600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p79000_p79700', ARRAY[100], 'Check count from id_taptest_table_p70000_p79000_p79700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p79000_p79800', ARRAY[100], 'Check count from id_taptest_table_p70000_p79000_p79800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70000_p79000_p79900', ARRAY[100], 'Check count from id_taptest_table_p70000_p79000_p79900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p80000_p80000', ARRAY[100], 'Check count from id_taptest_table_p80000_p80000_p80000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p80000_p80100', ARRAY[100], 'Check count from id_taptest_table_p80000_p80000_p80100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p80000_p80200', ARRAY[100], 'Check count from id_taptest_table_p80000_p80000_p80200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p80000_p80300', ARRAY[100], 'Check count from id_taptest_table_p80000_p80000_p80300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p80000_p80400', ARRAY[100], 'Check count from id_taptest_table_p80000_p80000_p80400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p80000_p80500', ARRAY[100], 'Check count from id_taptest_table_p80000_p80000_p80500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p80000_p80600', ARRAY[100], 'Check count from id_taptest_table_p80000_p80000_p80600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p80000_p80700', ARRAY[100], 'Check count from id_taptest_table_p80000_p80000_p80700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p80000_p80800', ARRAY[100], 'Check count from id_taptest_table_p80000_p80000_p80800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p80000_p80900', ARRAY[100], 'Check count from id_taptest_table_p80000_p80000_p80900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p81000_p81000', ARRAY[100], 'Check count from id_taptest_table_p80000_p81000_p81000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p81000_p81100', ARRAY[100], 'Check count from id_taptest_table_p80000_p81000_p81100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p81000_p81200', ARRAY[100], 'Check count from id_taptest_table_p80000_p81000_p81200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p81000_p81300', ARRAY[100], 'Check count from id_taptest_table_p80000_p81000_p81300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p81000_p81400', ARRAY[100], 'Check count from id_taptest_table_p80000_p81000_p81400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p81000_p81500', ARRAY[100], 'Check count from id_taptest_table_p80000_p81000_p81500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p81000_p81600', ARRAY[100], 'Check count from id_taptest_table_p80000_p81000_p81600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p81000_p81700', ARRAY[100], 'Check count from id_taptest_table_p80000_p81000_p81700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p81000_p81800', ARRAY[100], 'Check count from id_taptest_table_p80000_p81000_p81800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p81000_p81900', ARRAY[100], 'Check count from id_taptest_table_p80000_p81000_p81900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p82000_p82000', ARRAY[100], 'Check count from id_taptest_table_p80000_p82000_p82000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p82000_p82100', ARRAY[100], 'Check count from id_taptest_table_p80000_p82000_p82100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p82000_p82200', ARRAY[100], 'Check count from id_taptest_table_p80000_p82000_p82200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p82000_p82300', ARRAY[100], 'Check count from id_taptest_table_p80000_p82000_p82300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p82000_p82400', ARRAY[100], 'Check count from id_taptest_table_p80000_p82000_p82400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p82000_p82500', ARRAY[100], 'Check count from id_taptest_table_p80000_p82000_p82500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p82000_p82600', ARRAY[100], 'Check count from id_taptest_table_p80000_p82000_p82600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p82000_p82700', ARRAY[100], 'Check count from id_taptest_table_p80000_p82000_p82700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p82000_p82800', ARRAY[100], 'Check count from id_taptest_table_p80000_p82000_p82800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p82000_p82900', ARRAY[100], 'Check count from id_taptest_table_p80000_p82000_p82900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p83000_p83000', ARRAY[100], 'Check count from id_taptest_table_p80000_p83000_p83000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p83000_p83100', ARRAY[100], 'Check count from id_taptest_table_p80000_p83000_p83100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p83000_p83200', ARRAY[100], 'Check count from id_taptest_table_p80000_p83000_p83200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p83000_p83300', ARRAY[100], 'Check count from id_taptest_table_p80000_p83000_p83300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p83000_p83400', ARRAY[100], 'Check count from id_taptest_table_p80000_p83000_p83400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p83000_p83500', ARRAY[100], 'Check count from id_taptest_table_p80000_p83000_p83500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p83000_p83600', ARRAY[100], 'Check count from id_taptest_table_p80000_p83000_p83600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p83000_p83700', ARRAY[100], 'Check count from id_taptest_table_p80000_p83000_p83700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p83000_p83800', ARRAY[100], 'Check count from id_taptest_table_p80000_p83000_p83800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p83000_p83900', ARRAY[100], 'Check count from id_taptest_table_p80000_p83000_p83900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p84000_p84000', ARRAY[100], 'Check count from id_taptest_table_p80000_p84000_p84000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p84000_p84100', ARRAY[100], 'Check count from id_taptest_table_p80000_p84000_p84100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p84000_p84200', ARRAY[100], 'Check count from id_taptest_table_p80000_p84000_p84200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p84000_p84300', ARRAY[100], 'Check count from id_taptest_table_p80000_p84000_p84300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p84000_p84400', ARRAY[100], 'Check count from id_taptest_table_p80000_p84000_p84400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p84000_p84500', ARRAY[100], 'Check count from id_taptest_table_p80000_p84000_p84500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p84000_p84600', ARRAY[100], 'Check count from id_taptest_table_p80000_p84000_p84600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p84000_p84700', ARRAY[100], 'Check count from id_taptest_table_p80000_p84000_p84700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p84000_p84800', ARRAY[100], 'Check count from id_taptest_table_p80000_p84000_p84800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p84000_p84900', ARRAY[100], 'Check count from id_taptest_table_p80000_p84000_p84900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p85000_p85000', ARRAY[100], 'Check count from id_taptest_table_p80000_p85000_p85000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p85000_p85100', ARRAY[100], 'Check count from id_taptest_table_p80000_p85000_p85100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p85000_p85200', ARRAY[100], 'Check count from id_taptest_table_p80000_p85000_p85200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p85000_p85300', ARRAY[100], 'Check count from id_taptest_table_p80000_p85000_p85300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p85000_p85400', ARRAY[100], 'Check count from id_taptest_table_p80000_p85000_p85400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p85000_p85500', ARRAY[100], 'Check count from id_taptest_table_p80000_p85000_p85500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p85000_p85600', ARRAY[100], 'Check count from id_taptest_table_p80000_p85000_p85600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p85000_p85700', ARRAY[100], 'Check count from id_taptest_table_p80000_p85000_p85700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p85000_p85800', ARRAY[100], 'Check count from id_taptest_table_p80000_p85000_p85800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p85000_p85900', ARRAY[100], 'Check count from id_taptest_table_p80000_p85000_p85900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p86000_p86000', ARRAY[100], 'Check count from id_taptest_table_p80000_p86000_p86000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p86000_p86100', ARRAY[100], 'Check count from id_taptest_table_p80000_p86000_p86100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p86000_p86200', ARRAY[100], 'Check count from id_taptest_table_p80000_p86000_p86200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p86000_p86300', ARRAY[100], 'Check count from id_taptest_table_p80000_p86000_p86300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p86000_p86400', ARRAY[100], 'Check count from id_taptest_table_p80000_p86000_p86400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p86000_p86500', ARRAY[100], 'Check count from id_taptest_table_p80000_p86000_p86500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p86000_p86600', ARRAY[100], 'Check count from id_taptest_table_p80000_p86000_p86600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p86000_p86700', ARRAY[100], 'Check count from id_taptest_table_p80000_p86000_p86700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p86000_p86800', ARRAY[100], 'Check count from id_taptest_table_p80000_p86000_p86800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p86000_p86900', ARRAY[100], 'Check count from id_taptest_table_p80000_p86000_p86900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p87000_p87000', ARRAY[100], 'Check count from id_taptest_table_p80000_p87000_p87000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p87000_p87100', ARRAY[100], 'Check count from id_taptest_table_p80000_p87000_p87100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p87000_p87200', ARRAY[100], 'Check count from id_taptest_table_p80000_p87000_p87200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p87000_p87300', ARRAY[100], 'Check count from id_taptest_table_p80000_p87000_p87300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p87000_p87400', ARRAY[100], 'Check count from id_taptest_table_p80000_p87000_p87400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p87000_p87500', ARRAY[100], 'Check count from id_taptest_table_p80000_p87000_p87500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p87000_p87600', ARRAY[100], 'Check count from id_taptest_table_p80000_p87000_p87600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p87000_p87700', ARRAY[100], 'Check count from id_taptest_table_p80000_p87000_p87700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p87000_p87800', ARRAY[100], 'Check count from id_taptest_table_p80000_p87000_p87800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p87000_p87900', ARRAY[100], 'Check count from id_taptest_table_p80000_p87000_p87900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p88000_p88000', ARRAY[100], 'Check count from id_taptest_table_p80000_p88000_p88000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p88000_p88100', ARRAY[100], 'Check count from id_taptest_table_p80000_p88000_p88100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p88000_p88200', ARRAY[100], 'Check count from id_taptest_table_p80000_p88000_p88200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p88000_p88300', ARRAY[100], 'Check count from id_taptest_table_p80000_p88000_p88300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p88000_p88400', ARRAY[100], 'Check count from id_taptest_table_p80000_p88000_p88400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p88000_p88500', ARRAY[100], 'Check count from id_taptest_table_p80000_p88000_p88500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p88000_p88600', ARRAY[100], 'Check count from id_taptest_table_p80000_p88000_p88600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p88000_p88700', ARRAY[100], 'Check count from id_taptest_table_p80000_p88000_p88700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p88000_p88800', ARRAY[100], 'Check count from id_taptest_table_p80000_p88000_p88800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p88000_p88900', ARRAY[100], 'Check count from id_taptest_table_p80000_p88000_p88900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p89000_p89000', ARRAY[100], 'Check count from id_taptest_table_p80000_p89000_p89000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p89000_p89100', ARRAY[100], 'Check count from id_taptest_table_p80000_p89000_p89100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p89000_p89200', ARRAY[100], 'Check count from id_taptest_table_p80000_p89000_p89200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p89000_p89300', ARRAY[100], 'Check count from id_taptest_table_p80000_p89000_p89300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p89000_p89400', ARRAY[100], 'Check count from id_taptest_table_p80000_p89000_p89400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p89000_p89500', ARRAY[100], 'Check count from id_taptest_table_p80000_p89000_p89500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p89000_p89600', ARRAY[100], 'Check count from id_taptest_table_p80000_p89000_p89600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p89000_p89700', ARRAY[100], 'Check count from id_taptest_table_p80000_p89000_p89700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p89000_p89800', ARRAY[100], 'Check count from id_taptest_table_p80000_p89000_p89800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80000_p89000_p89900', ARRAY[100], 'Check count from id_taptest_table_p80000_p89000_p89900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p90000_p90000', ARRAY[100], 'Check count from id_taptest_table_p90000_p90000_p90000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p90000_p90100', ARRAY[100], 'Check count from id_taptest_table_p90000_p90000_p90100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p90000_p90200', ARRAY[100], 'Check count from id_taptest_table_p90000_p90000_p90200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p90000_p90300', ARRAY[100], 'Check count from id_taptest_table_p90000_p90000_p90300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p90000_p90400', ARRAY[100], 'Check count from id_taptest_table_p90000_p90000_p90400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p90000_p90500', ARRAY[100], 'Check count from id_taptest_table_p90000_p90000_p90500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p90000_p90600', ARRAY[100], 'Check count from id_taptest_table_p90000_p90000_p90600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p90000_p90700', ARRAY[100], 'Check count from id_taptest_table_p90000_p90000_p90700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p90000_p90800', ARRAY[100], 'Check count from id_taptest_table_p90000_p90000_p90800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p90000_p90900', ARRAY[100], 'Check count from id_taptest_table_p90000_p90000_p90900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p91000_p91000', ARRAY[100], 'Check count from id_taptest_table_p90000_p91000_p91000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p91000_p91100', ARRAY[100], 'Check count from id_taptest_table_p90000_p91000_p91100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p91000_p91200', ARRAY[100], 'Check count from id_taptest_table_p90000_p91000_p91200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p91000_p91300', ARRAY[100], 'Check count from id_taptest_table_p90000_p91000_p91300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p91000_p91400', ARRAY[100], 'Check count from id_taptest_table_p90000_p91000_p91400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p91000_p91500', ARRAY[100], 'Check count from id_taptest_table_p90000_p91000_p91500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p91000_p91600', ARRAY[100], 'Check count from id_taptest_table_p90000_p91000_p91600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p91000_p91700', ARRAY[100], 'Check count from id_taptest_table_p90000_p91000_p91700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p91000_p91800', ARRAY[100], 'Check count from id_taptest_table_p90000_p91000_p91800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p91000_p91900', ARRAY[100], 'Check count from id_taptest_table_p90000_p91000_p91900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p92000_p92000', ARRAY[100], 'Check count from id_taptest_table_p90000_p92000_p92000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p92000_p92100', ARRAY[100], 'Check count from id_taptest_table_p90000_p92000_p92100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p92000_p92200', ARRAY[100], 'Check count from id_taptest_table_p90000_p92000_p92200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p92000_p92300', ARRAY[100], 'Check count from id_taptest_table_p90000_p92000_p92300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p92000_p92400', ARRAY[100], 'Check count from id_taptest_table_p90000_p92000_p92400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p92000_p92500', ARRAY[100], 'Check count from id_taptest_table_p90000_p92000_p92500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p92000_p92600', ARRAY[100], 'Check count from id_taptest_table_p90000_p92000_p92600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p92000_p92700', ARRAY[100], 'Check count from id_taptest_table_p90000_p92000_p92700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p92000_p92800', ARRAY[100], 'Check count from id_taptest_table_p90000_p92000_p92800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p92000_p92900', ARRAY[100], 'Check count from id_taptest_table_p90000_p92000_p92900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p93000_p93000', ARRAY[100], 'Check count from id_taptest_table_p90000_p93000_p93000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p93000_p93100', ARRAY[100], 'Check count from id_taptest_table_p90000_p93000_p93100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p93000_p93200', ARRAY[100], 'Check count from id_taptest_table_p90000_p93000_p93200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p93000_p93300', ARRAY[100], 'Check count from id_taptest_table_p90000_p93000_p93300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p93000_p93400', ARRAY[100], 'Check count from id_taptest_table_p90000_p93000_p93400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p93000_p93500', ARRAY[100], 'Check count from id_taptest_table_p90000_p93000_p93500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p93000_p93600', ARRAY[100], 'Check count from id_taptest_table_p90000_p93000_p93600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p93000_p93700', ARRAY[100], 'Check count from id_taptest_table_p90000_p93000_p93700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p93000_p93800', ARRAY[100], 'Check count from id_taptest_table_p90000_p93000_p93800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p93000_p93900', ARRAY[100], 'Check count from id_taptest_table_p90000_p93000_p93900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p94000_p94000', ARRAY[100], 'Check count from id_taptest_table_p90000_p94000_p94000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p94000_p94100', ARRAY[100], 'Check count from id_taptest_table_p90000_p94000_p94100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p94000_p94200', ARRAY[100], 'Check count from id_taptest_table_p90000_p94000_p94200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p94000_p94300', ARRAY[100], 'Check count from id_taptest_table_p90000_p94000_p94300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p94000_p94400', ARRAY[100], 'Check count from id_taptest_table_p90000_p94000_p94400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p94000_p94500', ARRAY[100], 'Check count from id_taptest_table_p90000_p94000_p94500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p94000_p94600', ARRAY[100], 'Check count from id_taptest_table_p90000_p94000_p94600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p94000_p94700', ARRAY[100], 'Check count from id_taptest_table_p90000_p94000_p94700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p94000_p94800', ARRAY[100], 'Check count from id_taptest_table_p90000_p94000_p94800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p94000_p94900', ARRAY[100], 'Check count from id_taptest_table_p90000_p94000_p94900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p95000_p95000', ARRAY[100], 'Check count from id_taptest_table_p90000_p95000_p95000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p95000_p95100', ARRAY[100], 'Check count from id_taptest_table_p90000_p95000_p95100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p95000_p95200', ARRAY[100], 'Check count from id_taptest_table_p90000_p95000_p95200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p95000_p95300', ARRAY[100], 'Check count from id_taptest_table_p90000_p95000_p95300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p95000_p95400', ARRAY[100], 'Check count from id_taptest_table_p90000_p95000_p95400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p95000_p95500', ARRAY[100], 'Check count from id_taptest_table_p90000_p95000_p95500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p95000_p95600', ARRAY[100], 'Check count from id_taptest_table_p90000_p95000_p95600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p95000_p95700', ARRAY[100], 'Check count from id_taptest_table_p90000_p95000_p95700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p95000_p95800', ARRAY[100], 'Check count from id_taptest_table_p90000_p95000_p95800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p95000_p95900', ARRAY[100], 'Check count from id_taptest_table_p90000_p95000_p95900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p96000_p96000', ARRAY[100], 'Check count from id_taptest_table_p90000_p96000_p96000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p96000_p96100', ARRAY[100], 'Check count from id_taptest_table_p90000_p96000_p96100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p96000_p96200', ARRAY[100], 'Check count from id_taptest_table_p90000_p96000_p96200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p96000_p96300', ARRAY[100], 'Check count from id_taptest_table_p90000_p96000_p96300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p96000_p96400', ARRAY[100], 'Check count from id_taptest_table_p90000_p96000_p96400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p96000_p96500', ARRAY[100], 'Check count from id_taptest_table_p90000_p96000_p96500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p96000_p96600', ARRAY[100], 'Check count from id_taptest_table_p90000_p96000_p96600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p96000_p96700', ARRAY[100], 'Check count from id_taptest_table_p90000_p96000_p96700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p96000_p96800', ARRAY[100], 'Check count from id_taptest_table_p90000_p96000_p96800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p96000_p96900', ARRAY[100], 'Check count from id_taptest_table_p90000_p96000_p96900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p97000_p97000', ARRAY[100], 'Check count from id_taptest_table_p90000_p97000_p97000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p97000_p97100', ARRAY[100], 'Check count from id_taptest_table_p90000_p97000_p97100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p97000_p97200', ARRAY[100], 'Check count from id_taptest_table_p90000_p97000_p97200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p97000_p97300', ARRAY[100], 'Check count from id_taptest_table_p90000_p97000_p97300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p97000_p97400', ARRAY[100], 'Check count from id_taptest_table_p90000_p97000_p97400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p97000_p97500', ARRAY[100], 'Check count from id_taptest_table_p90000_p97000_p97500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p97000_p97600', ARRAY[100], 'Check count from id_taptest_table_p90000_p97000_p97600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p97000_p97700', ARRAY[100], 'Check count from id_taptest_table_p90000_p97000_p97700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p97000_p97800', ARRAY[100], 'Check count from id_taptest_table_p90000_p97000_p97800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p97000_p97900', ARRAY[100], 'Check count from id_taptest_table_p90000_p97000_p97900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p98000_p98000', ARRAY[100], 'Check count from id_taptest_table_p90000_p98000_p98000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p98000_p98100', ARRAY[100], 'Check count from id_taptest_table_p90000_p98000_p98100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p98000_p98200', ARRAY[100], 'Check count from id_taptest_table_p90000_p98000_p98200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p98000_p98300', ARRAY[100], 'Check count from id_taptest_table_p90000_p98000_p98300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p98000_p98400', ARRAY[100], 'Check count from id_taptest_table_p90000_p98000_p98400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p98000_p98500', ARRAY[100], 'Check count from id_taptest_table_p90000_p98000_p98500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p98000_p98600', ARRAY[100], 'Check count from id_taptest_table_p90000_p98000_p98600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p98000_p98700', ARRAY[100], 'Check count from id_taptest_table_p90000_p98000_p98700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p98000_p98800', ARRAY[100], 'Check count from id_taptest_table_p90000_p98000_p98800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p98000_p98900', ARRAY[100], 'Check count from id_taptest_table_p90000_p98000_p98900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p99000_p99000', ARRAY[100], 'Check count from id_taptest_table_p90000_p99000_p99000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p99000_p99100', ARRAY[100], 'Check count from id_taptest_table_p90000_p99000_p99100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p99000_p99200', ARRAY[100], 'Check count from id_taptest_table_p90000_p99000_p99200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p99000_p99300', ARRAY[100], 'Check count from id_taptest_table_p90000_p99000_p99300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p99000_p99400', ARRAY[100], 'Check count from id_taptest_table_p90000_p99000_p99400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p99000_p99500', ARRAY[100], 'Check count from id_taptest_table_p90000_p99000_p99500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p99000_p99600', ARRAY[100], 'Check count from id_taptest_table_p90000_p99000_p99600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p99000_p99700', ARRAY[100], 'Check count from id_taptest_table_p90000_p99000_p99700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p99000_p99800', ARRAY[100], 'Check count from id_taptest_table_p90000_p99000_p99800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90000_p99000_p99900', ARRAY[100], 'Check count from id_taptest_table_p90000_p99000_p99900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p100000_p100000', ARRAY[1], 'Check count from id_taptest_table_p100000_p100000_p100000'); -- INSERT batch 2 INSERT INTO partman_test.id_taptest_table (col1) VALUES (generate_series(100001, 100100)); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table', 'Check that parent table partman_test.id_taptest_table is empty'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table', ARRAY[100100], 'Check count from parent table partman_test.id_taptest_table (175000)'); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p100000', 'Check that parent table partman_test.id_taptest_table_p100000 is empty'); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p100000_p100000', 'Check that parent table partman_test.id_taptest_table_p100000_p100000 is empty'); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p100000_p101000', 'Check that parent table partman_test.id_taptest_table_p100000_p101000 is empty'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p100000_p100000', ARRAY[100], 'Check count from id_taptest_table_p100000_p100000_p100000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p100000_p100100', ARRAY[1], 'Check count from id_taptest_table_p100000_p100000_p100100'); SELECT run_maintenance(); SELECT has_table('partman_test', 'id_taptest_table_p100000_p100000_p100300', 'Check id_taptest_table_p100000_p100000_p100300 exists'); SELECT hasnt_table('partman_test', 'id_taptest_table_p100000_p100000_p100400', 'Check id_taptest_table_p100000_p100000_p100400 does not exists'); UPDATE part_config SET premake = 11 WHERE parent_table LIKE 'partman_test.id_taptest_table%' AND partition_type = 'id'; SELECT run_maintenance(); INSERT INTO partman_test.id_taptest_table (col1) VALUES (generate_series(100101,107500)); -- Stuff likely went into some parent tables. It's diffcult to test a constant stream of input data to regularly run run_maintenence() against. SELECT results_eq('SELECT parent_table, count::int FROM check_parent() ORDER BY parent_table', $$VALUES ('partman_test.id_taptest_table_p100000_p101000', 900), ('partman_test.id_taptest_table_p100000_p102000', 900), ('partman_test.id_taptest_table_p100000_p103000', 900), ('partman_test.id_taptest_table_p100000_p104000', 900), ('partman_test.id_taptest_table_p100000_p105000', 900), ('partman_test.id_taptest_table_p100000_p106000', 900), ('partman_test.id_taptest_table_p100000_p107000', 401)$$, 'check_parent() run to see if stuff was put into parent tables'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p100000_p101000'', p_batch_count := 20)::int', ARRAY[900], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p100000_p101000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p100000_p102000'', p_batch_count := 20)::int', ARRAY[900], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p100000_p102000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p100000_p103000'', p_batch_count := 20)::int', ARRAY[900], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p100000_p103000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p100000_p104000'', p_batch_count := 20)::int', ARRAY[900], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p100000_p104000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p100000_p105000'', p_batch_count := 20)::int', ARRAY[900], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p100000_p105000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p100000_p106000'', p_batch_count := 20)::int', ARRAY[900], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p100000_p106000'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_p100000_p107000'', p_batch_count := 20)::int', ARRAY[401], 'Check that partitioning function returns correct count of rows moved for id_taptest_table_p100000_p107000'); SELECT is_empty('SELECT * from check_parent()', 'check_parent() should return nothing now.'); -- Check for the rest of batch 2 tables now SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p100000_p100000', ARRAY[100], 'Check count fromid_taptest_table_p100000_p100000_p100000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p100000_p100100', ARRAY[100], 'Check count fromid_taptest_table_p100000_p100000_p100100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p100000_p100200', ARRAY[100], 'Check count fromid_taptest_table_p100000_p100000_p100200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p100000_p100300', ARRAY[100], 'Check count fromid_taptest_table_p100000_p100000_p100300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p100000_p100400', ARRAY[100], 'Check count fromid_taptest_table_p100000_p100000_p100400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p100000_p100500', ARRAY[100], 'Check count fromid_taptest_table_p100000_p100000_p100500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p100000_p100600', ARRAY[100], 'Check count fromid_taptest_table_p100000_p100000_p100600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p100000_p100700', ARRAY[100], 'Check count fromid_taptest_table_p100000_p100000_p100700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p100000_p100800', ARRAY[100], 'Check count fromid_taptest_table_p100000_p100000_p100800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p100000_p100900', ARRAY[100], 'Check count fromid_taptest_table_p100000_p100000_p100900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p101000_p101000', ARRAY[100], 'Check count fromid_taptest_table_p100000_p101000_p101000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p101000_p101100', ARRAY[100], 'Check count fromid_taptest_table_p100000_p101000_p101100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p101000_p101200', ARRAY[100], 'Check count fromid_taptest_table_p100000_p101000_p101200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p101000_p101300', ARRAY[100], 'Check count fromid_taptest_table_p100000_p101000_p101300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p101000_p101400', ARRAY[100], 'Check count fromid_taptest_table_p100000_p101000_p101400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p101000_p101500', ARRAY[100], 'Check count fromid_taptest_table_p100000_p101000_p101500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p101000_p101600', ARRAY[100], 'Check count fromid_taptest_table_p100000_p101000_p101600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p101000_p101700', ARRAY[100], 'Check count fromid_taptest_table_p100000_p101000_p101700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p101000_p101800', ARRAY[100], 'Check count fromid_taptest_table_p100000_p101000_p101800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p101000_p101900', ARRAY[100], 'Check count fromid_taptest_table_p100000_p101000_p101900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p102000_p102000', ARRAY[100], 'Check count fromid_taptest_table_p100000_p102000_p102000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p102000_p102100', ARRAY[100], 'Check count fromid_taptest_table_p100000_p102000_p102100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p102000_p102200', ARRAY[100], 'Check count fromid_taptest_table_p100000_p102000_p102200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p102000_p102300', ARRAY[100], 'Check count fromid_taptest_table_p100000_p102000_p102300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p102000_p102400', ARRAY[100], 'Check count fromid_taptest_table_p100000_p102000_p102400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p102000_p102500', ARRAY[100], 'Check count fromid_taptest_table_p100000_p102000_p102500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p102000_p102600', ARRAY[100], 'Check count fromid_taptest_table_p100000_p102000_p102600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p102000_p102700', ARRAY[100], 'Check count fromid_taptest_table_p100000_p102000_p102700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p102000_p102800', ARRAY[100], 'Check count fromid_taptest_table_p100000_p102000_p102800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p102000_p102900', ARRAY[100], 'Check count fromid_taptest_table_p100000_p102000_p102900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p103000_p103000', ARRAY[100], 'Check count fromid_taptest_table_p100000_p103000_p103000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p103000_p103100', ARRAY[100], 'Check count fromid_taptest_table_p100000_p103000_p103100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p103000_p103200', ARRAY[100], 'Check count fromid_taptest_table_p100000_p103000_p103200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p103000_p103300', ARRAY[100], 'Check count fromid_taptest_table_p100000_p103000_p103300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p103000_p103400', ARRAY[100], 'Check count fromid_taptest_table_p100000_p103000_p103400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p103000_p103500', ARRAY[100], 'Check count fromid_taptest_table_p100000_p103000_p103500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p103000_p103600', ARRAY[100], 'Check count fromid_taptest_table_p100000_p103000_p103600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p103000_p103700', ARRAY[100], 'Check count fromid_taptest_table_p100000_p103000_p103700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p103000_p103800', ARRAY[100], 'Check count fromid_taptest_table_p100000_p103000_p103800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p103000_p103900', ARRAY[100], 'Check count fromid_taptest_table_p100000_p103000_p103900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p104000_p104000', ARRAY[100], 'Check count fromid_taptest_table_p100000_p104000_p104000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p104000_p104100', ARRAY[100], 'Check count fromid_taptest_table_p100000_p104000_p104100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p104000_p104200', ARRAY[100], 'Check count fromid_taptest_table_p100000_p104000_p104200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p104000_p104300', ARRAY[100], 'Check count fromid_taptest_table_p100000_p104000_p104300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p104000_p104400', ARRAY[100], 'Check count fromid_taptest_table_p100000_p104000_p104400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p104000_p104500', ARRAY[100], 'Check count fromid_taptest_table_p100000_p104000_p104500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p104000_p104600', ARRAY[100], 'Check count fromid_taptest_table_p100000_p104000_p104600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p104000_p104700', ARRAY[100], 'Check count fromid_taptest_table_p100000_p104000_p104700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p104000_p104800', ARRAY[100], 'Check count fromid_taptest_table_p100000_p104000_p104800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p104000_p104900', ARRAY[100], 'Check count fromid_taptest_table_p100000_p104000_p104900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p105000_p105000', ARRAY[100], 'Check count fromid_taptest_table_p100000_p105000_p105000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p105000_p105100', ARRAY[100], 'Check count fromid_taptest_table_p100000_p105000_p105100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p105000_p105200', ARRAY[100], 'Check count fromid_taptest_table_p100000_p105000_p105200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p105000_p105300', ARRAY[100], 'Check count fromid_taptest_table_p100000_p105000_p105300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p105000_p105400', ARRAY[100], 'Check count fromid_taptest_table_p100000_p105000_p105400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p105000_p105500', ARRAY[100], 'Check count fromid_taptest_table_p100000_p105000_p105500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p105000_p105600', ARRAY[100], 'Check count fromid_taptest_table_p100000_p105000_p105600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p105000_p105700', ARRAY[100], 'Check count fromid_taptest_table_p100000_p105000_p105700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p105000_p105800', ARRAY[100], 'Check count fromid_taptest_table_p100000_p105000_p105800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p105000_p105900', ARRAY[100], 'Check count fromid_taptest_table_p100000_p105000_p105900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p106000_p106000', ARRAY[100], 'Check count fromid_taptest_table_p100000_p106000_p106000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p106000_p106100', ARRAY[100], 'Check count fromid_taptest_table_p100000_p106000_p106100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p106000_p106200', ARRAY[100], 'Check count fromid_taptest_table_p100000_p106000_p106200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p106000_p106300', ARRAY[100], 'Check count fromid_taptest_table_p100000_p106000_p106300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p106000_p106400', ARRAY[100], 'Check count fromid_taptest_table_p100000_p106000_p106400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p106000_p106500', ARRAY[100], 'Check count fromid_taptest_table_p100000_p106000_p106500'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p106000_p106600', ARRAY[100], 'Check count fromid_taptest_table_p100000_p106000_p106600'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p106000_p106700', ARRAY[100], 'Check count fromid_taptest_table_p100000_p106000_p106700'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p106000_p106800', ARRAY[100], 'Check count fromid_taptest_table_p100000_p106000_p106800'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p106000_p106900', ARRAY[100], 'Check count fromid_taptest_table_p100000_p106000_p106900'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p107000_p107000', ARRAY[100], 'Check count fromid_taptest_table_p100000_p107000_p107000'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p107000_p107100', ARRAY[100], 'Check count fromid_taptest_table_p100000_p107000_p107100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p107000_p107200', ARRAY[100], 'Check count fromid_taptest_table_p100000_p107000_p107200'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p107000_p107300', ARRAY[100], 'Check count fromid_taptest_table_p100000_p107000_p107300'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p107000_p107400', ARRAY[100], 'Check count fromid_taptest_table_p100000_p107000_p107400'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100000_p107000_p107500', ARRAY[1], 'Check count fromid_taptest_table_p100000_p107000_p107500'); SELECT run_maintenance(); -- UNDO ALL THE THINGS!! SELECT throws_ok('SELECT undo_partition_id(''partman_test.id_taptest_table'', 20, p_keep_table := false)', 'P0001', 'Child table for this parent has child table(s) itself (partman_test.id_taptest_table_p0). Run undo partitioning on this table or remove inheritance first to ensure all data is properly moved to parent CONTEXT: SQL statement "SELECT undo_partition_id(''partman_test.id_taptest_table'', 20, p_keep_table := false)" PL/pgSQL function throws_ok(text,character,text,text) line 16 at EXECUTE statement DETAIL: HINT: ', 'Check that undoing partitions is prevented if subpartitions still exist'); -- First the sub-sub parents SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p0_p0'', 20, p_keep_table := false)::int', ARRAY[999], 'Undo partitioning for parent table partman_test.id_taptest_table_p0_p0'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p0_p1000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p0_p1000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p0_p2000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p0_p2000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p0_p3000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p0_p3000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p0_p4000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p0_p4000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p0_p5000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p0_p5000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p0_p6000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p0_p6000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p0_p7000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p0_p7000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p0_p8000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p0_p8000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p0_p9000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p0_p9000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p10000_p10000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p10000_p10000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p10000_p11000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p10000_p11000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p10000_p12000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p10000_p12000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p10000_p13000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p10000_p13000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p10000_p14000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p10000_p14000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p10000_p15000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p10000_p15000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p10000_p16000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p10000_p16000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p10000_p17000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p10000_p17000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p10000_p18000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p10000_p18000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p10000_p19000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p10000_p19000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p20000_p20000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p20000_p20000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p20000_p21000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p20000_p21000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p20000_p22000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p20000_p22000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p20000_p23000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p20000_p23000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p20000_p24000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p20000_p24000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p20000_p25000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p20000_p25000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p20000_p26000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p20000_p26000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p20000_p27000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p20000_p27000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p20000_p28000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p20000_p28000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p20000_p29000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p20000_p29000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p30000_p30000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p30000_p30000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p30000_p31000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p30000_p31000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p30000_p32000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p30000_p32000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p30000_p33000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p30000_p33000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p30000_p34000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p30000_p34000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p30000_p35000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p30000_p35000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p30000_p36000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p30000_p36000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p30000_p37000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p30000_p37000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p30000_p38000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p30000_p38000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p30000_p39000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p30000_p39000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p40000_p40000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p40000_p40000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p40000_p41000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p40000_p41000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p40000_p42000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p40000_p42000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p40000_p43000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p40000_p43000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p40000_p44000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p40000_p44000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p40000_p45000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p40000_p45000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p40000_p46000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p40000_p46000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p40000_p47000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p40000_p47000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p40000_p48000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p40000_p48000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p40000_p49000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p40000_p49000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p50000_p50000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p50000_p50000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p50000_p51000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p50000_p51000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p50000_p52000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p50000_p52000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p50000_p53000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p50000_p53000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p50000_p54000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p50000_p54000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p50000_p55000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p50000_p55000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p50000_p56000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p50000_p56000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p50000_p57000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p50000_p57000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p50000_p58000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p50000_p58000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p50000_p59000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p50000_p59000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p60000_p60000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p60000_p60000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p60000_p61000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p60000_p61000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p60000_p62000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p60000_p62000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p60000_p63000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p60000_p63000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p60000_p64000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p60000_p64000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p60000_p65000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p60000_p65000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p60000_p66000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p60000_p66000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p60000_p67000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p60000_p67000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p60000_p68000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p60000_p68000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p60000_p69000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p60000_p69000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p70000_p70000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p70000_p70000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p70000_p71000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p70000_p71000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p70000_p72000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p70000_p72000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p70000_p73000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p70000_p73000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p70000_p74000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p70000_p74000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p70000_p75000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p70000_p75000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p70000_p76000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p70000_p76000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p70000_p77000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p70000_p77000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p70000_p78000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p70000_p78000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p70000_p79000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p70000_p79000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p80000_p80000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p80000_p80000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p80000_p81000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p80000_p81000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p80000_p82000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p80000_p82000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p80000_p83000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p80000_p83000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p80000_p84000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p80000_p84000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p80000_p85000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p80000_p85000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p80000_p86000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p80000_p86000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p80000_p87000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p80000_p87000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p80000_p88000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p80000_p88000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p80000_p89000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p80000_p89000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p90000_p90000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p90000_p90000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p90000_p91000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p90000_p91000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p90000_p92000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p90000_p92000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p90000_p93000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p90000_p93000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p90000_p94000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p90000_p94000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p90000_p95000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p90000_p95000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p90000_p96000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p90000_p96000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p90000_p97000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p90000_p97000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p90000_p98000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p90000_p98000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p90000_p99000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p90000_p99000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p100000_p100000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p100000_p100000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p100000_p101000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p100000_p101000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p100000_p102000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p100000_p102000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p100000_p103000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p100000_p103000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p100000_p104000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p100000_p104000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p100000_p105000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p100000_p105000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p100000_p106000'', 20, p_keep_table := false)::int', ARRAY[1000], 'Undo partitioning for parent table partman_test.id_taptest_table_p100000_p106000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p100000_p107000'', 20, p_keep_table := false)::int', ARRAY[501], 'Undo partitioning for parent table partman_test.id_taptest_table_p100000_p107000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p100000_p108000'', 20, p_keep_table := false)::int', ARRAY[0], 'Undo partitioning for parent table partman_test.id_taptest_table_p100000_p108000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p100000_p109000'', 20, p_keep_table := false)::int', ARRAY[0], 'Undo partitioning for parent table partman_test.id_taptest_table_p100000_p109000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p110000_p110000'', 20, p_keep_table := false)::int', ARRAY[0], 'Undo partitioning for parent table partman_test.id_taptest_table_p110000_p110000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p120000_p120000'', 20, p_keep_table := false)::int', ARRAY[0], 'Undo partitioning for parent table partman_test.id_taptest_table_p120000_p120000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p130000_p130000'', 20, p_keep_table := false)::int', ARRAY[0], 'Undo partitioning for parent table partman_test.id_taptest_table_p130000_p130000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p140000_p140000'', 20, p_keep_table := false)::int', ARRAY[0], 'Undo partitioning for parent table partman_test.id_taptest_table_p140000_p140000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p150000_p150000'', 20, p_keep_table := false)::int', ARRAY[0], 'Undo partitioning for parent table partman_test.id_taptest_table_p150000_p150000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p160000_p160000'', 20, p_keep_table := false)::int', ARRAY[0], 'Undo partitioning for parent table partman_test.id_taptest_table_p160000_p160000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p170000_p170000'', 20, p_keep_table := false)::int', ARRAY[0], 'Undo partitioning for parent table partman_test.id_taptest_table_p170000_p170000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p180000_p180000'', 20, p_keep_table := false)::int', ARRAY[0], 'Undo partitioning for parent table partman_test.id_taptest_table_p180000_p180000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p190000_p190000'', 20, p_keep_table := false)::int', ARRAY[0], 'Undo partitioning for parent table partman_test.id_taptest_table_p190000_p190000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p200000_p200000'', 20, p_keep_table := false)::int', ARRAY[0], 'Undo partitioning for parent table partman_test.id_taptest_table_p200000_p200000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p210000_p210000'', 20, p_keep_table := false)::int', ARRAY[0], 'Undo partitioning for parent table partman_test.id_taptest_table_p210000_p210000'); -- Check that lowest child tables were removed (TODO need to clean this list up, it's got everything) SELECT hasnt_table('partman_test.id_taptest_table_p0_p0_p0', 'Check that table partman_test.id_taptest_table_p0_p0_p0 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p0_p100', 'Check that table partman_test.id_taptest_table_p0_p0_p100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p0_p200', 'Check that table partman_test.id_taptest_table_p0_p0_p200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p0_p300', 'Check that table partman_test.id_taptest_table_p0_p0_p300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p0_p400', 'Check that table partman_test.id_taptest_table_p0_p0_p400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p0_p500', 'Check that table partman_test.id_taptest_table_p0_p0_p500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p0_p600', 'Check that table partman_test.id_taptest_table_p0_p0_p600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p0_p700', 'Check that table partman_test.id_taptest_table_p0_p0_p700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p0_p800', 'Check that table partman_test.id_taptest_table_p0_p0_p800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p0_p900', 'Check that table partman_test.id_taptest_table_p0_p0_p900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p1000_p1000', 'Check that table partman_test.id_taptest_table_p0_p1000_p1000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p1000_p1100', 'Check that table partman_test.id_taptest_table_p0_p1000_p1100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p1000_p1200', 'Check that table partman_test.id_taptest_table_p0_p1000_p1200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p1000_p1300', 'Check that table partman_test.id_taptest_table_p0_p1000_p1300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p1000_p1400', 'Check that table partman_test.id_taptest_table_p0_p1000_p1400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p1000_p1500', 'Check that table partman_test.id_taptest_table_p0_p1000_p1500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p1000_p1600', 'Check that table partman_test.id_taptest_table_p0_p1000_p1600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p1000_p1700', 'Check that table partman_test.id_taptest_table_p0_p1000_p1700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p1000_p1800', 'Check that table partman_test.id_taptest_table_p0_p1000_p1800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p1000_p1900', 'Check that table partman_test.id_taptest_table_p0_p1000_p1900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p2000_p2000', 'Check that table partman_test.id_taptest_table_p0_p2000_p2000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p2000_p2100', 'Check that table partman_test.id_taptest_table_p0_p2000_p2100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p2000_p2200', 'Check that table partman_test.id_taptest_table_p0_p2000_p2200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p2000_p2300', 'Check that table partman_test.id_taptest_table_p0_p2000_p2300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p2000_p2400', 'Check that table partman_test.id_taptest_table_p0_p2000_p2400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p2000_p2500', 'Check that table partman_test.id_taptest_table_p0_p2000_p2500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p2000_p2600', 'Check that table partman_test.id_taptest_table_p0_p2000_p2600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p2000_p2700', 'Check that table partman_test.id_taptest_table_p0_p2000_p2700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p2000_p2800', 'Check that table partman_test.id_taptest_table_p0_p2000_p2800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p2000_p2900', 'Check that table partman_test.id_taptest_table_p0_p2000_p2900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p3000_p3000', 'Check that table partman_test.id_taptest_table_p0_p3000_p3000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p3000_p3100', 'Check that table partman_test.id_taptest_table_p0_p3000_p3100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p3000_p3200', 'Check that table partman_test.id_taptest_table_p0_p3000_p3200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p3000_p3300', 'Check that table partman_test.id_taptest_table_p0_p3000_p3300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p3000_p3400', 'Check that table partman_test.id_taptest_table_p0_p3000_p3400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p3000_p3500', 'Check that table partman_test.id_taptest_table_p0_p3000_p3500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p3000_p3600', 'Check that table partman_test.id_taptest_table_p0_p3000_p3600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p3000_p3700', 'Check that table partman_test.id_taptest_table_p0_p3000_p3700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p3000_p3800', 'Check that table partman_test.id_taptest_table_p0_p3000_p3800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p3000_p3900', 'Check that table partman_test.id_taptest_table_p0_p3000_p3900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p4000_p4000', 'Check that table partman_test.id_taptest_table_p0_p4000_p4000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p4000_p4100', 'Check that table partman_test.id_taptest_table_p0_p4000_p4100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p4000_p4200', 'Check that table partman_test.id_taptest_table_p0_p4000_p4200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p4000_p4300', 'Check that table partman_test.id_taptest_table_p0_p4000_p4300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p4000_p4400', 'Check that table partman_test.id_taptest_table_p0_p4000_p4400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p4000_p4500', 'Check that table partman_test.id_taptest_table_p0_p4000_p4500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p4000_p4600', 'Check that table partman_test.id_taptest_table_p0_p4000_p4600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p4000_p4700', 'Check that table partman_test.id_taptest_table_p0_p4000_p4700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p4000_p4800', 'Check that table partman_test.id_taptest_table_p0_p4000_p4800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p4000_p4900', 'Check that table partman_test.id_taptest_table_p0_p4000_p4900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p5000_p5000', 'Check that table partman_test.id_taptest_table_p0_p5000_p5000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p5000_p5100', 'Check that table partman_test.id_taptest_table_p0_p5000_p5100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p5000_p5200', 'Check that table partman_test.id_taptest_table_p0_p5000_p5200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p5000_p5300', 'Check that table partman_test.id_taptest_table_p0_p5000_p5300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p5000_p5400', 'Check that table partman_test.id_taptest_table_p0_p5000_p5400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p5000_p5500', 'Check that table partman_test.id_taptest_table_p0_p5000_p5500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p5000_p5600', 'Check that table partman_test.id_taptest_table_p0_p5000_p5600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p5000_p5700', 'Check that table partman_test.id_taptest_table_p0_p5000_p5700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p5000_p5800', 'Check that table partman_test.id_taptest_table_p0_p5000_p5800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p5000_p5900', 'Check that table partman_test.id_taptest_table_p0_p5000_p5900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p6000_p6000', 'Check that table partman_test.id_taptest_table_p0_p6000_p6000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p6000_p6100', 'Check that table partman_test.id_taptest_table_p0_p6000_p6100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p6000_p6200', 'Check that table partman_test.id_taptest_table_p0_p6000_p6200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p6000_p6300', 'Check that table partman_test.id_taptest_table_p0_p6000_p6300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p6000_p6400', 'Check that table partman_test.id_taptest_table_p0_p6000_p6400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p6000_p6500', 'Check that table partman_test.id_taptest_table_p0_p6000_p6500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p6000_p6600', 'Check that table partman_test.id_taptest_table_p0_p6000_p6600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p6000_p6700', 'Check that table partman_test.id_taptest_table_p0_p6000_p6700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p6000_p6800', 'Check that table partman_test.id_taptest_table_p0_p6000_p6800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p6000_p6900', 'Check that table partman_test.id_taptest_table_p0_p6000_p6900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p7000_p7000', 'Check that table partman_test.id_taptest_table_p0_p7000_p7000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p7000_p7100', 'Check that table partman_test.id_taptest_table_p0_p7000_p7100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p7000_p7200', 'Check that table partman_test.id_taptest_table_p0_p7000_p7200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p7000_p7300', 'Check that table partman_test.id_taptest_table_p0_p7000_p7300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p7000_p7400', 'Check that table partman_test.id_taptest_table_p0_p7000_p7400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p7000_p7500', 'Check that table partman_test.id_taptest_table_p0_p7000_p7500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p7000_p7600', 'Check that table partman_test.id_taptest_table_p0_p7000_p7600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p7000_p7700', 'Check that table partman_test.id_taptest_table_p0_p7000_p7700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p7000_p7800', 'Check that table partman_test.id_taptest_table_p0_p7000_p7800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p7000_p7900', 'Check that table partman_test.id_taptest_table_p0_p7000_p7900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p8000_p8000', 'Check that table partman_test.id_taptest_table_p0_p8000_p8000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p8000_p8100', 'Check that table partman_test.id_taptest_table_p0_p8000_p8100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p8000_p8200', 'Check that table partman_test.id_taptest_table_p0_p8000_p8200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p8000_p8300', 'Check that table partman_test.id_taptest_table_p0_p8000_p8300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p8000_p8400', 'Check that table partman_test.id_taptest_table_p0_p8000_p8400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p8000_p8500', 'Check that table partman_test.id_taptest_table_p0_p8000_p8500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p8000_p8600', 'Check that table partman_test.id_taptest_table_p0_p8000_p8600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p8000_p8700', 'Check that table partman_test.id_taptest_table_p0_p8000_p8700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p8000_p8800', 'Check that table partman_test.id_taptest_table_p0_p8000_p8800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p8000_p8900', 'Check that table partman_test.id_taptest_table_p0_p8000_p8900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p9000_p9000', 'Check that table partman_test.id_taptest_table_p0_p9000_p9000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p9000_p9100', 'Check that table partman_test.id_taptest_table_p0_p9000_p9100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p9000_p9200', 'Check that table partman_test.id_taptest_table_p0_p9000_p9200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p9000_p9300', 'Check that table partman_test.id_taptest_table_p0_p9000_p9300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p9000_p9400', 'Check that table partman_test.id_taptest_table_p0_p9000_p9400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p9000_p9500', 'Check that table partman_test.id_taptest_table_p0_p9000_p9500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p9000_p9600', 'Check that table partman_test.id_taptest_table_p0_p9000_p9600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p9000_p9700', 'Check that table partman_test.id_taptest_table_p0_p9000_p9700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p9000_p9800', 'Check that table partman_test.id_taptest_table_p0_p9000_p9800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p9000_p9900', 'Check that table partman_test.id_taptest_table_p0_p9000_p9900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p100000_p100000', 'Check that table partman_test.id_taptest_table_p100000_p100000_p100000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p100000_p100100', 'Check that table partman_test.id_taptest_table_p100000_p100000_p100100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p100000_p100200', 'Check that table partman_test.id_taptest_table_p100000_p100000_p100200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p100000_p100300', 'Check that table partman_test.id_taptest_table_p100000_p100000_p100300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p100000_p100400', 'Check that table partman_test.id_taptest_table_p100000_p100000_p100400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p100000_p100500', 'Check that table partman_test.id_taptest_table_p100000_p100000_p100500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p100000_p100600', 'Check that table partman_test.id_taptest_table_p100000_p100000_p100600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p100000_p100700', 'Check that table partman_test.id_taptest_table_p100000_p100000_p100700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p100000_p100800', 'Check that table partman_test.id_taptest_table_p100000_p100000_p100800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p100000_p100900', 'Check that table partman_test.id_taptest_table_p100000_p100000_p100900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p101000_p101000', 'Check that table partman_test.id_taptest_table_p100000_p101000_p101000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p101000_p101100', 'Check that table partman_test.id_taptest_table_p100000_p101000_p101100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p101000_p101200', 'Check that table partman_test.id_taptest_table_p100000_p101000_p101200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p101000_p101300', 'Check that table partman_test.id_taptest_table_p100000_p101000_p101300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p101000_p101400', 'Check that table partman_test.id_taptest_table_p100000_p101000_p101400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p101000_p101500', 'Check that table partman_test.id_taptest_table_p100000_p101000_p101500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p101000_p101600', 'Check that table partman_test.id_taptest_table_p100000_p101000_p101600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p101000_p101700', 'Check that table partman_test.id_taptest_table_p100000_p101000_p101700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p101000_p101800', 'Check that table partman_test.id_taptest_table_p100000_p101000_p101800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p101000_p101900', 'Check that table partman_test.id_taptest_table_p100000_p101000_p101900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p102000_p102000', 'Check that table partman_test.id_taptest_table_p100000_p102000_p102000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p102000_p102100', 'Check that table partman_test.id_taptest_table_p100000_p102000_p102100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p102000_p102200', 'Check that table partman_test.id_taptest_table_p100000_p102000_p102200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p102000_p102300', 'Check that table partman_test.id_taptest_table_p100000_p102000_p102300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p102000_p102400', 'Check that table partman_test.id_taptest_table_p100000_p102000_p102400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p102000_p102500', 'Check that table partman_test.id_taptest_table_p100000_p102000_p102500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p102000_p102600', 'Check that table partman_test.id_taptest_table_p100000_p102000_p102600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p102000_p102700', 'Check that table partman_test.id_taptest_table_p100000_p102000_p102700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p102000_p102800', 'Check that table partman_test.id_taptest_table_p100000_p102000_p102800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p102000_p102900', 'Check that table partman_test.id_taptest_table_p100000_p102000_p102900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p103000_p103000', 'Check that table partman_test.id_taptest_table_p100000_p103000_p103000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p103000_p103100', 'Check that table partman_test.id_taptest_table_p100000_p103000_p103100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p103000_p103200', 'Check that table partman_test.id_taptest_table_p100000_p103000_p103200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p103000_p103300', 'Check that table partman_test.id_taptest_table_p100000_p103000_p103300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p103000_p103400', 'Check that table partman_test.id_taptest_table_p100000_p103000_p103400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p103000_p103500', 'Check that table partman_test.id_taptest_table_p100000_p103000_p103500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p103000_p103600', 'Check that table partman_test.id_taptest_table_p100000_p103000_p103600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p103000_p103700', 'Check that table partman_test.id_taptest_table_p100000_p103000_p103700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p103000_p103800', 'Check that table partman_test.id_taptest_table_p100000_p103000_p103800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p103000_p103900', 'Check that table partman_test.id_taptest_table_p100000_p103000_p103900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p104000_p104000', 'Check that table partman_test.id_taptest_table_p100000_p104000_p104000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p104000_p104100', 'Check that table partman_test.id_taptest_table_p100000_p104000_p104100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p104000_p104200', 'Check that table partman_test.id_taptest_table_p100000_p104000_p104200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p104000_p104300', 'Check that table partman_test.id_taptest_table_p100000_p104000_p104300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p104000_p104400', 'Check that table partman_test.id_taptest_table_p100000_p104000_p104400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p104000_p104500', 'Check that table partman_test.id_taptest_table_p100000_p104000_p104500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p104000_p104600', 'Check that table partman_test.id_taptest_table_p100000_p104000_p104600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p104000_p104700', 'Check that table partman_test.id_taptest_table_p100000_p104000_p104700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p104000_p104800', 'Check that table partman_test.id_taptest_table_p100000_p104000_p104800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p104000_p104900', 'Check that table partman_test.id_taptest_table_p100000_p104000_p104900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p105000_p105000', 'Check that table partman_test.id_taptest_table_p100000_p105000_p105000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p105000_p105100', 'Check that table partman_test.id_taptest_table_p100000_p105000_p105100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p105000_p105200', 'Check that table partman_test.id_taptest_table_p100000_p105000_p105200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p105000_p105300', 'Check that table partman_test.id_taptest_table_p100000_p105000_p105300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p105000_p105400', 'Check that table partman_test.id_taptest_table_p100000_p105000_p105400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p105000_p105500', 'Check that table partman_test.id_taptest_table_p100000_p105000_p105500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p105000_p105600', 'Check that table partman_test.id_taptest_table_p100000_p105000_p105600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p105000_p105700', 'Check that table partman_test.id_taptest_table_p100000_p105000_p105700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p105000_p105800', 'Check that table partman_test.id_taptest_table_p100000_p105000_p105800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p105000_p105900', 'Check that table partman_test.id_taptest_table_p100000_p105000_p105900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p106000_p106000', 'Check that table partman_test.id_taptest_table_p100000_p106000_p106000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p106000_p106100', 'Check that table partman_test.id_taptest_table_p100000_p106000_p106100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p106000_p106200', 'Check that table partman_test.id_taptest_table_p100000_p106000_p106200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p106000_p106300', 'Check that table partman_test.id_taptest_table_p100000_p106000_p106300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p106000_p106400', 'Check that table partman_test.id_taptest_table_p100000_p106000_p106400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p106000_p106500', 'Check that table partman_test.id_taptest_table_p100000_p106000_p106500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p106000_p106600', 'Check that table partman_test.id_taptest_table_p100000_p106000_p106600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p106000_p106700', 'Check that table partman_test.id_taptest_table_p100000_p106000_p106700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p106000_p106800', 'Check that table partman_test.id_taptest_table_p100000_p106000_p106800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p106000_p106900', 'Check that table partman_test.id_taptest_table_p100000_p106000_p106900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p107000_p107000', 'Check that table partman_test.id_taptest_table_p100000_p107000_p107000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p107000_p107100', 'Check that table partman_test.id_taptest_table_p100000_p107000_p107100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p107000_p107200', 'Check that table partman_test.id_taptest_table_p100000_p107000_p107200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p107000_p107300', 'Check that table partman_test.id_taptest_table_p100000_p107000_p107300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p107000_p107400', 'Check that table partman_test.id_taptest_table_p100000_p107000_p107400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p107000_p107500', 'Check that table partman_test.id_taptest_table_p100000_p107000_p107500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p107000_p107600', 'Check that table partman_test.id_taptest_table_p100000_p107000_p107600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p107000_p107700', 'Check that table partman_test.id_taptest_table_p100000_p107000_p107700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p108000_p108000', 'Check that table partman_test.id_taptest_table_p100000_p108000_p108000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p109000_p109000', 'Check that table partman_test.id_taptest_table_p100000_p109000_p109000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p10000_p10000', 'Check that table partman_test.id_taptest_table_p10000_p10000_p10000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p10000_p10100', 'Check that table partman_test.id_taptest_table_p10000_p10000_p10100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p10000_p10200', 'Check that table partman_test.id_taptest_table_p10000_p10000_p10200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p10000_p10300', 'Check that table partman_test.id_taptest_table_p10000_p10000_p10300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p10000_p10400', 'Check that table partman_test.id_taptest_table_p10000_p10000_p10400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p10000_p10500', 'Check that table partman_test.id_taptest_table_p10000_p10000_p10500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p10000_p10600', 'Check that table partman_test.id_taptest_table_p10000_p10000_p10600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p10000_p10700', 'Check that table partman_test.id_taptest_table_p10000_p10000_p10700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p10000_p10800', 'Check that table partman_test.id_taptest_table_p10000_p10000_p10800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p10000_p10900', 'Check that table partman_test.id_taptest_table_p10000_p10000_p10900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p11000_p11000', 'Check that table partman_test.id_taptest_table_p10000_p11000_p11000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p11000_p11100', 'Check that table partman_test.id_taptest_table_p10000_p11000_p11100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p11000_p11200', 'Check that table partman_test.id_taptest_table_p10000_p11000_p11200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p11000_p11300', 'Check that table partman_test.id_taptest_table_p10000_p11000_p11300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p11000_p11400', 'Check that table partman_test.id_taptest_table_p10000_p11000_p11400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p11000_p11500', 'Check that table partman_test.id_taptest_table_p10000_p11000_p11500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p11000_p11600', 'Check that table partman_test.id_taptest_table_p10000_p11000_p11600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p11000_p11700', 'Check that table partman_test.id_taptest_table_p10000_p11000_p11700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p11000_p11800', 'Check that table partman_test.id_taptest_table_p10000_p11000_p11800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p11000_p11900', 'Check that table partman_test.id_taptest_table_p10000_p11000_p11900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p12000_p12000', 'Check that table partman_test.id_taptest_table_p10000_p12000_p12000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p12000_p12100', 'Check that table partman_test.id_taptest_table_p10000_p12000_p12100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p12000_p12200', 'Check that table partman_test.id_taptest_table_p10000_p12000_p12200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p12000_p12300', 'Check that table partman_test.id_taptest_table_p10000_p12000_p12300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p12000_p12400', 'Check that table partman_test.id_taptest_table_p10000_p12000_p12400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p12000_p12500', 'Check that table partman_test.id_taptest_table_p10000_p12000_p12500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p12000_p12600', 'Check that table partman_test.id_taptest_table_p10000_p12000_p12600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p12000_p12700', 'Check that table partman_test.id_taptest_table_p10000_p12000_p12700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p12000_p12800', 'Check that table partman_test.id_taptest_table_p10000_p12000_p12800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p12000_p12900', 'Check that table partman_test.id_taptest_table_p10000_p12000_p12900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p13000_p13000', 'Check that table partman_test.id_taptest_table_p10000_p13000_p13000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p13000_p13100', 'Check that table partman_test.id_taptest_table_p10000_p13000_p13100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p13000_p13200', 'Check that table partman_test.id_taptest_table_p10000_p13000_p13200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p13000_p13300', 'Check that table partman_test.id_taptest_table_p10000_p13000_p13300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p13000_p13400', 'Check that table partman_test.id_taptest_table_p10000_p13000_p13400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p13000_p13500', 'Check that table partman_test.id_taptest_table_p10000_p13000_p13500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p13000_p13600', 'Check that table partman_test.id_taptest_table_p10000_p13000_p13600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p13000_p13700', 'Check that table partman_test.id_taptest_table_p10000_p13000_p13700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p13000_p13800', 'Check that table partman_test.id_taptest_table_p10000_p13000_p13800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p13000_p13900', 'Check that table partman_test.id_taptest_table_p10000_p13000_p13900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p14000_p14000', 'Check that table partman_test.id_taptest_table_p10000_p14000_p14000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p14000_p14100', 'Check that table partman_test.id_taptest_table_p10000_p14000_p14100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p14000_p14200', 'Check that table partman_test.id_taptest_table_p10000_p14000_p14200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p14000_p14300', 'Check that table partman_test.id_taptest_table_p10000_p14000_p14300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p14000_p14400', 'Check that table partman_test.id_taptest_table_p10000_p14000_p14400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p14000_p14500', 'Check that table partman_test.id_taptest_table_p10000_p14000_p14500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p14000_p14600', 'Check that table partman_test.id_taptest_table_p10000_p14000_p14600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p14000_p14700', 'Check that table partman_test.id_taptest_table_p10000_p14000_p14700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p14000_p14800', 'Check that table partman_test.id_taptest_table_p10000_p14000_p14800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p14000_p14900', 'Check that table partman_test.id_taptest_table_p10000_p14000_p14900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p15000_p15000', 'Check that table partman_test.id_taptest_table_p10000_p15000_p15000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p15000_p15100', 'Check that table partman_test.id_taptest_table_p10000_p15000_p15100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p15000_p15200', 'Check that table partman_test.id_taptest_table_p10000_p15000_p15200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p15000_p15300', 'Check that table partman_test.id_taptest_table_p10000_p15000_p15300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p15000_p15400', 'Check that table partman_test.id_taptest_table_p10000_p15000_p15400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p15000_p15500', 'Check that table partman_test.id_taptest_table_p10000_p15000_p15500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p15000_p15600', 'Check that table partman_test.id_taptest_table_p10000_p15000_p15600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p15000_p15700', 'Check that table partman_test.id_taptest_table_p10000_p15000_p15700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p15000_p15800', 'Check that table partman_test.id_taptest_table_p10000_p15000_p15800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p15000_p15900', 'Check that table partman_test.id_taptest_table_p10000_p15000_p15900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p16000_p16000', 'Check that table partman_test.id_taptest_table_p10000_p16000_p16000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p16000_p16100', 'Check that table partman_test.id_taptest_table_p10000_p16000_p16100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p16000_p16200', 'Check that table partman_test.id_taptest_table_p10000_p16000_p16200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p16000_p16300', 'Check that table partman_test.id_taptest_table_p10000_p16000_p16300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p16000_p16400', 'Check that table partman_test.id_taptest_table_p10000_p16000_p16400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p16000_p16500', 'Check that table partman_test.id_taptest_table_p10000_p16000_p16500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p16000_p16600', 'Check that table partman_test.id_taptest_table_p10000_p16000_p16600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p16000_p16700', 'Check that table partman_test.id_taptest_table_p10000_p16000_p16700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p16000_p16800', 'Check that table partman_test.id_taptest_table_p10000_p16000_p16800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p16000_p16900', 'Check that table partman_test.id_taptest_table_p10000_p16000_p16900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p17000_p17000', 'Check that table partman_test.id_taptest_table_p10000_p17000_p17000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p17000_p17100', 'Check that table partman_test.id_taptest_table_p10000_p17000_p17100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p17000_p17200', 'Check that table partman_test.id_taptest_table_p10000_p17000_p17200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p17000_p17300', 'Check that table partman_test.id_taptest_table_p10000_p17000_p17300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p17000_p17400', 'Check that table partman_test.id_taptest_table_p10000_p17000_p17400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p17000_p17500', 'Check that table partman_test.id_taptest_table_p10000_p17000_p17500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p17000_p17600', 'Check that table partman_test.id_taptest_table_p10000_p17000_p17600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p17000_p17700', 'Check that table partman_test.id_taptest_table_p10000_p17000_p17700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p17000_p17800', 'Check that table partman_test.id_taptest_table_p10000_p17000_p17800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p17000_p17900', 'Check that table partman_test.id_taptest_table_p10000_p17000_p17900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p18000_p18000', 'Check that table partman_test.id_taptest_table_p10000_p18000_p18000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p18000_p18100', 'Check that table partman_test.id_taptest_table_p10000_p18000_p18100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p18000_p18200', 'Check that table partman_test.id_taptest_table_p10000_p18000_p18200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p18000_p18300', 'Check that table partman_test.id_taptest_table_p10000_p18000_p18300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p18000_p18400', 'Check that table partman_test.id_taptest_table_p10000_p18000_p18400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p18000_p18500', 'Check that table partman_test.id_taptest_table_p10000_p18000_p18500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p18000_p18600', 'Check that table partman_test.id_taptest_table_p10000_p18000_p18600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p18000_p18700', 'Check that table partman_test.id_taptest_table_p10000_p18000_p18700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p18000_p18800', 'Check that table partman_test.id_taptest_table_p10000_p18000_p18800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p18000_p18900', 'Check that table partman_test.id_taptest_table_p10000_p18000_p18900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p19000_p19000', 'Check that table partman_test.id_taptest_table_p10000_p19000_p19000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p19000_p19100', 'Check that table partman_test.id_taptest_table_p10000_p19000_p19100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p19000_p19200', 'Check that table partman_test.id_taptest_table_p10000_p19000_p19200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p19000_p19300', 'Check that table partman_test.id_taptest_table_p10000_p19000_p19300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p19000_p19400', 'Check that table partman_test.id_taptest_table_p10000_p19000_p19400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p19000_p19500', 'Check that table partman_test.id_taptest_table_p10000_p19000_p19500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p19000_p19600', 'Check that table partman_test.id_taptest_table_p10000_p19000_p19600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p19000_p19700', 'Check that table partman_test.id_taptest_table_p10000_p19000_p19700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p19000_p19800', 'Check that table partman_test.id_taptest_table_p10000_p19000_p19800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p19000_p19900', 'Check that table partman_test.id_taptest_table_p10000_p19000_p19900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p110000_p110000_p110000', 'Check that table partman_test.id_taptest_table_p110000_p110000_p110000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p120000_p120000_p120000', 'Check that table partman_test.id_taptest_table_p120000_p120000_p120000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p130000_p130000_p130000', 'Check that table partman_test.id_taptest_table_p130000_p130000_p130000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p140000_p140000_p140000', 'Check that table partman_test.id_taptest_table_p140000_p140000_p140000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p150000_p150000_p150000', 'Check that table partman_test.id_taptest_table_p150000_p150000_p150000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p160000_p160000_p160000', 'Check that table partman_test.id_taptest_table_p160000_p160000_p160000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p170000_p170000_p170000', 'Check that table partman_test.id_taptest_table_p170000_p170000_p170000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p180000_p180000_p180000', 'Check that table partman_test.id_taptest_table_p180000_p180000_p180000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p190000_p190000_p190000', 'Check that table partman_test.id_taptest_table_p190000_p190000_p190000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p200000_p200000_p200000', 'Check that table partman_test.id_taptest_table_p200000_p200000_p200000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p20000_p20000', 'Check that table partman_test.id_taptest_table_p20000_p20000_p20000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p20000_p20100', 'Check that table partman_test.id_taptest_table_p20000_p20000_p20100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p20000_p20200', 'Check that table partman_test.id_taptest_table_p20000_p20000_p20200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p20000_p20300', 'Check that table partman_test.id_taptest_table_p20000_p20000_p20300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p20000_p20400', 'Check that table partman_test.id_taptest_table_p20000_p20000_p20400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p20000_p20500', 'Check that table partman_test.id_taptest_table_p20000_p20000_p20500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p20000_p20600', 'Check that table partman_test.id_taptest_table_p20000_p20000_p20600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p20000_p20700', 'Check that table partman_test.id_taptest_table_p20000_p20000_p20700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p20000_p20800', 'Check that table partman_test.id_taptest_table_p20000_p20000_p20800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p20000_p20900', 'Check that table partman_test.id_taptest_table_p20000_p20000_p20900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p21000_p21000', 'Check that table partman_test.id_taptest_table_p20000_p21000_p21000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p21000_p21100', 'Check that table partman_test.id_taptest_table_p20000_p21000_p21100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p21000_p21200', 'Check that table partman_test.id_taptest_table_p20000_p21000_p21200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p21000_p21300', 'Check that table partman_test.id_taptest_table_p20000_p21000_p21300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p21000_p21400', 'Check that table partman_test.id_taptest_table_p20000_p21000_p21400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p21000_p21500', 'Check that table partman_test.id_taptest_table_p20000_p21000_p21500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p21000_p21600', 'Check that table partman_test.id_taptest_table_p20000_p21000_p21600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p21000_p21700', 'Check that table partman_test.id_taptest_table_p20000_p21000_p21700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p21000_p21800', 'Check that table partman_test.id_taptest_table_p20000_p21000_p21800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p21000_p21900', 'Check that table partman_test.id_taptest_table_p20000_p21000_p21900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p22000_p22000', 'Check that table partman_test.id_taptest_table_p20000_p22000_p22000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p22000_p22100', 'Check that table partman_test.id_taptest_table_p20000_p22000_p22100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p22000_p22200', 'Check that table partman_test.id_taptest_table_p20000_p22000_p22200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p22000_p22300', 'Check that table partman_test.id_taptest_table_p20000_p22000_p22300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p22000_p22400', 'Check that table partman_test.id_taptest_table_p20000_p22000_p22400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p22000_p22500', 'Check that table partman_test.id_taptest_table_p20000_p22000_p22500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p22000_p22600', 'Check that table partman_test.id_taptest_table_p20000_p22000_p22600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p22000_p22700', 'Check that table partman_test.id_taptest_table_p20000_p22000_p22700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p22000_p22800', 'Check that table partman_test.id_taptest_table_p20000_p22000_p22800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p22000_p22900', 'Check that table partman_test.id_taptest_table_p20000_p22000_p22900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p23000_p23000', 'Check that table partman_test.id_taptest_table_p20000_p23000_p23000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p23000_p23100', 'Check that table partman_test.id_taptest_table_p20000_p23000_p23100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p23000_p23200', 'Check that table partman_test.id_taptest_table_p20000_p23000_p23200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p23000_p23300', 'Check that table partman_test.id_taptest_table_p20000_p23000_p23300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p23000_p23400', 'Check that table partman_test.id_taptest_table_p20000_p23000_p23400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p23000_p23500', 'Check that table partman_test.id_taptest_table_p20000_p23000_p23500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p23000_p23600', 'Check that table partman_test.id_taptest_table_p20000_p23000_p23600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p23000_p23700', 'Check that table partman_test.id_taptest_table_p20000_p23000_p23700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p23000_p23800', 'Check that table partman_test.id_taptest_table_p20000_p23000_p23800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p23000_p23900', 'Check that table partman_test.id_taptest_table_p20000_p23000_p23900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p24000_p24000', 'Check that table partman_test.id_taptest_table_p20000_p24000_p24000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p24000_p24100', 'Check that table partman_test.id_taptest_table_p20000_p24000_p24100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p24000_p24200', 'Check that table partman_test.id_taptest_table_p20000_p24000_p24200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p24000_p24300', 'Check that table partman_test.id_taptest_table_p20000_p24000_p24300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p24000_p24400', 'Check that table partman_test.id_taptest_table_p20000_p24000_p24400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p24000_p24500', 'Check that table partman_test.id_taptest_table_p20000_p24000_p24500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p24000_p24600', 'Check that table partman_test.id_taptest_table_p20000_p24000_p24600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p24000_p24700', 'Check that table partman_test.id_taptest_table_p20000_p24000_p24700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p24000_p24800', 'Check that table partman_test.id_taptest_table_p20000_p24000_p24800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p24000_p24900', 'Check that table partman_test.id_taptest_table_p20000_p24000_p24900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p25000_p25000', 'Check that table partman_test.id_taptest_table_p20000_p25000_p25000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p25000_p25100', 'Check that table partman_test.id_taptest_table_p20000_p25000_p25100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p25000_p25200', 'Check that table partman_test.id_taptest_table_p20000_p25000_p25200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p25000_p25300', 'Check that table partman_test.id_taptest_table_p20000_p25000_p25300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p25000_p25400', 'Check that table partman_test.id_taptest_table_p20000_p25000_p25400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p25000_p25500', 'Check that table partman_test.id_taptest_table_p20000_p25000_p25500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p25000_p25600', 'Check that table partman_test.id_taptest_table_p20000_p25000_p25600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p25000_p25700', 'Check that table partman_test.id_taptest_table_p20000_p25000_p25700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p25000_p25800', 'Check that table partman_test.id_taptest_table_p20000_p25000_p25800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p25000_p25900', 'Check that table partman_test.id_taptest_table_p20000_p25000_p25900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p26000_p26000', 'Check that table partman_test.id_taptest_table_p20000_p26000_p26000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p26000_p26100', 'Check that table partman_test.id_taptest_table_p20000_p26000_p26100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p26000_p26200', 'Check that table partman_test.id_taptest_table_p20000_p26000_p26200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p26000_p26300', 'Check that table partman_test.id_taptest_table_p20000_p26000_p26300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p26000_p26400', 'Check that table partman_test.id_taptest_table_p20000_p26000_p26400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p26000_p26500', 'Check that table partman_test.id_taptest_table_p20000_p26000_p26500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p26000_p26600', 'Check that table partman_test.id_taptest_table_p20000_p26000_p26600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p26000_p26700', 'Check that table partman_test.id_taptest_table_p20000_p26000_p26700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p26000_p26800', 'Check that table partman_test.id_taptest_table_p20000_p26000_p26800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p26000_p26900', 'Check that table partman_test.id_taptest_table_p20000_p26000_p26900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p27000_p27000', 'Check that table partman_test.id_taptest_table_p20000_p27000_p27000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p27000_p27100', 'Check that table partman_test.id_taptest_table_p20000_p27000_p27100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p27000_p27200', 'Check that table partman_test.id_taptest_table_p20000_p27000_p27200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p27000_p27300', 'Check that table partman_test.id_taptest_table_p20000_p27000_p27300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p27000_p27400', 'Check that table partman_test.id_taptest_table_p20000_p27000_p27400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p27000_p27500', 'Check that table partman_test.id_taptest_table_p20000_p27000_p27500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p27000_p27600', 'Check that table partman_test.id_taptest_table_p20000_p27000_p27600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p27000_p27700', 'Check that table partman_test.id_taptest_table_p20000_p27000_p27700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p27000_p27800', 'Check that table partman_test.id_taptest_table_p20000_p27000_p27800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p27000_p27900', 'Check that table partman_test.id_taptest_table_p20000_p27000_p27900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p28000_p28000', 'Check that table partman_test.id_taptest_table_p20000_p28000_p28000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p28000_p28100', 'Check that table partman_test.id_taptest_table_p20000_p28000_p28100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p28000_p28200', 'Check that table partman_test.id_taptest_table_p20000_p28000_p28200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p28000_p28300', 'Check that table partman_test.id_taptest_table_p20000_p28000_p28300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p28000_p28400', 'Check that table partman_test.id_taptest_table_p20000_p28000_p28400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p28000_p28500', 'Check that table partman_test.id_taptest_table_p20000_p28000_p28500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p28000_p28600', 'Check that table partman_test.id_taptest_table_p20000_p28000_p28600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p28000_p28700', 'Check that table partman_test.id_taptest_table_p20000_p28000_p28700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p28000_p28800', 'Check that table partman_test.id_taptest_table_p20000_p28000_p28800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p28000_p28900', 'Check that table partman_test.id_taptest_table_p20000_p28000_p28900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p29000_p29000', 'Check that table partman_test.id_taptest_table_p20000_p29000_p29000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p29000_p29100', 'Check that table partman_test.id_taptest_table_p20000_p29000_p29100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p29000_p29200', 'Check that table partman_test.id_taptest_table_p20000_p29000_p29200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p29000_p29300', 'Check that table partman_test.id_taptest_table_p20000_p29000_p29300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p29000_p29400', 'Check that table partman_test.id_taptest_table_p20000_p29000_p29400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p29000_p29500', 'Check that table partman_test.id_taptest_table_p20000_p29000_p29500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p29000_p29600', 'Check that table partman_test.id_taptest_table_p20000_p29000_p29600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p29000_p29700', 'Check that table partman_test.id_taptest_table_p20000_p29000_p29700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p29000_p29800', 'Check that table partman_test.id_taptest_table_p20000_p29000_p29800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p29000_p29900', 'Check that table partman_test.id_taptest_table_p20000_p29000_p29900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p210000_p210000_p210000', 'Check that table partman_test.id_taptest_table_p210000_p210000_p210000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p30000_p30000', 'Check that table partman_test.id_taptest_table_p30000_p30000_p30000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p30000_p30100', 'Check that table partman_test.id_taptest_table_p30000_p30000_p30100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p30000_p30200', 'Check that table partman_test.id_taptest_table_p30000_p30000_p30200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p30000_p30300', 'Check that table partman_test.id_taptest_table_p30000_p30000_p30300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p30000_p30400', 'Check that table partman_test.id_taptest_table_p30000_p30000_p30400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p30000_p30500', 'Check that table partman_test.id_taptest_table_p30000_p30000_p30500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p30000_p30600', 'Check that table partman_test.id_taptest_table_p30000_p30000_p30600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p30000_p30700', 'Check that table partman_test.id_taptest_table_p30000_p30000_p30700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p30000_p30800', 'Check that table partman_test.id_taptest_table_p30000_p30000_p30800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p30000_p30900', 'Check that table partman_test.id_taptest_table_p30000_p30000_p30900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p31000_p31000', 'Check that table partman_test.id_taptest_table_p30000_p31000_p31000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p31000_p31100', 'Check that table partman_test.id_taptest_table_p30000_p31000_p31100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p31000_p31200', 'Check that table partman_test.id_taptest_table_p30000_p31000_p31200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p31000_p31300', 'Check that table partman_test.id_taptest_table_p30000_p31000_p31300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p31000_p31400', 'Check that table partman_test.id_taptest_table_p30000_p31000_p31400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p31000_p31500', 'Check that table partman_test.id_taptest_table_p30000_p31000_p31500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p31000_p31600', 'Check that table partman_test.id_taptest_table_p30000_p31000_p31600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p31000_p31700', 'Check that table partman_test.id_taptest_table_p30000_p31000_p31700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p31000_p31800', 'Check that table partman_test.id_taptest_table_p30000_p31000_p31800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p31000_p31900', 'Check that table partman_test.id_taptest_table_p30000_p31000_p31900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p32000_p32000', 'Check that table partman_test.id_taptest_table_p30000_p32000_p32000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p32000_p32100', 'Check that table partman_test.id_taptest_table_p30000_p32000_p32100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p32000_p32200', 'Check that table partman_test.id_taptest_table_p30000_p32000_p32200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p32000_p32300', 'Check that table partman_test.id_taptest_table_p30000_p32000_p32300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p32000_p32400', 'Check that table partman_test.id_taptest_table_p30000_p32000_p32400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p32000_p32500', 'Check that table partman_test.id_taptest_table_p30000_p32000_p32500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p32000_p32600', 'Check that table partman_test.id_taptest_table_p30000_p32000_p32600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p32000_p32700', 'Check that table partman_test.id_taptest_table_p30000_p32000_p32700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p32000_p32800', 'Check that table partman_test.id_taptest_table_p30000_p32000_p32800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p32000_p32900', 'Check that table partman_test.id_taptest_table_p30000_p32000_p32900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p33000_p33000', 'Check that table partman_test.id_taptest_table_p30000_p33000_p33000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p33000_p33100', 'Check that table partman_test.id_taptest_table_p30000_p33000_p33100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p33000_p33200', 'Check that table partman_test.id_taptest_table_p30000_p33000_p33200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p33000_p33300', 'Check that table partman_test.id_taptest_table_p30000_p33000_p33300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p33000_p33400', 'Check that table partman_test.id_taptest_table_p30000_p33000_p33400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p33000_p33500', 'Check that table partman_test.id_taptest_table_p30000_p33000_p33500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p33000_p33600', 'Check that table partman_test.id_taptest_table_p30000_p33000_p33600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p33000_p33700', 'Check that table partman_test.id_taptest_table_p30000_p33000_p33700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p33000_p33800', 'Check that table partman_test.id_taptest_table_p30000_p33000_p33800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p33000_p33900', 'Check that table partman_test.id_taptest_table_p30000_p33000_p33900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p34000_p34000', 'Check that table partman_test.id_taptest_table_p30000_p34000_p34000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p34000_p34100', 'Check that table partman_test.id_taptest_table_p30000_p34000_p34100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p34000_p34200', 'Check that table partman_test.id_taptest_table_p30000_p34000_p34200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p34000_p34300', 'Check that table partman_test.id_taptest_table_p30000_p34000_p34300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p34000_p34400', 'Check that table partman_test.id_taptest_table_p30000_p34000_p34400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p34000_p34500', 'Check that table partman_test.id_taptest_table_p30000_p34000_p34500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p34000_p34600', 'Check that table partman_test.id_taptest_table_p30000_p34000_p34600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p34000_p34700', 'Check that table partman_test.id_taptest_table_p30000_p34000_p34700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p34000_p34800', 'Check that table partman_test.id_taptest_table_p30000_p34000_p34800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p34000_p34900', 'Check that table partman_test.id_taptest_table_p30000_p34000_p34900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p35000_p35000', 'Check that table partman_test.id_taptest_table_p30000_p35000_p35000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p35000_p35100', 'Check that table partman_test.id_taptest_table_p30000_p35000_p35100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p35000_p35200', 'Check that table partman_test.id_taptest_table_p30000_p35000_p35200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p35000_p35300', 'Check that table partman_test.id_taptest_table_p30000_p35000_p35300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p35000_p35400', 'Check that table partman_test.id_taptest_table_p30000_p35000_p35400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p35000_p35500', 'Check that table partman_test.id_taptest_table_p30000_p35000_p35500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p35000_p35600', 'Check that table partman_test.id_taptest_table_p30000_p35000_p35600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p35000_p35700', 'Check that table partman_test.id_taptest_table_p30000_p35000_p35700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p35000_p35800', 'Check that table partman_test.id_taptest_table_p30000_p35000_p35800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p35000_p35900', 'Check that table partman_test.id_taptest_table_p30000_p35000_p35900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p36000_p36000', 'Check that table partman_test.id_taptest_table_p30000_p36000_p36000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p36000_p36100', 'Check that table partman_test.id_taptest_table_p30000_p36000_p36100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p36000_p36200', 'Check that table partman_test.id_taptest_table_p30000_p36000_p36200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p36000_p36300', 'Check that table partman_test.id_taptest_table_p30000_p36000_p36300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p36000_p36400', 'Check that table partman_test.id_taptest_table_p30000_p36000_p36400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p36000_p36500', 'Check that table partman_test.id_taptest_table_p30000_p36000_p36500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p36000_p36600', 'Check that table partman_test.id_taptest_table_p30000_p36000_p36600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p36000_p36700', 'Check that table partman_test.id_taptest_table_p30000_p36000_p36700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p36000_p36800', 'Check that table partman_test.id_taptest_table_p30000_p36000_p36800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p36000_p36900', 'Check that table partman_test.id_taptest_table_p30000_p36000_p36900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p37000_p37000', 'Check that table partman_test.id_taptest_table_p30000_p37000_p37000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p37000_p37100', 'Check that table partman_test.id_taptest_table_p30000_p37000_p37100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p37000_p37200', 'Check that table partman_test.id_taptest_table_p30000_p37000_p37200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p37000_p37300', 'Check that table partman_test.id_taptest_table_p30000_p37000_p37300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p37000_p37400', 'Check that table partman_test.id_taptest_table_p30000_p37000_p37400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p37000_p37500', 'Check that table partman_test.id_taptest_table_p30000_p37000_p37500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p37000_p37600', 'Check that table partman_test.id_taptest_table_p30000_p37000_p37600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p37000_p37700', 'Check that table partman_test.id_taptest_table_p30000_p37000_p37700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p37000_p37800', 'Check that table partman_test.id_taptest_table_p30000_p37000_p37800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p37000_p37900', 'Check that table partman_test.id_taptest_table_p30000_p37000_p37900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p38000_p38000', 'Check that table partman_test.id_taptest_table_p30000_p38000_p38000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p38000_p38100', 'Check that table partman_test.id_taptest_table_p30000_p38000_p38100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p38000_p38200', 'Check that table partman_test.id_taptest_table_p30000_p38000_p38200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p38000_p38300', 'Check that table partman_test.id_taptest_table_p30000_p38000_p38300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p38000_p38400', 'Check that table partman_test.id_taptest_table_p30000_p38000_p38400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p38000_p38500', 'Check that table partman_test.id_taptest_table_p30000_p38000_p38500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p38000_p38600', 'Check that table partman_test.id_taptest_table_p30000_p38000_p38600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p38000_p38700', 'Check that table partman_test.id_taptest_table_p30000_p38000_p38700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p38000_p38800', 'Check that table partman_test.id_taptest_table_p30000_p38000_p38800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p38000_p38900', 'Check that table partman_test.id_taptest_table_p30000_p38000_p38900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p39000_p39000', 'Check that table partman_test.id_taptest_table_p30000_p39000_p39000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p39000_p39100', 'Check that table partman_test.id_taptest_table_p30000_p39000_p39100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p39000_p39200', 'Check that table partman_test.id_taptest_table_p30000_p39000_p39200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p39000_p39300', 'Check that table partman_test.id_taptest_table_p30000_p39000_p39300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p39000_p39400', 'Check that table partman_test.id_taptest_table_p30000_p39000_p39400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p39000_p39500', 'Check that table partman_test.id_taptest_table_p30000_p39000_p39500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p39000_p39600', 'Check that table partman_test.id_taptest_table_p30000_p39000_p39600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p39000_p39700', 'Check that table partman_test.id_taptest_table_p30000_p39000_p39700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p39000_p39800', 'Check that table partman_test.id_taptest_table_p30000_p39000_p39800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p39000_p39900', 'Check that table partman_test.id_taptest_table_p30000_p39000_p39900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p40000_p40000', 'Check that table partman_test.id_taptest_table_p40000_p40000_p40000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p40000_p40100', 'Check that table partman_test.id_taptest_table_p40000_p40000_p40100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p40000_p40200', 'Check that table partman_test.id_taptest_table_p40000_p40000_p40200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p40000_p40300', 'Check that table partman_test.id_taptest_table_p40000_p40000_p40300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p40000_p40400', 'Check that table partman_test.id_taptest_table_p40000_p40000_p40400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p40000_p40500', 'Check that table partman_test.id_taptest_table_p40000_p40000_p40500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p40000_p40600', 'Check that table partman_test.id_taptest_table_p40000_p40000_p40600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p40000_p40700', 'Check that table partman_test.id_taptest_table_p40000_p40000_p40700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p40000_p40800', 'Check that table partman_test.id_taptest_table_p40000_p40000_p40800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p40000_p40900', 'Check that table partman_test.id_taptest_table_p40000_p40000_p40900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p41000_p41000', 'Check that table partman_test.id_taptest_table_p40000_p41000_p41000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p41000_p41100', 'Check that table partman_test.id_taptest_table_p40000_p41000_p41100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p41000_p41200', 'Check that table partman_test.id_taptest_table_p40000_p41000_p41200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p41000_p41300', 'Check that table partman_test.id_taptest_table_p40000_p41000_p41300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p41000_p41400', 'Check that table partman_test.id_taptest_table_p40000_p41000_p41400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p41000_p41500', 'Check that table partman_test.id_taptest_table_p40000_p41000_p41500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p41000_p41600', 'Check that table partman_test.id_taptest_table_p40000_p41000_p41600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p41000_p41700', 'Check that table partman_test.id_taptest_table_p40000_p41000_p41700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p41000_p41800', 'Check that table partman_test.id_taptest_table_p40000_p41000_p41800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p41000_p41900', 'Check that table partman_test.id_taptest_table_p40000_p41000_p41900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p42000_p42000', 'Check that table partman_test.id_taptest_table_p40000_p42000_p42000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p42000_p42100', 'Check that table partman_test.id_taptest_table_p40000_p42000_p42100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p42000_p42200', 'Check that table partman_test.id_taptest_table_p40000_p42000_p42200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p42000_p42300', 'Check that table partman_test.id_taptest_table_p40000_p42000_p42300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p42000_p42400', 'Check that table partman_test.id_taptest_table_p40000_p42000_p42400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p42000_p42500', 'Check that table partman_test.id_taptest_table_p40000_p42000_p42500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p42000_p42600', 'Check that table partman_test.id_taptest_table_p40000_p42000_p42600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p42000_p42700', 'Check that table partman_test.id_taptest_table_p40000_p42000_p42700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p42000_p42800', 'Check that table partman_test.id_taptest_table_p40000_p42000_p42800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p42000_p42900', 'Check that table partman_test.id_taptest_table_p40000_p42000_p42900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p43000_p43000', 'Check that table partman_test.id_taptest_table_p40000_p43000_p43000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p43000_p43100', 'Check that table partman_test.id_taptest_table_p40000_p43000_p43100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p43000_p43200', 'Check that table partman_test.id_taptest_table_p40000_p43000_p43200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p43000_p43300', 'Check that table partman_test.id_taptest_table_p40000_p43000_p43300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p43000_p43400', 'Check that table partman_test.id_taptest_table_p40000_p43000_p43400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p43000_p43500', 'Check that table partman_test.id_taptest_table_p40000_p43000_p43500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p43000_p43600', 'Check that table partman_test.id_taptest_table_p40000_p43000_p43600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p43000_p43700', 'Check that table partman_test.id_taptest_table_p40000_p43000_p43700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p43000_p43800', 'Check that table partman_test.id_taptest_table_p40000_p43000_p43800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p43000_p43900', 'Check that table partman_test.id_taptest_table_p40000_p43000_p43900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p44000_p44000', 'Check that table partman_test.id_taptest_table_p40000_p44000_p44000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p44000_p44100', 'Check that table partman_test.id_taptest_table_p40000_p44000_p44100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p44000_p44200', 'Check that table partman_test.id_taptest_table_p40000_p44000_p44200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p44000_p44300', 'Check that table partman_test.id_taptest_table_p40000_p44000_p44300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p44000_p44400', 'Check that table partman_test.id_taptest_table_p40000_p44000_p44400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p44000_p44500', 'Check that table partman_test.id_taptest_table_p40000_p44000_p44500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p44000_p44600', 'Check that table partman_test.id_taptest_table_p40000_p44000_p44600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p44000_p44700', 'Check that table partman_test.id_taptest_table_p40000_p44000_p44700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p44000_p44800', 'Check that table partman_test.id_taptest_table_p40000_p44000_p44800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p44000_p44900', 'Check that table partman_test.id_taptest_table_p40000_p44000_p44900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p45000_p45000', 'Check that table partman_test.id_taptest_table_p40000_p45000_p45000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p45000_p45100', 'Check that table partman_test.id_taptest_table_p40000_p45000_p45100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p45000_p45200', 'Check that table partman_test.id_taptest_table_p40000_p45000_p45200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p45000_p45300', 'Check that table partman_test.id_taptest_table_p40000_p45000_p45300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p45000_p45400', 'Check that table partman_test.id_taptest_table_p40000_p45000_p45400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p45000_p45500', 'Check that table partman_test.id_taptest_table_p40000_p45000_p45500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p45000_p45600', 'Check that table partman_test.id_taptest_table_p40000_p45000_p45600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p45000_p45700', 'Check that table partman_test.id_taptest_table_p40000_p45000_p45700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p45000_p45800', 'Check that table partman_test.id_taptest_table_p40000_p45000_p45800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p45000_p45900', 'Check that table partman_test.id_taptest_table_p40000_p45000_p45900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p46000_p46000', 'Check that table partman_test.id_taptest_table_p40000_p46000_p46000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p46000_p46100', 'Check that table partman_test.id_taptest_table_p40000_p46000_p46100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p46000_p46200', 'Check that table partman_test.id_taptest_table_p40000_p46000_p46200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p46000_p46300', 'Check that table partman_test.id_taptest_table_p40000_p46000_p46300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p46000_p46400', 'Check that table partman_test.id_taptest_table_p40000_p46000_p46400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p46000_p46500', 'Check that table partman_test.id_taptest_table_p40000_p46000_p46500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p46000_p46600', 'Check that table partman_test.id_taptest_table_p40000_p46000_p46600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p46000_p46700', 'Check that table partman_test.id_taptest_table_p40000_p46000_p46700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p46000_p46800', 'Check that table partman_test.id_taptest_table_p40000_p46000_p46800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p46000_p46900', 'Check that table partman_test.id_taptest_table_p40000_p46000_p46900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p47000_p47000', 'Check that table partman_test.id_taptest_table_p40000_p47000_p47000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p47000_p47100', 'Check that table partman_test.id_taptest_table_p40000_p47000_p47100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p47000_p47200', 'Check that table partman_test.id_taptest_table_p40000_p47000_p47200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p47000_p47300', 'Check that table partman_test.id_taptest_table_p40000_p47000_p47300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p47000_p47400', 'Check that table partman_test.id_taptest_table_p40000_p47000_p47400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p47000_p47500', 'Check that table partman_test.id_taptest_table_p40000_p47000_p47500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p47000_p47600', 'Check that table partman_test.id_taptest_table_p40000_p47000_p47600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p47000_p47700', 'Check that table partman_test.id_taptest_table_p40000_p47000_p47700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p47000_p47800', 'Check that table partman_test.id_taptest_table_p40000_p47000_p47800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p47000_p47900', 'Check that table partman_test.id_taptest_table_p40000_p47000_p47900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p48000_p48000', 'Check that table partman_test.id_taptest_table_p40000_p48000_p48000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p48000_p48100', 'Check that table partman_test.id_taptest_table_p40000_p48000_p48100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p48000_p48200', 'Check that table partman_test.id_taptest_table_p40000_p48000_p48200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p48000_p48300', 'Check that table partman_test.id_taptest_table_p40000_p48000_p48300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p48000_p48400', 'Check that table partman_test.id_taptest_table_p40000_p48000_p48400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p48000_p48500', 'Check that table partman_test.id_taptest_table_p40000_p48000_p48500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p48000_p48600', 'Check that table partman_test.id_taptest_table_p40000_p48000_p48600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p48000_p48700', 'Check that table partman_test.id_taptest_table_p40000_p48000_p48700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p48000_p48800', 'Check that table partman_test.id_taptest_table_p40000_p48000_p48800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p48000_p48900', 'Check that table partman_test.id_taptest_table_p40000_p48000_p48900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p49000_p49000', 'Check that table partman_test.id_taptest_table_p40000_p49000_p49000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p49000_p49100', 'Check that table partman_test.id_taptest_table_p40000_p49000_p49100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p49000_p49200', 'Check that table partman_test.id_taptest_table_p40000_p49000_p49200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p49000_p49300', 'Check that table partman_test.id_taptest_table_p40000_p49000_p49300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p49000_p49400', 'Check that table partman_test.id_taptest_table_p40000_p49000_p49400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p49000_p49500', 'Check that table partman_test.id_taptest_table_p40000_p49000_p49500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p49000_p49600', 'Check that table partman_test.id_taptest_table_p40000_p49000_p49600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p49000_p49700', 'Check that table partman_test.id_taptest_table_p40000_p49000_p49700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p49000_p49800', 'Check that table partman_test.id_taptest_table_p40000_p49000_p49800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p49000_p49900', 'Check that table partman_test.id_taptest_table_p40000_p49000_p49900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p50000_p50000', 'Check that table partman_test.id_taptest_table_p50000_p50000_p50000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p50000_p50100', 'Check that table partman_test.id_taptest_table_p50000_p50000_p50100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p50000_p50200', 'Check that table partman_test.id_taptest_table_p50000_p50000_p50200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p50000_p50300', 'Check that table partman_test.id_taptest_table_p50000_p50000_p50300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p50000_p50400', 'Check that table partman_test.id_taptest_table_p50000_p50000_p50400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p50000_p50500', 'Check that table partman_test.id_taptest_table_p50000_p50000_p50500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p50000_p50600', 'Check that table partman_test.id_taptest_table_p50000_p50000_p50600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p50000_p50700', 'Check that table partman_test.id_taptest_table_p50000_p50000_p50700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p50000_p50800', 'Check that table partman_test.id_taptest_table_p50000_p50000_p50800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p50000_p50900', 'Check that table partman_test.id_taptest_table_p50000_p50000_p50900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p51000_p51000', 'Check that table partman_test.id_taptest_table_p50000_p51000_p51000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p51000_p51100', 'Check that table partman_test.id_taptest_table_p50000_p51000_p51100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p51000_p51200', 'Check that table partman_test.id_taptest_table_p50000_p51000_p51200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p51000_p51300', 'Check that table partman_test.id_taptest_table_p50000_p51000_p51300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p51000_p51400', 'Check that table partman_test.id_taptest_table_p50000_p51000_p51400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p51000_p51500', 'Check that table partman_test.id_taptest_table_p50000_p51000_p51500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p51000_p51600', 'Check that table partman_test.id_taptest_table_p50000_p51000_p51600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p51000_p51700', 'Check that table partman_test.id_taptest_table_p50000_p51000_p51700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p51000_p51800', 'Check that table partman_test.id_taptest_table_p50000_p51000_p51800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p51000_p51900', 'Check that table partman_test.id_taptest_table_p50000_p51000_p51900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p52000_p52000', 'Check that table partman_test.id_taptest_table_p50000_p52000_p52000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p52000_p52100', 'Check that table partman_test.id_taptest_table_p50000_p52000_p52100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p52000_p52200', 'Check that table partman_test.id_taptest_table_p50000_p52000_p52200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p52000_p52300', 'Check that table partman_test.id_taptest_table_p50000_p52000_p52300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p52000_p52400', 'Check that table partman_test.id_taptest_table_p50000_p52000_p52400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p52000_p52500', 'Check that table partman_test.id_taptest_table_p50000_p52000_p52500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p52000_p52600', 'Check that table partman_test.id_taptest_table_p50000_p52000_p52600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p52000_p52700', 'Check that table partman_test.id_taptest_table_p50000_p52000_p52700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p52000_p52800', 'Check that table partman_test.id_taptest_table_p50000_p52000_p52800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p52000_p52900', 'Check that table partman_test.id_taptest_table_p50000_p52000_p52900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p53000_p53000', 'Check that table partman_test.id_taptest_table_p50000_p53000_p53000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p53000_p53100', 'Check that table partman_test.id_taptest_table_p50000_p53000_p53100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p53000_p53200', 'Check that table partman_test.id_taptest_table_p50000_p53000_p53200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p53000_p53300', 'Check that table partman_test.id_taptest_table_p50000_p53000_p53300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p53000_p53400', 'Check that table partman_test.id_taptest_table_p50000_p53000_p53400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p53000_p53500', 'Check that table partman_test.id_taptest_table_p50000_p53000_p53500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p53000_p53600', 'Check that table partman_test.id_taptest_table_p50000_p53000_p53600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p53000_p53700', 'Check that table partman_test.id_taptest_table_p50000_p53000_p53700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p53000_p53800', 'Check that table partman_test.id_taptest_table_p50000_p53000_p53800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p53000_p53900', 'Check that table partman_test.id_taptest_table_p50000_p53000_p53900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p54000_p54000', 'Check that table partman_test.id_taptest_table_p50000_p54000_p54000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p54000_p54100', 'Check that table partman_test.id_taptest_table_p50000_p54000_p54100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p54000_p54200', 'Check that table partman_test.id_taptest_table_p50000_p54000_p54200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p54000_p54300', 'Check that table partman_test.id_taptest_table_p50000_p54000_p54300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p54000_p54400', 'Check that table partman_test.id_taptest_table_p50000_p54000_p54400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p54000_p54500', 'Check that table partman_test.id_taptest_table_p50000_p54000_p54500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p54000_p54600', 'Check that table partman_test.id_taptest_table_p50000_p54000_p54600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p54000_p54700', 'Check that table partman_test.id_taptest_table_p50000_p54000_p54700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p54000_p54800', 'Check that table partman_test.id_taptest_table_p50000_p54000_p54800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p54000_p54900', 'Check that table partman_test.id_taptest_table_p50000_p54000_p54900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p55000_p55000', 'Check that table partman_test.id_taptest_table_p50000_p55000_p55000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p55000_p55100', 'Check that table partman_test.id_taptest_table_p50000_p55000_p55100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p55000_p55200', 'Check that table partman_test.id_taptest_table_p50000_p55000_p55200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p55000_p55300', 'Check that table partman_test.id_taptest_table_p50000_p55000_p55300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p55000_p55400', 'Check that table partman_test.id_taptest_table_p50000_p55000_p55400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p55000_p55500', 'Check that table partman_test.id_taptest_table_p50000_p55000_p55500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p55000_p55600', 'Check that table partman_test.id_taptest_table_p50000_p55000_p55600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p55000_p55700', 'Check that table partman_test.id_taptest_table_p50000_p55000_p55700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p55000_p55800', 'Check that table partman_test.id_taptest_table_p50000_p55000_p55800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p55000_p55900', 'Check that table partman_test.id_taptest_table_p50000_p55000_p55900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p56000_p56000', 'Check that table partman_test.id_taptest_table_p50000_p56000_p56000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p56000_p56100', 'Check that table partman_test.id_taptest_table_p50000_p56000_p56100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p56000_p56200', 'Check that table partman_test.id_taptest_table_p50000_p56000_p56200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p56000_p56300', 'Check that table partman_test.id_taptest_table_p50000_p56000_p56300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p56000_p56400', 'Check that table partman_test.id_taptest_table_p50000_p56000_p56400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p56000_p56500', 'Check that table partman_test.id_taptest_table_p50000_p56000_p56500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p56000_p56600', 'Check that table partman_test.id_taptest_table_p50000_p56000_p56600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p56000_p56700', 'Check that table partman_test.id_taptest_table_p50000_p56000_p56700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p56000_p56800', 'Check that table partman_test.id_taptest_table_p50000_p56000_p56800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p56000_p56900', 'Check that table partman_test.id_taptest_table_p50000_p56000_p56900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p57000_p57000', 'Check that table partman_test.id_taptest_table_p50000_p57000_p57000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p57000_p57100', 'Check that table partman_test.id_taptest_table_p50000_p57000_p57100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p57000_p57200', 'Check that table partman_test.id_taptest_table_p50000_p57000_p57200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p57000_p57300', 'Check that table partman_test.id_taptest_table_p50000_p57000_p57300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p57000_p57400', 'Check that table partman_test.id_taptest_table_p50000_p57000_p57400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p57000_p57500', 'Check that table partman_test.id_taptest_table_p50000_p57000_p57500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p57000_p57600', 'Check that table partman_test.id_taptest_table_p50000_p57000_p57600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p57000_p57700', 'Check that table partman_test.id_taptest_table_p50000_p57000_p57700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p57000_p57800', 'Check that table partman_test.id_taptest_table_p50000_p57000_p57800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p57000_p57900', 'Check that table partman_test.id_taptest_table_p50000_p57000_p57900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p58000_p58000', 'Check that table partman_test.id_taptest_table_p50000_p58000_p58000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p58000_p58100', 'Check that table partman_test.id_taptest_table_p50000_p58000_p58100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p58000_p58200', 'Check that table partman_test.id_taptest_table_p50000_p58000_p58200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p58000_p58300', 'Check that table partman_test.id_taptest_table_p50000_p58000_p58300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p58000_p58400', 'Check that table partman_test.id_taptest_table_p50000_p58000_p58400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p58000_p58500', 'Check that table partman_test.id_taptest_table_p50000_p58000_p58500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p58000_p58600', 'Check that table partman_test.id_taptest_table_p50000_p58000_p58600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p58000_p58700', 'Check that table partman_test.id_taptest_table_p50000_p58000_p58700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p58000_p58800', 'Check that table partman_test.id_taptest_table_p50000_p58000_p58800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p58000_p58900', 'Check that table partman_test.id_taptest_table_p50000_p58000_p58900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p59000_p59000', 'Check that table partman_test.id_taptest_table_p50000_p59000_p59000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p59000_p59100', 'Check that table partman_test.id_taptest_table_p50000_p59000_p59100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p59000_p59200', 'Check that table partman_test.id_taptest_table_p50000_p59000_p59200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p59000_p59300', 'Check that table partman_test.id_taptest_table_p50000_p59000_p59300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p59000_p59400', 'Check that table partman_test.id_taptest_table_p50000_p59000_p59400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p59000_p59500', 'Check that table partman_test.id_taptest_table_p50000_p59000_p59500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p59000_p59600', 'Check that table partman_test.id_taptest_table_p50000_p59000_p59600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p59000_p59700', 'Check that table partman_test.id_taptest_table_p50000_p59000_p59700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p59000_p59800', 'Check that table partman_test.id_taptest_table_p50000_p59000_p59800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p59000_p59900', 'Check that table partman_test.id_taptest_table_p50000_p59000_p59900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p60000_p60000', 'Check that table partman_test.id_taptest_table_p60000_p60000_p60000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p60000_p60100', 'Check that table partman_test.id_taptest_table_p60000_p60000_p60100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p60000_p60200', 'Check that table partman_test.id_taptest_table_p60000_p60000_p60200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p60000_p60300', 'Check that table partman_test.id_taptest_table_p60000_p60000_p60300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p60000_p60400', 'Check that table partman_test.id_taptest_table_p60000_p60000_p60400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p60000_p60500', 'Check that table partman_test.id_taptest_table_p60000_p60000_p60500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p60000_p60600', 'Check that table partman_test.id_taptest_table_p60000_p60000_p60600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p60000_p60700', 'Check that table partman_test.id_taptest_table_p60000_p60000_p60700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p60000_p60800', 'Check that table partman_test.id_taptest_table_p60000_p60000_p60800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p60000_p60900', 'Check that table partman_test.id_taptest_table_p60000_p60000_p60900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p61000_p61000', 'Check that table partman_test.id_taptest_table_p60000_p61000_p61000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p61000_p61100', 'Check that table partman_test.id_taptest_table_p60000_p61000_p61100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p61000_p61200', 'Check that table partman_test.id_taptest_table_p60000_p61000_p61200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p61000_p61300', 'Check that table partman_test.id_taptest_table_p60000_p61000_p61300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p61000_p61400', 'Check that table partman_test.id_taptest_table_p60000_p61000_p61400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p61000_p61500', 'Check that table partman_test.id_taptest_table_p60000_p61000_p61500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p61000_p61600', 'Check that table partman_test.id_taptest_table_p60000_p61000_p61600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p61000_p61700', 'Check that table partman_test.id_taptest_table_p60000_p61000_p61700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p61000_p61800', 'Check that table partman_test.id_taptest_table_p60000_p61000_p61800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p61000_p61900', 'Check that table partman_test.id_taptest_table_p60000_p61000_p61900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p62000_p62000', 'Check that table partman_test.id_taptest_table_p60000_p62000_p62000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p62000_p62100', 'Check that table partman_test.id_taptest_table_p60000_p62000_p62100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p62000_p62200', 'Check that table partman_test.id_taptest_table_p60000_p62000_p62200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p62000_p62300', 'Check that table partman_test.id_taptest_table_p60000_p62000_p62300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p62000_p62400', 'Check that table partman_test.id_taptest_table_p60000_p62000_p62400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p62000_p62500', 'Check that table partman_test.id_taptest_table_p60000_p62000_p62500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p62000_p62600', 'Check that table partman_test.id_taptest_table_p60000_p62000_p62600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p62000_p62700', 'Check that table partman_test.id_taptest_table_p60000_p62000_p62700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p62000_p62800', 'Check that table partman_test.id_taptest_table_p60000_p62000_p62800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p62000_p62900', 'Check that table partman_test.id_taptest_table_p60000_p62000_p62900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p63000_p63000', 'Check that table partman_test.id_taptest_table_p60000_p63000_p63000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p63000_p63100', 'Check that table partman_test.id_taptest_table_p60000_p63000_p63100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p63000_p63200', 'Check that table partman_test.id_taptest_table_p60000_p63000_p63200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p63000_p63300', 'Check that table partman_test.id_taptest_table_p60000_p63000_p63300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p63000_p63400', 'Check that table partman_test.id_taptest_table_p60000_p63000_p63400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p63000_p63500', 'Check that table partman_test.id_taptest_table_p60000_p63000_p63500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p63000_p63600', 'Check that table partman_test.id_taptest_table_p60000_p63000_p63600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p63000_p63700', 'Check that table partman_test.id_taptest_table_p60000_p63000_p63700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p63000_p63800', 'Check that table partman_test.id_taptest_table_p60000_p63000_p63800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p63000_p63900', 'Check that table partman_test.id_taptest_table_p60000_p63000_p63900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p64000_p64000', 'Check that table partman_test.id_taptest_table_p60000_p64000_p64000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p64000_p64100', 'Check that table partman_test.id_taptest_table_p60000_p64000_p64100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p64000_p64200', 'Check that table partman_test.id_taptest_table_p60000_p64000_p64200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p64000_p64300', 'Check that table partman_test.id_taptest_table_p60000_p64000_p64300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p64000_p64400', 'Check that table partman_test.id_taptest_table_p60000_p64000_p64400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p64000_p64500', 'Check that table partman_test.id_taptest_table_p60000_p64000_p64500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p64000_p64600', 'Check that table partman_test.id_taptest_table_p60000_p64000_p64600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p64000_p64700', 'Check that table partman_test.id_taptest_table_p60000_p64000_p64700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p64000_p64800', 'Check that table partman_test.id_taptest_table_p60000_p64000_p64800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p64000_p64900', 'Check that table partman_test.id_taptest_table_p60000_p64000_p64900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p65000_p65000', 'Check that table partman_test.id_taptest_table_p60000_p65000_p65000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p65000_p65100', 'Check that table partman_test.id_taptest_table_p60000_p65000_p65100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p65000_p65200', 'Check that table partman_test.id_taptest_table_p60000_p65000_p65200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p65000_p65300', 'Check that table partman_test.id_taptest_table_p60000_p65000_p65300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p65000_p65400', 'Check that table partman_test.id_taptest_table_p60000_p65000_p65400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p65000_p65500', 'Check that table partman_test.id_taptest_table_p60000_p65000_p65500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p65000_p65600', 'Check that table partman_test.id_taptest_table_p60000_p65000_p65600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p65000_p65700', 'Check that table partman_test.id_taptest_table_p60000_p65000_p65700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p65000_p65800', 'Check that table partman_test.id_taptest_table_p60000_p65000_p65800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p65000_p65900', 'Check that table partman_test.id_taptest_table_p60000_p65000_p65900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p66000_p66000', 'Check that table partman_test.id_taptest_table_p60000_p66000_p66000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p66000_p66100', 'Check that table partman_test.id_taptest_table_p60000_p66000_p66100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p66000_p66200', 'Check that table partman_test.id_taptest_table_p60000_p66000_p66200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p66000_p66300', 'Check that table partman_test.id_taptest_table_p60000_p66000_p66300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p66000_p66400', 'Check that table partman_test.id_taptest_table_p60000_p66000_p66400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p66000_p66500', 'Check that table partman_test.id_taptest_table_p60000_p66000_p66500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p66000_p66600', 'Check that table partman_test.id_taptest_table_p60000_p66000_p66600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p66000_p66700', 'Check that table partman_test.id_taptest_table_p60000_p66000_p66700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p66000_p66800', 'Check that table partman_test.id_taptest_table_p60000_p66000_p66800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p66000_p66900', 'Check that table partman_test.id_taptest_table_p60000_p66000_p66900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p67000_p67000', 'Check that table partman_test.id_taptest_table_p60000_p67000_p67000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p67000_p67100', 'Check that table partman_test.id_taptest_table_p60000_p67000_p67100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p67000_p67200', 'Check that table partman_test.id_taptest_table_p60000_p67000_p67200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p67000_p67300', 'Check that table partman_test.id_taptest_table_p60000_p67000_p67300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p67000_p67400', 'Check that table partman_test.id_taptest_table_p60000_p67000_p67400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p67000_p67500', 'Check that table partman_test.id_taptest_table_p60000_p67000_p67500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p67000_p67600', 'Check that table partman_test.id_taptest_table_p60000_p67000_p67600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p67000_p67700', 'Check that table partman_test.id_taptest_table_p60000_p67000_p67700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p67000_p67800', 'Check that table partman_test.id_taptest_table_p60000_p67000_p67800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p67000_p67900', 'Check that table partman_test.id_taptest_table_p60000_p67000_p67900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p68000_p68000', 'Check that table partman_test.id_taptest_table_p60000_p68000_p68000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p68000_p68100', 'Check that table partman_test.id_taptest_table_p60000_p68000_p68100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p68000_p68200', 'Check that table partman_test.id_taptest_table_p60000_p68000_p68200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p68000_p68300', 'Check that table partman_test.id_taptest_table_p60000_p68000_p68300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p68000_p68400', 'Check that table partman_test.id_taptest_table_p60000_p68000_p68400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p68000_p68500', 'Check that table partman_test.id_taptest_table_p60000_p68000_p68500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p68000_p68600', 'Check that table partman_test.id_taptest_table_p60000_p68000_p68600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p68000_p68700', 'Check that table partman_test.id_taptest_table_p60000_p68000_p68700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p68000_p68800', 'Check that table partman_test.id_taptest_table_p60000_p68000_p68800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p68000_p68900', 'Check that table partman_test.id_taptest_table_p60000_p68000_p68900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p69000_p69000', 'Check that table partman_test.id_taptest_table_p60000_p69000_p69000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p69000_p69100', 'Check that table partman_test.id_taptest_table_p60000_p69000_p69100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p69000_p69200', 'Check that table partman_test.id_taptest_table_p60000_p69000_p69200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p69000_p69300', 'Check that table partman_test.id_taptest_table_p60000_p69000_p69300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p69000_p69400', 'Check that table partman_test.id_taptest_table_p60000_p69000_p69400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p69000_p69500', 'Check that table partman_test.id_taptest_table_p60000_p69000_p69500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p69000_p69600', 'Check that table partman_test.id_taptest_table_p60000_p69000_p69600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p69000_p69700', 'Check that table partman_test.id_taptest_table_p60000_p69000_p69700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p69000_p69800', 'Check that table partman_test.id_taptest_table_p60000_p69000_p69800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p69000_p69900', 'Check that table partman_test.id_taptest_table_p60000_p69000_p69900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p70000_p70000', 'Check that table partman_test.id_taptest_table_p70000_p70000_p70000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p70000_p70100', 'Check that table partman_test.id_taptest_table_p70000_p70000_p70100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p70000_p70200', 'Check that table partman_test.id_taptest_table_p70000_p70000_p70200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p70000_p70300', 'Check that table partman_test.id_taptest_table_p70000_p70000_p70300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p70000_p70400', 'Check that table partman_test.id_taptest_table_p70000_p70000_p70400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p70000_p70500', 'Check that table partman_test.id_taptest_table_p70000_p70000_p70500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p70000_p70600', 'Check that table partman_test.id_taptest_table_p70000_p70000_p70600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p70000_p70700', 'Check that table partman_test.id_taptest_table_p70000_p70000_p70700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p70000_p70800', 'Check that table partman_test.id_taptest_table_p70000_p70000_p70800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p70000_p70900', 'Check that table partman_test.id_taptest_table_p70000_p70000_p70900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p71000_p71000', 'Check that table partman_test.id_taptest_table_p70000_p71000_p71000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p71000_p71100', 'Check that table partman_test.id_taptest_table_p70000_p71000_p71100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p71000_p71200', 'Check that table partman_test.id_taptest_table_p70000_p71000_p71200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p71000_p71300', 'Check that table partman_test.id_taptest_table_p70000_p71000_p71300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p71000_p71400', 'Check that table partman_test.id_taptest_table_p70000_p71000_p71400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p71000_p71500', 'Check that table partman_test.id_taptest_table_p70000_p71000_p71500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p71000_p71600', 'Check that table partman_test.id_taptest_table_p70000_p71000_p71600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p71000_p71700', 'Check that table partman_test.id_taptest_table_p70000_p71000_p71700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p71000_p71800', 'Check that table partman_test.id_taptest_table_p70000_p71000_p71800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p71000_p71900', 'Check that table partman_test.id_taptest_table_p70000_p71000_p71900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p72000_p72000', 'Check that table partman_test.id_taptest_table_p70000_p72000_p72000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p72000_p72100', 'Check that table partman_test.id_taptest_table_p70000_p72000_p72100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p72000_p72200', 'Check that table partman_test.id_taptest_table_p70000_p72000_p72200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p72000_p72300', 'Check that table partman_test.id_taptest_table_p70000_p72000_p72300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p72000_p72400', 'Check that table partman_test.id_taptest_table_p70000_p72000_p72400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p72000_p72500', 'Check that table partman_test.id_taptest_table_p70000_p72000_p72500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p72000_p72600', 'Check that table partman_test.id_taptest_table_p70000_p72000_p72600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p72000_p72700', 'Check that table partman_test.id_taptest_table_p70000_p72000_p72700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p72000_p72800', 'Check that table partman_test.id_taptest_table_p70000_p72000_p72800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p72000_p72900', 'Check that table partman_test.id_taptest_table_p70000_p72000_p72900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p73000_p73000', 'Check that table partman_test.id_taptest_table_p70000_p73000_p73000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p73000_p73100', 'Check that table partman_test.id_taptest_table_p70000_p73000_p73100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p73000_p73200', 'Check that table partman_test.id_taptest_table_p70000_p73000_p73200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p73000_p73300', 'Check that table partman_test.id_taptest_table_p70000_p73000_p73300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p73000_p73400', 'Check that table partman_test.id_taptest_table_p70000_p73000_p73400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p73000_p73500', 'Check that table partman_test.id_taptest_table_p70000_p73000_p73500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p73000_p73600', 'Check that table partman_test.id_taptest_table_p70000_p73000_p73600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p73000_p73700', 'Check that table partman_test.id_taptest_table_p70000_p73000_p73700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p73000_p73800', 'Check that table partman_test.id_taptest_table_p70000_p73000_p73800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p73000_p73900', 'Check that table partman_test.id_taptest_table_p70000_p73000_p73900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p74000_p74000', 'Check that table partman_test.id_taptest_table_p70000_p74000_p74000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p74000_p74100', 'Check that table partman_test.id_taptest_table_p70000_p74000_p74100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p74000_p74200', 'Check that table partman_test.id_taptest_table_p70000_p74000_p74200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p74000_p74300', 'Check that table partman_test.id_taptest_table_p70000_p74000_p74300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p74000_p74400', 'Check that table partman_test.id_taptest_table_p70000_p74000_p74400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p74000_p74500', 'Check that table partman_test.id_taptest_table_p70000_p74000_p74500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p74000_p74600', 'Check that table partman_test.id_taptest_table_p70000_p74000_p74600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p74000_p74700', 'Check that table partman_test.id_taptest_table_p70000_p74000_p74700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p74000_p74800', 'Check that table partman_test.id_taptest_table_p70000_p74000_p74800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p74000_p74900', 'Check that table partman_test.id_taptest_table_p70000_p74000_p74900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p75000_p75000', 'Check that table partman_test.id_taptest_table_p70000_p75000_p75000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p75000_p75100', 'Check that table partman_test.id_taptest_table_p70000_p75000_p75100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p75000_p75200', 'Check that table partman_test.id_taptest_table_p70000_p75000_p75200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p75000_p75300', 'Check that table partman_test.id_taptest_table_p70000_p75000_p75300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p75000_p75400', 'Check that table partman_test.id_taptest_table_p70000_p75000_p75400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p75000_p75500', 'Check that table partman_test.id_taptest_table_p70000_p75000_p75500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p75000_p75600', 'Check that table partman_test.id_taptest_table_p70000_p75000_p75600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p75000_p75700', 'Check that table partman_test.id_taptest_table_p70000_p75000_p75700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p75000_p75800', 'Check that table partman_test.id_taptest_table_p70000_p75000_p75800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p75000_p75900', 'Check that table partman_test.id_taptest_table_p70000_p75000_p75900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p76000_p76000', 'Check that table partman_test.id_taptest_table_p70000_p76000_p76000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p76000_p76100', 'Check that table partman_test.id_taptest_table_p70000_p76000_p76100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p76000_p76200', 'Check that table partman_test.id_taptest_table_p70000_p76000_p76200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p76000_p76300', 'Check that table partman_test.id_taptest_table_p70000_p76000_p76300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p76000_p76400', 'Check that table partman_test.id_taptest_table_p70000_p76000_p76400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p76000_p76500', 'Check that table partman_test.id_taptest_table_p70000_p76000_p76500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p76000_p76600', 'Check that table partman_test.id_taptest_table_p70000_p76000_p76600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p76000_p76700', 'Check that table partman_test.id_taptest_table_p70000_p76000_p76700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p76000_p76800', 'Check that table partman_test.id_taptest_table_p70000_p76000_p76800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p76000_p76900', 'Check that table partman_test.id_taptest_table_p70000_p76000_p76900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p77000_p77000', 'Check that table partman_test.id_taptest_table_p70000_p77000_p77000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p77000_p77100', 'Check that table partman_test.id_taptest_table_p70000_p77000_p77100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p77000_p77200', 'Check that table partman_test.id_taptest_table_p70000_p77000_p77200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p77000_p77300', 'Check that table partman_test.id_taptest_table_p70000_p77000_p77300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p77000_p77400', 'Check that table partman_test.id_taptest_table_p70000_p77000_p77400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p77000_p77500', 'Check that table partman_test.id_taptest_table_p70000_p77000_p77500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p77000_p77600', 'Check that table partman_test.id_taptest_table_p70000_p77000_p77600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p77000_p77700', 'Check that table partman_test.id_taptest_table_p70000_p77000_p77700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p77000_p77800', 'Check that table partman_test.id_taptest_table_p70000_p77000_p77800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p77000_p77900', 'Check that table partman_test.id_taptest_table_p70000_p77000_p77900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p78000_p78000', 'Check that table partman_test.id_taptest_table_p70000_p78000_p78000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p78000_p78100', 'Check that table partman_test.id_taptest_table_p70000_p78000_p78100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p78000_p78200', 'Check that table partman_test.id_taptest_table_p70000_p78000_p78200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p78000_p78300', 'Check that table partman_test.id_taptest_table_p70000_p78000_p78300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p78000_p78400', 'Check that table partman_test.id_taptest_table_p70000_p78000_p78400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p78000_p78500', 'Check that table partman_test.id_taptest_table_p70000_p78000_p78500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p78000_p78600', 'Check that table partman_test.id_taptest_table_p70000_p78000_p78600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p78000_p78700', 'Check that table partman_test.id_taptest_table_p70000_p78000_p78700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p78000_p78800', 'Check that table partman_test.id_taptest_table_p70000_p78000_p78800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p78000_p78900', 'Check that table partman_test.id_taptest_table_p70000_p78000_p78900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p79000_p79000', 'Check that table partman_test.id_taptest_table_p70000_p79000_p79000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p79000_p79100', 'Check that table partman_test.id_taptest_table_p70000_p79000_p79100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p79000_p79200', 'Check that table partman_test.id_taptest_table_p70000_p79000_p79200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p79000_p79300', 'Check that table partman_test.id_taptest_table_p70000_p79000_p79300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p79000_p79400', 'Check that table partman_test.id_taptest_table_p70000_p79000_p79400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p79000_p79500', 'Check that table partman_test.id_taptest_table_p70000_p79000_p79500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p79000_p79600', 'Check that table partman_test.id_taptest_table_p70000_p79000_p79600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p79000_p79700', 'Check that table partman_test.id_taptest_table_p70000_p79000_p79700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p79000_p79800', 'Check that table partman_test.id_taptest_table_p70000_p79000_p79800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p79000_p79900', 'Check that table partman_test.id_taptest_table_p70000_p79000_p79900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p80000_p80000', 'Check that table partman_test.id_taptest_table_p80000_p80000_p80000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p80000_p80100', 'Check that table partman_test.id_taptest_table_p80000_p80000_p80100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p80000_p80200', 'Check that table partman_test.id_taptest_table_p80000_p80000_p80200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p80000_p80300', 'Check that table partman_test.id_taptest_table_p80000_p80000_p80300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p80000_p80400', 'Check that table partman_test.id_taptest_table_p80000_p80000_p80400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p80000_p80500', 'Check that table partman_test.id_taptest_table_p80000_p80000_p80500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p80000_p80600', 'Check that table partman_test.id_taptest_table_p80000_p80000_p80600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p80000_p80700', 'Check that table partman_test.id_taptest_table_p80000_p80000_p80700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p80000_p80800', 'Check that table partman_test.id_taptest_table_p80000_p80000_p80800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p80000_p80900', 'Check that table partman_test.id_taptest_table_p80000_p80000_p80900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p81000_p81000', 'Check that table partman_test.id_taptest_table_p80000_p81000_p81000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p81000_p81100', 'Check that table partman_test.id_taptest_table_p80000_p81000_p81100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p81000_p81200', 'Check that table partman_test.id_taptest_table_p80000_p81000_p81200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p81000_p81300', 'Check that table partman_test.id_taptest_table_p80000_p81000_p81300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p81000_p81400', 'Check that table partman_test.id_taptest_table_p80000_p81000_p81400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p81000_p81500', 'Check that table partman_test.id_taptest_table_p80000_p81000_p81500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p81000_p81600', 'Check that table partman_test.id_taptest_table_p80000_p81000_p81600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p81000_p81700', 'Check that table partman_test.id_taptest_table_p80000_p81000_p81700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p81000_p81800', 'Check that table partman_test.id_taptest_table_p80000_p81000_p81800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p81000_p81900', 'Check that table partman_test.id_taptest_table_p80000_p81000_p81900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p82000_p82000', 'Check that table partman_test.id_taptest_table_p80000_p82000_p82000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p82000_p82100', 'Check that table partman_test.id_taptest_table_p80000_p82000_p82100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p82000_p82200', 'Check that table partman_test.id_taptest_table_p80000_p82000_p82200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p82000_p82300', 'Check that table partman_test.id_taptest_table_p80000_p82000_p82300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p82000_p82400', 'Check that table partman_test.id_taptest_table_p80000_p82000_p82400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p82000_p82500', 'Check that table partman_test.id_taptest_table_p80000_p82000_p82500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p82000_p82600', 'Check that table partman_test.id_taptest_table_p80000_p82000_p82600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p82000_p82700', 'Check that table partman_test.id_taptest_table_p80000_p82000_p82700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p82000_p82800', 'Check that table partman_test.id_taptest_table_p80000_p82000_p82800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p82000_p82900', 'Check that table partman_test.id_taptest_table_p80000_p82000_p82900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p83000_p83000', 'Check that table partman_test.id_taptest_table_p80000_p83000_p83000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p83000_p83100', 'Check that table partman_test.id_taptest_table_p80000_p83000_p83100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p83000_p83200', 'Check that table partman_test.id_taptest_table_p80000_p83000_p83200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p83000_p83300', 'Check that table partman_test.id_taptest_table_p80000_p83000_p83300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p83000_p83400', 'Check that table partman_test.id_taptest_table_p80000_p83000_p83400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p83000_p83500', 'Check that table partman_test.id_taptest_table_p80000_p83000_p83500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p83000_p83600', 'Check that table partman_test.id_taptest_table_p80000_p83000_p83600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p83000_p83700', 'Check that table partman_test.id_taptest_table_p80000_p83000_p83700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p83000_p83800', 'Check that table partman_test.id_taptest_table_p80000_p83000_p83800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p83000_p83900', 'Check that table partman_test.id_taptest_table_p80000_p83000_p83900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p84000_p84000', 'Check that table partman_test.id_taptest_table_p80000_p84000_p84000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p84000_p84100', 'Check that table partman_test.id_taptest_table_p80000_p84000_p84100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p84000_p84200', 'Check that table partman_test.id_taptest_table_p80000_p84000_p84200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p84000_p84300', 'Check that table partman_test.id_taptest_table_p80000_p84000_p84300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p84000_p84400', 'Check that table partman_test.id_taptest_table_p80000_p84000_p84400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p84000_p84500', 'Check that table partman_test.id_taptest_table_p80000_p84000_p84500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p84000_p84600', 'Check that table partman_test.id_taptest_table_p80000_p84000_p84600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p84000_p84700', 'Check that table partman_test.id_taptest_table_p80000_p84000_p84700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p84000_p84800', 'Check that table partman_test.id_taptest_table_p80000_p84000_p84800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p84000_p84900', 'Check that table partman_test.id_taptest_table_p80000_p84000_p84900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p85000_p85000', 'Check that table partman_test.id_taptest_table_p80000_p85000_p85000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p85000_p85100', 'Check that table partman_test.id_taptest_table_p80000_p85000_p85100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p85000_p85200', 'Check that table partman_test.id_taptest_table_p80000_p85000_p85200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p85000_p85300', 'Check that table partman_test.id_taptest_table_p80000_p85000_p85300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p85000_p85400', 'Check that table partman_test.id_taptest_table_p80000_p85000_p85400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p85000_p85500', 'Check that table partman_test.id_taptest_table_p80000_p85000_p85500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p85000_p85600', 'Check that table partman_test.id_taptest_table_p80000_p85000_p85600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p85000_p85700', 'Check that table partman_test.id_taptest_table_p80000_p85000_p85700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p85000_p85800', 'Check that table partman_test.id_taptest_table_p80000_p85000_p85800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p85000_p85900', 'Check that table partman_test.id_taptest_table_p80000_p85000_p85900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p86000_p86000', 'Check that table partman_test.id_taptest_table_p80000_p86000_p86000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p86000_p86100', 'Check that table partman_test.id_taptest_table_p80000_p86000_p86100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p86000_p86200', 'Check that table partman_test.id_taptest_table_p80000_p86000_p86200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p86000_p86300', 'Check that table partman_test.id_taptest_table_p80000_p86000_p86300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p86000_p86400', 'Check that table partman_test.id_taptest_table_p80000_p86000_p86400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p86000_p86500', 'Check that table partman_test.id_taptest_table_p80000_p86000_p86500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p86000_p86600', 'Check that table partman_test.id_taptest_table_p80000_p86000_p86600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p86000_p86700', 'Check that table partman_test.id_taptest_table_p80000_p86000_p86700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p86000_p86800', 'Check that table partman_test.id_taptest_table_p80000_p86000_p86800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p86000_p86900', 'Check that table partman_test.id_taptest_table_p80000_p86000_p86900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p87000_p87000', 'Check that table partman_test.id_taptest_table_p80000_p87000_p87000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p87000_p87100', 'Check that table partman_test.id_taptest_table_p80000_p87000_p87100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p87000_p87200', 'Check that table partman_test.id_taptest_table_p80000_p87000_p87200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p87000_p87300', 'Check that table partman_test.id_taptest_table_p80000_p87000_p87300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p87000_p87400', 'Check that table partman_test.id_taptest_table_p80000_p87000_p87400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p87000_p87500', 'Check that table partman_test.id_taptest_table_p80000_p87000_p87500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p87000_p87600', 'Check that table partman_test.id_taptest_table_p80000_p87000_p87600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p87000_p87700', 'Check that table partman_test.id_taptest_table_p80000_p87000_p87700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p87000_p87800', 'Check that table partman_test.id_taptest_table_p80000_p87000_p87800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p87000_p87900', 'Check that table partman_test.id_taptest_table_p80000_p87000_p87900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p88000_p88000', 'Check that table partman_test.id_taptest_table_p80000_p88000_p88000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p88000_p88100', 'Check that table partman_test.id_taptest_table_p80000_p88000_p88100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p88000_p88200', 'Check that table partman_test.id_taptest_table_p80000_p88000_p88200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p88000_p88300', 'Check that table partman_test.id_taptest_table_p80000_p88000_p88300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p88000_p88400', 'Check that table partman_test.id_taptest_table_p80000_p88000_p88400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p88000_p88500', 'Check that table partman_test.id_taptest_table_p80000_p88000_p88500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p88000_p88600', 'Check that table partman_test.id_taptest_table_p80000_p88000_p88600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p88000_p88700', 'Check that table partman_test.id_taptest_table_p80000_p88000_p88700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p88000_p88800', 'Check that table partman_test.id_taptest_table_p80000_p88000_p88800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p88000_p88900', 'Check that table partman_test.id_taptest_table_p80000_p88000_p88900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p89000_p89000', 'Check that table partman_test.id_taptest_table_p80000_p89000_p89000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p89000_p89100', 'Check that table partman_test.id_taptest_table_p80000_p89000_p89100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p89000_p89200', 'Check that table partman_test.id_taptest_table_p80000_p89000_p89200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p89000_p89300', 'Check that table partman_test.id_taptest_table_p80000_p89000_p89300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p89000_p89400', 'Check that table partman_test.id_taptest_table_p80000_p89000_p89400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p89000_p89500', 'Check that table partman_test.id_taptest_table_p80000_p89000_p89500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p89000_p89600', 'Check that table partman_test.id_taptest_table_p80000_p89000_p89600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p89000_p89700', 'Check that table partman_test.id_taptest_table_p80000_p89000_p89700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p89000_p89800', 'Check that table partman_test.id_taptest_table_p80000_p89000_p89800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p89000_p89900', 'Check that table partman_test.id_taptest_table_p80000_p89000_p89900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p90000_p90000', 'Check that table partman_test.id_taptest_table_p90000_p90000_p90000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p90000_p90100', 'Check that table partman_test.id_taptest_table_p90000_p90000_p90100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p90000_p90200', 'Check that table partman_test.id_taptest_table_p90000_p90000_p90200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p90000_p90300', 'Check that table partman_test.id_taptest_table_p90000_p90000_p90300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p90000_p90400', 'Check that table partman_test.id_taptest_table_p90000_p90000_p90400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p90000_p90500', 'Check that table partman_test.id_taptest_table_p90000_p90000_p90500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p90000_p90600', 'Check that table partman_test.id_taptest_table_p90000_p90000_p90600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p90000_p90700', 'Check that table partman_test.id_taptest_table_p90000_p90000_p90700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p90000_p90800', 'Check that table partman_test.id_taptest_table_p90000_p90000_p90800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p90000_p90900', 'Check that table partman_test.id_taptest_table_p90000_p90000_p90900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p91000_p91000', 'Check that table partman_test.id_taptest_table_p90000_p91000_p91000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p91000_p91100', 'Check that table partman_test.id_taptest_table_p90000_p91000_p91100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p91000_p91200', 'Check that table partman_test.id_taptest_table_p90000_p91000_p91200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p91000_p91300', 'Check that table partman_test.id_taptest_table_p90000_p91000_p91300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p91000_p91400', 'Check that table partman_test.id_taptest_table_p90000_p91000_p91400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p91000_p91500', 'Check that table partman_test.id_taptest_table_p90000_p91000_p91500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p91000_p91600', 'Check that table partman_test.id_taptest_table_p90000_p91000_p91600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p91000_p91700', 'Check that table partman_test.id_taptest_table_p90000_p91000_p91700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p91000_p91800', 'Check that table partman_test.id_taptest_table_p90000_p91000_p91800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p91000_p91900', 'Check that table partman_test.id_taptest_table_p90000_p91000_p91900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p92000_p92000', 'Check that table partman_test.id_taptest_table_p90000_p92000_p92000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p92000_p92100', 'Check that table partman_test.id_taptest_table_p90000_p92000_p92100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p92000_p92200', 'Check that table partman_test.id_taptest_table_p90000_p92000_p92200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p92000_p92300', 'Check that table partman_test.id_taptest_table_p90000_p92000_p92300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p92000_p92400', 'Check that table partman_test.id_taptest_table_p90000_p92000_p92400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p92000_p92500', 'Check that table partman_test.id_taptest_table_p90000_p92000_p92500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p92000_p92600', 'Check that table partman_test.id_taptest_table_p90000_p92000_p92600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p92000_p92700', 'Check that table partman_test.id_taptest_table_p90000_p92000_p92700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p92000_p92800', 'Check that table partman_test.id_taptest_table_p90000_p92000_p92800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p92000_p92900', 'Check that table partman_test.id_taptest_table_p90000_p92000_p92900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p93000_p93000', 'Check that table partman_test.id_taptest_table_p90000_p93000_p93000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p93000_p93100', 'Check that table partman_test.id_taptest_table_p90000_p93000_p93100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p93000_p93200', 'Check that table partman_test.id_taptest_table_p90000_p93000_p93200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p93000_p93300', 'Check that table partman_test.id_taptest_table_p90000_p93000_p93300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p93000_p93400', 'Check that table partman_test.id_taptest_table_p90000_p93000_p93400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p93000_p93500', 'Check that table partman_test.id_taptest_table_p90000_p93000_p93500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p93000_p93600', 'Check that table partman_test.id_taptest_table_p90000_p93000_p93600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p93000_p93700', 'Check that table partman_test.id_taptest_table_p90000_p93000_p93700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p93000_p93800', 'Check that table partman_test.id_taptest_table_p90000_p93000_p93800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p93000_p93900', 'Check that table partman_test.id_taptest_table_p90000_p93000_p93900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p94000_p94000', 'Check that table partman_test.id_taptest_table_p90000_p94000_p94000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p94000_p94100', 'Check that table partman_test.id_taptest_table_p90000_p94000_p94100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p94000_p94200', 'Check that table partman_test.id_taptest_table_p90000_p94000_p94200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p94000_p94300', 'Check that table partman_test.id_taptest_table_p90000_p94000_p94300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p94000_p94400', 'Check that table partman_test.id_taptest_table_p90000_p94000_p94400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p94000_p94500', 'Check that table partman_test.id_taptest_table_p90000_p94000_p94500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p94000_p94600', 'Check that table partman_test.id_taptest_table_p90000_p94000_p94600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p94000_p94700', 'Check that table partman_test.id_taptest_table_p90000_p94000_p94700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p94000_p94800', 'Check that table partman_test.id_taptest_table_p90000_p94000_p94800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p94000_p94900', 'Check that table partman_test.id_taptest_table_p90000_p94000_p94900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p95000_p95000', 'Check that table partman_test.id_taptest_table_p90000_p95000_p95000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p95000_p95100', 'Check that table partman_test.id_taptest_table_p90000_p95000_p95100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p95000_p95200', 'Check that table partman_test.id_taptest_table_p90000_p95000_p95200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p95000_p95300', 'Check that table partman_test.id_taptest_table_p90000_p95000_p95300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p95000_p95400', 'Check that table partman_test.id_taptest_table_p90000_p95000_p95400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p95000_p95500', 'Check that table partman_test.id_taptest_table_p90000_p95000_p95500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p95000_p95600', 'Check that table partman_test.id_taptest_table_p90000_p95000_p95600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p95000_p95700', 'Check that table partman_test.id_taptest_table_p90000_p95000_p95700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p95000_p95800', 'Check that table partman_test.id_taptest_table_p90000_p95000_p95800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p95000_p95900', 'Check that table partman_test.id_taptest_table_p90000_p95000_p95900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p96000_p96000', 'Check that table partman_test.id_taptest_table_p90000_p96000_p96000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p96000_p96100', 'Check that table partman_test.id_taptest_table_p90000_p96000_p96100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p96000_p96200', 'Check that table partman_test.id_taptest_table_p90000_p96000_p96200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p96000_p96300', 'Check that table partman_test.id_taptest_table_p90000_p96000_p96300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p96000_p96400', 'Check that table partman_test.id_taptest_table_p90000_p96000_p96400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p96000_p96500', 'Check that table partman_test.id_taptest_table_p90000_p96000_p96500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p96000_p96600', 'Check that table partman_test.id_taptest_table_p90000_p96000_p96600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p96000_p96700', 'Check that table partman_test.id_taptest_table_p90000_p96000_p96700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p96000_p96800', 'Check that table partman_test.id_taptest_table_p90000_p96000_p96800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p96000_p96900', 'Check that table partman_test.id_taptest_table_p90000_p96000_p96900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p97000_p97000', 'Check that table partman_test.id_taptest_table_p90000_p97000_p97000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p97000_p97100', 'Check that table partman_test.id_taptest_table_p90000_p97000_p97100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p97000_p97200', 'Check that table partman_test.id_taptest_table_p90000_p97000_p97200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p97000_p97300', 'Check that table partman_test.id_taptest_table_p90000_p97000_p97300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p97000_p97400', 'Check that table partman_test.id_taptest_table_p90000_p97000_p97400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p97000_p97500', 'Check that table partman_test.id_taptest_table_p90000_p97000_p97500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p97000_p97600', 'Check that table partman_test.id_taptest_table_p90000_p97000_p97600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p97000_p97700', 'Check that table partman_test.id_taptest_table_p90000_p97000_p97700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p97000_p97800', 'Check that table partman_test.id_taptest_table_p90000_p97000_p97800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p97000_p97900', 'Check that table partman_test.id_taptest_table_p90000_p97000_p97900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p98000_p98000', 'Check that table partman_test.id_taptest_table_p90000_p98000_p98000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p98000_p98100', 'Check that table partman_test.id_taptest_table_p90000_p98000_p98100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p98000_p98200', 'Check that table partman_test.id_taptest_table_p90000_p98000_p98200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p98000_p98300', 'Check that table partman_test.id_taptest_table_p90000_p98000_p98300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p98000_p98400', 'Check that table partman_test.id_taptest_table_p90000_p98000_p98400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p98000_p98500', 'Check that table partman_test.id_taptest_table_p90000_p98000_p98500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p98000_p98600', 'Check that table partman_test.id_taptest_table_p90000_p98000_p98600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p98000_p98700', 'Check that table partman_test.id_taptest_table_p90000_p98000_p98700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p98000_p98800', 'Check that table partman_test.id_taptest_table_p90000_p98000_p98800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p98000_p98900', 'Check that table partman_test.id_taptest_table_p90000_p98000_p98900 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p99000_p99000', 'Check that table partman_test.id_taptest_table_p90000_p99000_p99000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p99000_p99100', 'Check that table partman_test.id_taptest_table_p90000_p99000_p99100 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p99000_p99200', 'Check that table partman_test.id_taptest_table_p90000_p99000_p99200 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p99000_p99300', 'Check that table partman_test.id_taptest_table_p90000_p99000_p99300 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p99000_p99400', 'Check that table partman_test.id_taptest_table_p90000_p99000_p99400 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p99000_p99500', 'Check that table partman_test.id_taptest_table_p90000_p99000_p99500 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p99000_p99600', 'Check that table partman_test.id_taptest_table_p90000_p99000_p99600 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p99000_p99700', 'Check that table partman_test.id_taptest_table_p90000_p99000_p99700 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p99000_p99800', 'Check that table partman_test.id_taptest_table_p90000_p99000_p99800 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p99000_p99900', 'Check that table partman_test.id_taptest_table_p90000_p99000_p99900 no longer exists'); -- Recheck that all parents partitions have their data SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table', 'Check that parent table is still empty'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table', ARRAY[107500], 'Check count from parent table'); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p0', 'Check that parent table id_taptest_table_p0 is still empty '); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p0_p0', ARRAY[999], 'Check count from id_taptest_table_p0_p0'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p0_p1000', ARRAY[1000], 'Check count from id_taptest_table_p0_p1000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p0_p2000', ARRAY[1000], 'Check count from id_taptest_table_p0_p2000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p0_p3000', ARRAY[1000], 'Check count from id_taptest_table_p0_p3000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p0_p4000', ARRAY[1000], 'Check count from id_taptest_table_p0_p4000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p0_p5000', ARRAY[1000], 'Check count from id_taptest_table_p0_p5000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p0_p6000', ARRAY[1000], 'Check count from id_taptest_table_p0_p6000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p0_p7000', ARRAY[1000], 'Check count from id_taptest_table_p0_p7000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p0_p8000', ARRAY[1000], 'Check count from id_taptest_table_p0_p8000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p0_p9000', ARRAY[1000], 'Check count from id_taptest_table_p0_p9000'); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p10000', 'Check that parent table id_taptest_table_p10000 is still empty '); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p10000_p10000', ARRAY[1000], 'Check count from id_taptest_table_p10000_p10000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p10000_p11000', ARRAY[1000], 'Check count from id_taptest_table_p10000_p11000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p10000_p12000', ARRAY[1000], 'Check count from id_taptest_table_p10000_p12000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p10000_p13000', ARRAY[1000], 'Check count from id_taptest_table_p10000_p13000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p10000_p14000', ARRAY[1000], 'Check count from id_taptest_table_p10000_p14000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p10000_p15000', ARRAY[1000], 'Check count from id_taptest_table_p10000_p15000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p10000_p16000', ARRAY[1000], 'Check count from id_taptest_table_p10000_p16000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p10000_p17000', ARRAY[1000], 'Check count from id_taptest_table_p10000_p17000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p10000_p18000', ARRAY[1000], 'Check count from id_taptest_table_p10000_p18000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p10000_p19000', ARRAY[1000], 'Check count from id_taptest_table_p10000_p19000'); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p20000', 'Check that parent table id_taptest_table_p20000 is still empty '); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p20000_p20000', ARRAY[1000], 'Check count from id_taptest_table_p20000_p20000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p20000_p21000', ARRAY[1000], 'Check count from id_taptest_table_p20000_p21000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p20000_p22000', ARRAY[1000], 'Check count from id_taptest_table_p20000_p22000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p20000_p23000', ARRAY[1000], 'Check count from id_taptest_table_p20000_p23000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p20000_p24000', ARRAY[1000], 'Check count from id_taptest_table_p20000_p24000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p20000_p25000', ARRAY[1000], 'Check count from id_taptest_table_p20000_p25000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p20000_p26000', ARRAY[1000], 'Check count from id_taptest_table_p20000_p26000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p20000_p27000', ARRAY[1000], 'Check count from id_taptest_table_p20000_p27000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p20000_p28000', ARRAY[1000], 'Check count from id_taptest_table_p20000_p28000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p20000_p29000', ARRAY[1000], 'Check count from id_taptest_table_p20000_p29000'); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p30000', 'Check that parent table id_taptest_table_p30000 is still empty '); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p30000_p30000', ARRAY[1000], 'Check count from id_taptest_table_p30000_p30000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p30000_p31000', ARRAY[1000], 'Check count from id_taptest_table_p30000_p31000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p30000_p32000', ARRAY[1000], 'Check count from id_taptest_table_p30000_p32000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p30000_p33000', ARRAY[1000], 'Check count from id_taptest_table_p30000_p33000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p30000_p34000', ARRAY[1000], 'Check count from id_taptest_table_p30000_p34000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p30000_p35000', ARRAY[1000], 'Check count from id_taptest_table_p30000_p35000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p30000_p36000', ARRAY[1000], 'Check count from id_taptest_table_p30000_p36000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p30000_p37000', ARRAY[1000], 'Check count from id_taptest_table_p30000_p37000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p30000_p38000', ARRAY[1000], 'Check count from id_taptest_table_p30000_p38000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p30000_p39000', ARRAY[1000], 'Check count from id_taptest_table_p30000_p39000'); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p40000', 'Check that parent table id_taptest_table_p40000 is still empty '); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p40000_p40000', ARRAY[1000], 'Check count from id_taptest_table_p40000_p40000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p40000_p41000', ARRAY[1000], 'Check count from id_taptest_table_p40000_p41000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p40000_p42000', ARRAY[1000], 'Check count from id_taptest_table_p40000_p42000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p40000_p43000', ARRAY[1000], 'Check count from id_taptest_table_p40000_p43000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p40000_p44000', ARRAY[1000], 'Check count from id_taptest_table_p40000_p44000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p40000_p45000', ARRAY[1000], 'Check count from id_taptest_table_p40000_p45000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p40000_p46000', ARRAY[1000], 'Check count from id_taptest_table_p40000_p46000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p40000_p47000', ARRAY[1000], 'Check count from id_taptest_table_p40000_p47000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p40000_p48000', ARRAY[1000], 'Check count from id_taptest_table_p40000_p48000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p40000_p49000', ARRAY[1000], 'Check count from id_taptest_table_p40000_p49000'); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p50000', 'Check that parent table id_taptest_table_p50000 is still empty '); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p50000_p50000', ARRAY[1000], 'Check count from id_taptest_table_p50000_p50000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p50000_p51000', ARRAY[1000], 'Check count from id_taptest_table_p50000_p51000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p50000_p52000', ARRAY[1000], 'Check count from id_taptest_table_p50000_p52000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p50000_p53000', ARRAY[1000], 'Check count from id_taptest_table_p50000_p53000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p50000_p54000', ARRAY[1000], 'Check count from id_taptest_table_p50000_p54000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p50000_p55000', ARRAY[1000], 'Check count from id_taptest_table_p50000_p55000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p50000_p56000', ARRAY[1000], 'Check count from id_taptest_table_p50000_p56000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p50000_p57000', ARRAY[1000], 'Check count from id_taptest_table_p50000_p57000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p50000_p58000', ARRAY[1000], 'Check count from id_taptest_table_p50000_p58000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p50000_p59000', ARRAY[1000], 'Check count from id_taptest_table_p50000_p59000'); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p60000', 'Check that parent table id_taptest_table_p60000 is still empty '); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p60000_p60000', ARRAY[1000], 'Check count from id_taptest_table_p60000_p60000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p60000_p61000', ARRAY[1000], 'Check count from id_taptest_table_p60000_p61000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p60000_p62000', ARRAY[1000], 'Check count from id_taptest_table_p60000_p62000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p60000_p63000', ARRAY[1000], 'Check count from id_taptest_table_p60000_p63000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p60000_p64000', ARRAY[1000], 'Check count from id_taptest_table_p60000_p64000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p60000_p65000', ARRAY[1000], 'Check count from id_taptest_table_p60000_p65000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p60000_p66000', ARRAY[1000], 'Check count from id_taptest_table_p60000_p66000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p60000_p67000', ARRAY[1000], 'Check count from id_taptest_table_p60000_p67000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p60000_p68000', ARRAY[1000], 'Check count from id_taptest_table_p60000_p68000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p60000_p69000', ARRAY[1000], 'Check count from id_taptest_table_p60000_p69000'); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p70000', 'Check that parent table id_taptest_table_p70000 is still empty '); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p70000_p70000', ARRAY[1000], 'Check count from id_taptest_table_p70000_p70000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p70000_p71000', ARRAY[1000], 'Check count from id_taptest_table_p70000_p71000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p70000_p72000', ARRAY[1000], 'Check count from id_taptest_table_p70000_p72000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p70000_p73000', ARRAY[1000], 'Check count from id_taptest_table_p70000_p73000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p70000_p74000', ARRAY[1000], 'Check count from id_taptest_table_p70000_p74000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p70000_p75000', ARRAY[1000], 'Check count from id_taptest_table_p70000_p75000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p70000_p76000', ARRAY[1000], 'Check count from id_taptest_table_p70000_p76000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p70000_p77000', ARRAY[1000], 'Check count from id_taptest_table_p70000_p77000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p70000_p78000', ARRAY[1000], 'Check count from id_taptest_table_p70000_p78000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p70000_p79000', ARRAY[1000], 'Check count from id_taptest_table_p70000_p79000'); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p80000', 'Check that parent table id_taptest_table_p80000 is still empty '); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p80000_p80000', ARRAY[1000], 'Check count from id_taptest_table_p80000_p80000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p80000_p81000', ARRAY[1000], 'Check count from id_taptest_table_p80000_p81000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p80000_p82000', ARRAY[1000], 'Check count from id_taptest_table_p80000_p82000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p80000_p83000', ARRAY[1000], 'Check count from id_taptest_table_p80000_p83000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p80000_p84000', ARRAY[1000], 'Check count from id_taptest_table_p80000_p84000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p80000_p85000', ARRAY[1000], 'Check count from id_taptest_table_p80000_p85000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p80000_p86000', ARRAY[1000], 'Check count from id_taptest_table_p80000_p86000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p80000_p87000', ARRAY[1000], 'Check count from id_taptest_table_p80000_p87000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p80000_p88000', ARRAY[1000], 'Check count from id_taptest_table_p80000_p88000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p80000_p89000', ARRAY[1000], 'Check count from id_taptest_table_p80000_p89000'); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p90000', 'Check that parent table id_taptest_table_p90000 is still empty '); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p90000_p90000', ARRAY[1000], 'Check count from id_taptest_table_p90000_p90000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p90000_p91000', ARRAY[1000], 'Check count from id_taptest_table_p90000_p91000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p90000_p92000', ARRAY[1000], 'Check count from id_taptest_table_p90000_p92000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p90000_p93000', ARRAY[1000], 'Check count from id_taptest_table_p90000_p93000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p90000_p94000', ARRAY[1000], 'Check count from id_taptest_table_p90000_p94000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p90000_p95000', ARRAY[1000], 'Check count from id_taptest_table_p90000_p95000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p90000_p96000', ARRAY[1000], 'Check count from id_taptest_table_p90000_p96000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p90000_p97000', ARRAY[1000], 'Check count from id_taptest_table_p90000_p97000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p90000_p98000', ARRAY[1000], 'Check count from id_taptest_table_p90000_p98000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p90000_p99000', ARRAY[1000], 'Check count from id_taptest_table_p90000_p99000'); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p100000', 'Check that parent table id_taptest_table_p100000 is still empty '); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p100000_p100000', ARRAY[1000], 'Check count from id_taptest_table_p100000_p100000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p100000_p101000', ARRAY[1000], 'Check count from id_taptest_table_p100000_p100000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p100000_p102000', ARRAY[1000], 'Check count from id_taptest_table_p100000_p100000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p100000_p103000', ARRAY[1000], 'Check count from id_taptest_table_p100000_p100000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p100000_p104000', ARRAY[1000], 'Check count from id_taptest_table_p100000_p100000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p100000_p105000', ARRAY[1000], 'Check count from id_taptest_table_p100000_p100000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p100000_p106000', ARRAY[1000], 'Check count from id_taptest_table_p100000_p100000'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_p100000_p107000', ARRAY[501], 'Check count from id_taptest_table_p100000_p100000'); -- Undo sub-parent partitions SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p0'', 20, p_keep_table := false)::int', ARRAY[9999], 'Undo partitioning for parent table partman_test.id_taptest_table_p0'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p10000'', 20, p_keep_table := false)::int', ARRAY[10000], 'Undo partitioning for parent table partman_test.id_taptest_table_p10000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p20000'', 20, p_keep_table := false)::int', ARRAY[10000], 'Undo partitioning for parent table partman_test.id_taptest_table_p20000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p30000'', 20, p_keep_table := false)::int', ARRAY[10000], 'Undo partitioning for parent table partman_test.id_taptest_table_p30000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p40000'', 20, p_keep_table := false)::int', ARRAY[10000], 'Undo partitioning for parent table partman_test.id_taptest_table_p40000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p50000'', 20, p_keep_table := false)::int', ARRAY[10000], 'Undo partitioning for parent table partman_test.id_taptest_table_p50000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p60000'', 20, p_keep_table := false)::int', ARRAY[10000], 'Undo partitioning for parent table partman_test.id_taptest_table_p60000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p70000'', 20, p_keep_table := false)::int', ARRAY[10000], 'Undo partitioning for parent table partman_test.id_taptest_table_p70000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p80000'', 20, p_keep_table := false)::int', ARRAY[10000], 'Undo partitioning for parent table partman_test.id_taptest_table_p80000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p90000'', 20, p_keep_table := false)::int', ARRAY[10000], 'Undo partitioning for parent table partman_test.id_taptest_table_p90000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p100000'', 20, p_keep_table := false)::int', ARRAY[7501], 'Undo partitioning for parent table partman_test.id_taptest_table_p100000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p110000'', 20, p_keep_table := false)::int', ARRAY[0], 'Undo partitioning for parent table partman_test.id_taptest_table_p110000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p120000'', 20, p_keep_table := false)::int', ARRAY[0], 'Undo partitioning for parent table partman_test.id_taptest_table_p120000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p130000'', 20, p_keep_table := false)::int', ARRAY[0], 'Undo partitioning for parent table partman_test.id_taptest_table_p130000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p140000'', 20, p_keep_table := false)::int', ARRAY[0], 'Undo partitioning for parent table partman_test.id_taptest_table_p140000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p150000'', 20, p_keep_table := false)::int', ARRAY[0], 'Undo partitioning for parent table partman_test.id_taptest_table_p150000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p160000'', 20, p_keep_table := false)::int', ARRAY[0], 'Undo partitioning for parent table partman_test.id_taptest_table_p160000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p170000'', 20, p_keep_table := false)::int', ARRAY[0], 'Undo partitioning for parent table partman_test.id_taptest_table_p170000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p180000'', 20, p_keep_table := false)::int', ARRAY[0], 'Undo partitioning for parent table partman_test.id_taptest_table_p180000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p190000'', 20, p_keep_table := false)::int', ARRAY[0], 'Undo partitioning for parent table partman_test.id_taptest_table_p190000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p200000'', 20, p_keep_table := false)::int', ARRAY[0], 'Undo partitioning for parent table partman_test.id_taptest_table_p200000'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table_p210000'', 20, p_keep_table := false)::int', ARRAY[0], 'Undo partitioning for parent table partman_test.id_taptest_table_p210000'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p0', 'Check that table partman_test.id_taptest_table_p0_p0 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p1000', 'Check that table partman_test.id_taptest_table_p0_p1000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p2000', 'Check that table partman_test.id_taptest_table_p0_p2000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p3000', 'Check that table partman_test.id_taptest_table_p0_p3000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p4000', 'Check that table partman_test.id_taptest_table_p0_p4000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p5000', 'Check that table partman_test.id_taptest_table_p0_p5000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p6000', 'Check that table partman_test.id_taptest_table_p0_p6000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p7000', 'Check that table partman_test.id_taptest_table_p0_p7000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p8000', 'Check that table partman_test.id_taptest_table_p0_p8000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p0_p9000', 'Check that table partman_test.id_taptest_table_p0_p9000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p100000', 'Check that table partman_test.id_taptest_table_p100000_p100000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p101000', 'Check that table partman_test.id_taptest_table_p100000_p101000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p102000', 'Check that table partman_test.id_taptest_table_p100000_p102000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p103000', 'Check that table partman_test.id_taptest_table_p100000_p103000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p104000', 'Check that table partman_test.id_taptest_table_p100000_p104000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p105000', 'Check that table partman_test.id_taptest_table_p100000_p105000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p106000', 'Check that table partman_test.id_taptest_table_p100000_p106000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p107000', 'Check that table partman_test.id_taptest_table_p100000_p107000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p108000', 'Check that table partman_test.id_taptest_table_p100000_p108000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000_p109000', 'Check that table partman_test.id_taptest_table_p100000_p109000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p10000', 'Check that table partman_test.id_taptest_table_p10000_p10000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p11000', 'Check that table partman_test.id_taptest_table_p10000_p11000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p12000', 'Check that table partman_test.id_taptest_table_p10000_p12000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p13000', 'Check that table partman_test.id_taptest_table_p10000_p13000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p14000', 'Check that table partman_test.id_taptest_table_p10000_p14000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p15000', 'Check that table partman_test.id_taptest_table_p10000_p15000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p16000', 'Check that table partman_test.id_taptest_table_p10000_p16000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p17000', 'Check that table partman_test.id_taptest_table_p10000_p17000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p18000', 'Check that table partman_test.id_taptest_table_p10000_p18000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000_p19000', 'Check that table partman_test.id_taptest_table_p10000_p19000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p110000_p110000', 'Check that table partman_test.id_taptest_table_p110000_p110000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p120000_p120000', 'Check that table partman_test.id_taptest_table_p120000_p120000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p130000_p130000', 'Check that table partman_test.id_taptest_table_p130000_p130000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p140000_p140000', 'Check that table partman_test.id_taptest_table_p140000_p140000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p150000_p150000', 'Check that table partman_test.id_taptest_table_p150000_p150000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p160000_p160000', 'Check that table partman_test.id_taptest_table_p160000_p160000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p170000_p170000', 'Check that table partman_test.id_taptest_table_p170000_p170000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p180000_p180000', 'Check that table partman_test.id_taptest_table_p180000_p180000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p190000_p190000', 'Check that table partman_test.id_taptest_table_p190000_p190000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p200000_p200000', 'Check that table partman_test.id_taptest_table_p200000_p200000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p20000', 'Check that table partman_test.id_taptest_table_p20000_p20000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p21000', 'Check that table partman_test.id_taptest_table_p20000_p21000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p22000', 'Check that table partman_test.id_taptest_table_p20000_p22000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p23000', 'Check that table partman_test.id_taptest_table_p20000_p23000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p24000', 'Check that table partman_test.id_taptest_table_p20000_p24000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p25000', 'Check that table partman_test.id_taptest_table_p20000_p25000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p26000', 'Check that table partman_test.id_taptest_table_p20000_p26000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p27000', 'Check that table partman_test.id_taptest_table_p20000_p27000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p28000', 'Check that table partman_test.id_taptest_table_p20000_p28000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000_p29000', 'Check that table partman_test.id_taptest_table_p20000_p29000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p210000_p210000', 'Check that table partman_test.id_taptest_table_p210000_p210000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p30000', 'Check that table partman_test.id_taptest_table_p30000_p30000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p31000', 'Check that table partman_test.id_taptest_table_p30000_p31000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p32000', 'Check that table partman_test.id_taptest_table_p30000_p32000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p33000', 'Check that table partman_test.id_taptest_table_p30000_p33000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p34000', 'Check that table partman_test.id_taptest_table_p30000_p34000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p35000', 'Check that table partman_test.id_taptest_table_p30000_p35000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p36000', 'Check that table partman_test.id_taptest_table_p30000_p36000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p37000', 'Check that table partman_test.id_taptest_table_p30000_p37000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p38000', 'Check that table partman_test.id_taptest_table_p30000_p38000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000_p39000', 'Check that table partman_test.id_taptest_table_p30000_p39000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p40000', 'Check that table partman_test.id_taptest_table_p40000_p40000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p41000', 'Check that table partman_test.id_taptest_table_p40000_p41000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p42000', 'Check that table partman_test.id_taptest_table_p40000_p42000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p43000', 'Check that table partman_test.id_taptest_table_p40000_p43000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p44000', 'Check that table partman_test.id_taptest_table_p40000_p44000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p45000', 'Check that table partman_test.id_taptest_table_p40000_p45000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p46000', 'Check that table partman_test.id_taptest_table_p40000_p46000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p47000', 'Check that table partman_test.id_taptest_table_p40000_p47000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p48000', 'Check that table partman_test.id_taptest_table_p40000_p48000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000_p49000', 'Check that table partman_test.id_taptest_table_p40000_p49000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p50000', 'Check that table partman_test.id_taptest_table_p50000_p50000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p51000', 'Check that table partman_test.id_taptest_table_p50000_p51000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p52000', 'Check that table partman_test.id_taptest_table_p50000_p52000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p53000', 'Check that table partman_test.id_taptest_table_p50000_p53000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p54000', 'Check that table partman_test.id_taptest_table_p50000_p54000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p55000', 'Check that table partman_test.id_taptest_table_p50000_p55000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p56000', 'Check that table partman_test.id_taptest_table_p50000_p56000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p57000', 'Check that table partman_test.id_taptest_table_p50000_p57000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p58000', 'Check that table partman_test.id_taptest_table_p50000_p58000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000_p59000', 'Check that table partman_test.id_taptest_table_p50000_p59000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p60000', 'Check that table partman_test.id_taptest_table_p60000_p60000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p61000', 'Check that table partman_test.id_taptest_table_p60000_p61000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p62000', 'Check that table partman_test.id_taptest_table_p60000_p62000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p63000', 'Check that table partman_test.id_taptest_table_p60000_p63000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p64000', 'Check that table partman_test.id_taptest_table_p60000_p64000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p65000', 'Check that table partman_test.id_taptest_table_p60000_p65000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p66000', 'Check that table partman_test.id_taptest_table_p60000_p66000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p67000', 'Check that table partman_test.id_taptest_table_p60000_p67000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p68000', 'Check that table partman_test.id_taptest_table_p60000_p68000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000_p69000', 'Check that table partman_test.id_taptest_table_p60000_p69000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p70000', 'Check that table partman_test.id_taptest_table_p70000_p70000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p71000', 'Check that table partman_test.id_taptest_table_p70000_p71000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p72000', 'Check that table partman_test.id_taptest_table_p70000_p72000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p73000', 'Check that table partman_test.id_taptest_table_p70000_p73000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p74000', 'Check that table partman_test.id_taptest_table_p70000_p74000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p75000', 'Check that table partman_test.id_taptest_table_p70000_p75000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p76000', 'Check that table partman_test.id_taptest_table_p70000_p76000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p77000', 'Check that table partman_test.id_taptest_table_p70000_p77000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p78000', 'Check that table partman_test.id_taptest_table_p70000_p78000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000_p79000', 'Check that table partman_test.id_taptest_table_p70000_p79000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p80000', 'Check that table partman_test.id_taptest_table_p80000_p80000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p81000', 'Check that table partman_test.id_taptest_table_p80000_p81000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p82000', 'Check that table partman_test.id_taptest_table_p80000_p82000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p83000', 'Check that table partman_test.id_taptest_table_p80000_p83000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p84000', 'Check that table partman_test.id_taptest_table_p80000_p84000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p85000', 'Check that table partman_test.id_taptest_table_p80000_p85000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p86000', 'Check that table partman_test.id_taptest_table_p80000_p86000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p87000', 'Check that table partman_test.id_taptest_table_p80000_p87000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p88000', 'Check that table partman_test.id_taptest_table_p80000_p88000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000_p89000', 'Check that table partman_test.id_taptest_table_p80000_p89000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p90000', 'Check that table partman_test.id_taptest_table_p90000_p90000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p91000', 'Check that table partman_test.id_taptest_table_p90000_p91000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p92000', 'Check that table partman_test.id_taptest_table_p90000_p92000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p93000', 'Check that table partman_test.id_taptest_table_p90000_p93000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p94000', 'Check that table partman_test.id_taptest_table_p90000_p94000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p95000', 'Check that table partman_test.id_taptest_table_p90000_p95000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p96000', 'Check that table partman_test.id_taptest_table_p90000_p96000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p97000', 'Check that table partman_test.id_taptest_table_p90000_p97000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p98000', 'Check that table partman_test.id_taptest_table_p90000_p98000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000_p99000', 'Check that table partman_test.id_taptest_table_p90000_p99000 no longer exists'); SELECT results_eq('SELECT undo_partition_id(''partman_test.id_taptest_table'', 20, p_keep_table := false)::int', ARRAY[107500], 'Undo partitioning for final parent table partman_test.id_taptest_table'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table', ARRAY[107500], 'Check count from final, non-partitioned table'); SELECT hasnt_table('partman_test.id_taptest_table_p0', 'Check that table partman_test.id_taptest_table_p0 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p10000', 'Check that table partman_test.id_taptest_table_p10000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p100000', 'Check that table partman_test.id_taptest_table_p100000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p110000', 'Check that table partman_test.id_taptest_table_p110000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p120000', 'Check that table partman_test.id_taptest_table_p120000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p130000', 'Check that table partman_test.id_taptest_table_p130000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p140000', 'Check that table partman_test.id_taptest_table_p140000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p150000', 'Check that table partman_test.id_taptest_table_p150000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p160000', 'Check that table partman_test.id_taptest_table_p160000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p170000', 'Check that table partman_test.id_taptest_table_p170000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p180000', 'Check that table partman_test.id_taptest_table_p180000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p190000', 'Check that table partman_test.id_taptest_table_p190000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p20000', 'Check that table partman_test.id_taptest_table_p20000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p200000', 'Check that table partman_test.id_taptest_table_p200000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p210000', 'Check that table partman_test.id_taptest_table_p210000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p30000', 'Check that table partman_test.id_taptest_table_p30000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p40000', 'Check that table partman_test.id_taptest_table_p40000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p50000', 'Check that table partman_test.id_taptest_table_p50000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p60000', 'Check that table partman_test.id_taptest_table_p60000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p70000', 'Check that table partman_test.id_taptest_table_p70000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p80000', 'Check that table partman_test.id_taptest_table_p80000 no longer exists'); SELECT hasnt_table('partman_test.id_taptest_table_p90000', 'Check that table partman_test.id_taptest_table_p90000 no longer exists'); SELECT is_empty('SELECT * FROM part_config WHERE parent_table LIKE ''partman_test.id_taptest_table%''', 'Check that part_config has all test tables removed'); SELECT is_empty('SELECT * FROM part_config_sub WHERE sub_parent LIKE ''partman_test.id_taptest_table%''', 'Check that part_config_sub has all test tables removed'); SELECT * FROM finish(); ROLLBACK; pg_partman-2.2.2/test/test-id-mixed-case.sql000066400000000000000000001605211262146621700207470ustar00rootroot00000000000000-- ########## ID MIXED CASE SUBPARTITION TESTS ########## -- Other tests: Foreign keys, hybrid trigger puts data outside premake into proper tables, retention schema, no jobmon, test 50% trigger with mixed case \set ON_ERROR_ROLLBACK 1 \set ON_ERROR_STOP true BEGIN; SELECT set_config('search_path','partman, public',false); SELECT plan(375); CREATE SCHEMA "Partman_test"; CREATE SCHEMA "Partman-retention-Test"; CREATE TABLE "Partman_test"."FK_test_reference" (col2 text not null, col4 text not null); CREATE UNIQUE INDEX ON "Partman_test"."FK_test_reference"(col2, col4); INSERT INTO "Partman_test"."FK_test_reference" VALUES ('stuff', 'stuff'); CREATE TABLE "Partman_test"."ID-taptest_Table" ( "COL1" int primary key , col2 text default 'stuff' , "Col-3" timestamptz NOT NULL DEFAULT now() , col4 text default 'stuff' , FOREIGN KEY (col2, col4) REFERENCES "Partman_test"."FK_test_reference"(col2, col4) MATCH FULL ON DELETE RESTRICT DEFERRABLE); INSERT INTO "Partman_test"."ID-taptest_Table" ("COL1") VALUES (generate_series(1,50000)); SELECT create_parent('Partman_test.ID-taptest_Table', 'COL1', 'id', '10000', p_jobmon := false, p_premake := 2); SELECT results_eq('SELECT partition_data_id(''Partman_test.ID-taptest_Table'', p_batch_count := 20)::int', ARRAY[50000], 'Check that partitioning function returns correct count of rows moved'); SELECT is_empty('SELECT * FROM ONLY "Partman_test"."ID-taptest_Table"', 'Check that parent table has had data moved to partition'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table"', ARRAY[50000], 'Check count from parent table'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p0"', ARRAY[9999], 'Check count from ID-taptest_Table_p0'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p10000"', ARRAY[10000], 'Check count from ID-taptest_Table_p10000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p20000"', ARRAY[10000], 'Check count from ID-taptest_Table_p20000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p30000"', ARRAY[10000], 'Check count from ID-taptest_Table_p30000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p40000"', ARRAY[10000], 'Check count from ID-taptest_Table_p40000'); SELECT col_is_pk('Partman_test', 'ID-taptest_Table_p0', ARRAY['COL1'], 'Check for primary key in ID-taptest_Table_p0'); SELECT col_is_pk('Partman_test', 'ID-taptest_Table_p10000', ARRAY['COL1'], 'Check for primary key in ID-taptest_Table_p10000'); SELECT col_is_pk('Partman_test', 'ID-taptest_Table_p20000', ARRAY['COL1'], 'Check for primary key in ID-taptest_Table_p20000'); SELECT col_is_pk('Partman_test', 'ID-taptest_Table_p30000', ARRAY['COL1'], 'Check for primary key in ID-taptest_Table_p30000'); SELECT col_is_pk('Partman_test', 'ID-taptest_Table_p40000', ARRAY['COL1'], 'Check for primary key in ID-taptest_Table_p40000'); SELECT col_is_pk('Partman_test', 'ID-taptest_Table_p50000', ARRAY['COL1'], 'Check for primary key in ID-taptest_Table_p50000'); SELECT col_is_pk('Partman_test', 'ID-taptest_Table_p60000', ARRAY['COL1'], 'Check for primary key in ID-taptest_Table_p60000'); SELECT col_is_pk('Partman_test', 'ID-taptest_Table_p70000', ARRAY['COL1'], 'Check for primary key in ID-taptest_Table_p70000'); SELECT col_is_fk('Partman_test', 'ID-taptest_Table_p0', ARRAY['col2', 'col4'], 'Check for inherited foreign key in ID-taptest_Table_p0'); SELECT col_is_fk('Partman_test', 'ID-taptest_Table_p10000', ARRAY['col2', 'col4'], 'Check for inherited foreign key in ID-taptest_Table_p10000'); SELECT col_is_fk('Partman_test', 'ID-taptest_Table_p20000', ARRAY['col2', 'col4'], 'Check for inherited foreign key in ID-taptest_Table_p20000'); SELECT col_is_fk('Partman_test', 'ID-taptest_Table_p30000', ARRAY['col2', 'col4'], 'Check for inherited foreign key in ID-taptest_Table_p30000'); SELECT col_is_fk('Partman_test', 'ID-taptest_Table_p40000', ARRAY['col2', 'col4'], 'Check for inherited foreign key in ID-taptest_Table_p40000'); SELECT col_is_fk('Partman_test', 'ID-taptest_Table_p50000', ARRAY['col2', 'col4'], 'Check for inherited foreign key in ID-taptest_Table_p50000'); SELECT col_is_fk('Partman_test', 'ID-taptest_Table_p60000', ARRAY['col2', 'col4'], 'Check for inherited foreign key in ID-taptest_Table_p60000'); SELECT col_is_fk('Partman_test', 'ID-taptest_Table_p70000', ARRAY['col2', 'col4'], 'Check for inherited foreign key in ID-taptest_Table_p70000'); -- Insert remaining rows to check that 50% trigger is working before enabling sub-partitioning INSERT INTO "Partman_test"."ID-taptest_Table" ("COL1") VALUES (generate_series(50001, 100000)); SELECT has_table('Partman_test', 'ID-taptest_Table_p80000', 'Check "ID-taptest_Table_p80000 exists'); SELECT has_table('Partman_test', 'ID-taptest_Table_p90000', 'Check "ID-taptest_Table_p90000 exists'); SELECT has_table('Partman_test', 'ID-taptest_Table_p100000', 'Check "ID-taptest_Table_p100000 exists'); SELECT has_table('Partman_test', 'ID-taptest_Table_p110000', 'Check "ID-taptest_Table_p110000 exists'); -- Won't get created by 50% rule unless 100000 row table gets half full. run_maintenance() call below after subpart will create it SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p120000', 'Check "ID-taptest_Table_p120000 does not exist'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p50000"', ARRAY[10000], 'Check count from ID-taptest_Table_p50000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p60000"', ARRAY[10000], 'Check count from ID-taptest_Table_p60000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p70000"', ARRAY[10000], 'Check count from ID-taptest_Table_p70000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p80000"', ARRAY[10000], 'Check count from ID-taptest_Table_p80000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p90000"', ARRAY[10000], 'Check count from ID-taptest_Table_p90000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p100000"', ARRAY[1], 'Check count from ID-taptest_Table_p100000'); SELECT col_is_pk('Partman_test', 'ID-taptest_Table_p80000', ARRAY['COL1'], 'Check for primary key in ID-taptest_Table_p80000'); SELECT col_is_pk('Partman_test', 'ID-taptest_Table_p90000', ARRAY['COL1'], 'Check for primary key in ID-taptest_Table_p90000'); SELECT col_is_pk('Partman_test', 'ID-taptest_Table_p100000', ARRAY['COL1'], 'Check for primary key in ID-taptest_Table_p100000'); SELECT col_is_pk('Partman_test', 'ID-taptest_Table_p110000', ARRAY['COL1'], 'Check for primary key in ID-taptest_Table_p110000'); SELECT col_is_fk('Partman_test', 'ID-taptest_Table_p80000', ARRAY['col2', 'col4'], 'Check for inherited foreign key in ID-taptest_Table_p80000'); SELECT col_is_fk('Partman_test', 'ID-taptest_Table_p90000', ARRAY['col2', 'col4'], 'Check for inherited foreign key in ID-taptest_Table_p90000'); SELECT col_is_fk('Partman_test', 'ID-taptest_Table_p100000', ARRAY['col2', 'col4'], 'Check for inherited foreign key in ID-taptest_Table_p100000'); SELECT col_is_fk('Partman_test', 'ID-taptest_Table_p110000', ARRAY['col2', 'col4'], 'Check for inherited foreign key in ID-taptest_Table_p110000'); -- Have to set current parent table to use run_maintenance before subpartitioning can be used. First, check that this error is returned. SELECT throws_ok('SELECT create_sub_parent(''Partman_test.ID-taptest_Table'', ''COL1'', ''id'', ''1000'', p_jobmon := false, p_premake := 2)', 'P0001', 'Any parent table that will be part of a sub-partitioned set (on any level) must have use_run_maintenance set to true in part_config table, even for serial partitioning. See documentation for more info.', 'Make sure sub-partitioning checks that parent is using run_maintenance'); UPDATE part_config SET use_run_maintenance = true WHERE parent_table = 'Partman_test.ID-taptest_Table'; SELECT create_sub_parent('Partman_test.ID-taptest_Table', 'COL1', 'id', '1000', p_jobmon := false, p_premake := 2); -- Test for normal partitions that should be made based on current max value of 100,000 SELECT has_table('Partman_test', 'ID-taptest_Table_p90000_p98000', 'Check ID-taptest_Table_p90000_p98000 exists'); SELECT has_table('Partman_test', 'ID-taptest_Table_p90000_p99000', 'Check ID-taptest_Table_p90000_p99000 exists'); SELECT has_table('Partman_test', 'ID-taptest_Table_p100000_p100000', 'Check ID-taptest_Table_p100000_p100000 exists'); SELECT has_table('Partman_test', 'ID-taptest_Table_p100000_p101000', 'Check ID-taptest_Table_p100000_p101000 exists'); SELECT has_table('Partman_test', 'ID-taptest_Table_p100000_p102000', 'Check ID-taptest_Table_p100000_p102000 exists'); -- Tests that ensure minimal partition was made in all other sets SELECT has_table('Partman_test', 'ID-taptest_Table_p0_p0', 'Check ID-taptest_Table_p0_p0 exists'); SELECT has_table('Partman_test', 'ID-taptest_Table_p10000_p10000', 'Check ID-taptest_Table_p10000_p10000 exists'); SELECT has_table('Partman_test', 'ID-taptest_Table_p20000_p20000', 'Check ID-taptest_Table_p20000_p20000 exists'); SELECT has_table('Partman_test', 'ID-taptest_Table_p30000_p30000', 'Check ID-taptest_Table_p30000_p30000 exists'); SELECT has_table('Partman_test', 'ID-taptest_Table_p40000_p40000', 'Check ID-taptest_Table_p40000_p40000 exists'); SELECT has_table('Partman_test', 'ID-taptest_Table_p50000_p50000', 'Check ID-taptest_Table_p50000_p50000 exists'); SELECT has_table('Partman_test', 'ID-taptest_Table_p60000_p60000', 'Check ID-taptest_Table_p60000_p60000 exists'); SELECT has_table('Partman_test', 'ID-taptest_Table_p70000_p70000', 'Check ID-taptest_Table_p70000_p70000 exists'); SELECT has_table('Partman_test', 'ID-taptest_Table_p80000_p80000', 'Check ID-taptest_Table_p80000_p80000 exists'); SELECT has_table('Partman_test', 'ID-taptest_Table_p110000_p110000', 'Check ID-taptest_Table_p110000_p110000 exists'); -- Partition all data again SELECT results_eq('SELECT partition_data_id(''Partman_test.ID-taptest_Table_p0'', p_batch_count := 20)::int', ARRAY[9999], 'Check that partitioning function returns correct count of rows moved for ID-taptest_Table_p0'); SELECT results_eq('SELECT partition_data_id(''Partman_test.ID-taptest_Table_p10000'', p_batch_count := 20)::int', ARRAY[10000], 'Check that partitioning function returns correct count of rows moved for ID-taptest_Table_p10000'); SELECT results_eq('SELECT partition_data_id(''Partman_test.ID-taptest_Table_p20000'', p_batch_count := 20)::int', ARRAY[10000], 'Check that partitioning function returns correct count of rows moved for ID-taptest_Table_p20000'); SELECT results_eq('SELECT partition_data_id(''Partman_test.ID-taptest_Table_p30000'', p_batch_count := 20)::int', ARRAY[10000], 'Check that partitioning function returns correct count of rows moved for ID-taptest_Table_p30000'); SELECT results_eq('SELECT partition_data_id(''Partman_test.ID-taptest_Table_p40000'', p_batch_count := 20)::int', ARRAY[10000], 'Check that partitioning function returns correct count of rows moved for ID-taptest_Table_p40000'); SELECT results_eq('SELECT partition_data_id(''Partman_test.ID-taptest_Table_p50000'', p_batch_count := 20)::int', ARRAY[10000], 'Check that partitioning function returns correct count of rows moved for ID-taptest_Table_p50000'); SELECT results_eq('SELECT partition_data_id(''Partman_test.ID-taptest_Table_p60000'', p_batch_count := 20)::int', ARRAY[10000], 'Check that partitioning function returns correct count of rows moved for ID-taptest_Table_p60000'); SELECT results_eq('SELECT partition_data_id(''Partman_test.ID-taptest_Table_p70000'', p_batch_count := 20)::int', ARRAY[10000], 'Check that partitioning function returns correct count of rows moved for ID-taptest_Table_p70000'); SELECT results_eq('SELECT partition_data_id(''Partman_test.ID-taptest_Table_p80000'', p_batch_count := 20)::int', ARRAY[10000], 'Check that partitioning function returns correct count of rows moved for ID-taptest_Table_p80000'); SELECT results_eq('SELECT partition_data_id(''Partman_test.ID-taptest_Table_p90000'', p_batch_count := 20)::int', ARRAY[10000], 'Check that partitioning function returns correct count of rows moved for ID-taptest_Table_p90000'); SELECT results_eq('SELECT partition_data_id(''Partman_test.ID-taptest_Table_p100000'', p_batch_count := 20)::int', ARRAY[1], 'Check that partitioning function returns correct count of rows moved for ID-taptest_Table_p100000'); -- Test that all partitions have their data/exist SELECT is_empty('SELECT * FROM ONLY "Partman_test"."ID-taptest_Table"', 'Check that parent table has had data moved to partition'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table"', ARRAY[100000], 'Check count from parent table'); SELECT is_empty('SELECT * FROM ONLY "Partman_test"."ID-taptest_Table_p0"', 'Check that parent table ID-taptest_Table_p0 has had data moved to partition '); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p0_p0"', ARRAY[999], 'Check count from ID-taptest_Table_p0_p0'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p0_p1000"', ARRAY[1000], 'Check count from ID-taptest_Table_p0_p1000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p0_p2000"', ARRAY[1000], 'Check count from ID-taptest_Table_p0_p2000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p0_p3000"', ARRAY[1000], 'Check count from ID-taptest_Table_p0_p3000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p0_p4000"', ARRAY[1000], 'Check count from ID-taptest_Table_p0_p4000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p0_p5000"', ARRAY[1000], 'Check count from ID-taptest_Table_p0_p5000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p0_p6000"', ARRAY[1000], 'Check count from ID-taptest_Table_p0_p6000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p0_p7000"', ARRAY[1000], 'Check count from ID-taptest_Table_p0_p7000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p0_p8000"', ARRAY[1000], 'Check count from ID-taptest_Table_p0_p8000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p0_p9000"', ARRAY[1000], 'Check count from ID-taptest_Table_p0_p9000'); SELECT is_empty('SELECT * FROM ONLY "Partman_test"."ID-taptest_Table_p10000"', 'Check that parent table ID-taptest_Table_p10000 has had data moved to partition '); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p10000_p10000"', ARRAY[1000], 'Check count from ID-taptest_Table_p10000_p10000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p10000_p11000"', ARRAY[1000], 'Check count from ID-taptest_Table_p10000_p11000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p10000_p12000"', ARRAY[1000], 'Check count from ID-taptest_Table_p10000_p12000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p10000_p13000"', ARRAY[1000], 'Check count from ID-taptest_Table_p10000_p13000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p10000_p14000"', ARRAY[1000], 'Check count from ID-taptest_Table_p10000_p14000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p10000_p15000"', ARRAY[1000], 'Check count from ID-taptest_Table_p10000_p15000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p10000_p16000"', ARRAY[1000], 'Check count from ID-taptest_Table_p10000_p16000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p10000_p17000"', ARRAY[1000], 'Check count from ID-taptest_Table_p10000_p17000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p10000_p18000"', ARRAY[1000], 'Check count from ID-taptest_Table_p10000_p18000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p10000_p19000"', ARRAY[1000], 'Check count from ID-taptest_Table_p10000_p19000'); SELECT is_empty('SELECT * FROM ONLY "Partman_test"."ID-taptest_Table_p20000"', 'Check that parent table ID-taptest_Table_p20000 has had data moved to partition '); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p20000_p20000"', ARRAY[1000], 'Check count from ID-taptest_Table_p20000_p20000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p20000_p21000"', ARRAY[1000], 'Check count from ID-taptest_Table_p20000_p21000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p20000_p22000"', ARRAY[1000], 'Check count from ID-taptest_Table_p20000_p22000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p20000_p23000"', ARRAY[1000], 'Check count from ID-taptest_Table_p20000_p23000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p20000_p24000"', ARRAY[1000], 'Check count from ID-taptest_Table_p20000_p24000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p20000_p25000"', ARRAY[1000], 'Check count from ID-taptest_Table_p20000_p25000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p20000_p26000"', ARRAY[1000], 'Check count from ID-taptest_Table_p20000_p26000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p20000_p27000"', ARRAY[1000], 'Check count from ID-taptest_Table_p20000_p27000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p20000_p28000"', ARRAY[1000], 'Check count from ID-taptest_Table_p20000_p28000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p20000_p29000"', ARRAY[1000], 'Check count from ID-taptest_Table_p20000_p29000'); SELECT is_empty('SELECT * FROM ONLY "Partman_test"."ID-taptest_Table_p30000"', 'Check that parent table ID-taptest_Table_p30000 has had data moved to partition '); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p30000_p30000"', ARRAY[1000], 'Check count from ID-taptest_Table_p30000_p30000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p30000_p31000"', ARRAY[1000], 'Check count from ID-taptest_Table_p30000_p31000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p30000_p32000"', ARRAY[1000], 'Check count from ID-taptest_Table_p30000_p32000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p30000_p33000"', ARRAY[1000], 'Check count from ID-taptest_Table_p30000_p33000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p30000_p34000"', ARRAY[1000], 'Check count from ID-taptest_Table_p30000_p34000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p30000_p35000"', ARRAY[1000], 'Check count from ID-taptest_Table_p30000_p35000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p30000_p36000"', ARRAY[1000], 'Check count from ID-taptest_Table_p30000_p36000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p30000_p37000"', ARRAY[1000], 'Check count from ID-taptest_Table_p30000_p37000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p30000_p38000"', ARRAY[1000], 'Check count from ID-taptest_Table_p30000_p38000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p30000_p39000"', ARRAY[1000], 'Check count from ID-taptest_Table_p30000_p39000'); SELECT is_empty('SELECT * FROM ONLY "Partman_test"."ID-taptest_Table_p40000"', 'Check that parent table ID-taptest_Table_p40000 has had data moved to partition '); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p40000_p40000"', ARRAY[1000], 'Check count from ID-taptest_Table_p40000_p40000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p40000_p41000"', ARRAY[1000], 'Check count from ID-taptest_Table_p40000_p41000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p40000_p42000"', ARRAY[1000], 'Check count from ID-taptest_Table_p40000_p42000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p40000_p43000"', ARRAY[1000], 'Check count from ID-taptest_Table_p40000_p43000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p40000_p44000"', ARRAY[1000], 'Check count from ID-taptest_Table_p40000_p44000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p40000_p45000"', ARRAY[1000], 'Check count from ID-taptest_Table_p40000_p45000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p40000_p46000"', ARRAY[1000], 'Check count from ID-taptest_Table_p40000_p46000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p40000_p47000"', ARRAY[1000], 'Check count from ID-taptest_Table_p40000_p47000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p40000_p48000"', ARRAY[1000], 'Check count from ID-taptest_Table_p40000_p48000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p40000_p49000"', ARRAY[1000], 'Check count from ID-taptest_Table_p40000_p49000'); SELECT is_empty('SELECT * FROM ONLY "Partman_test"."ID-taptest_Table_p50000"', 'Check that parent table ID-taptest_Table_p50000 has had data moved to partition '); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p50000_p50000"', ARRAY[1000], 'Check count from ID-taptest_Table_p50000_p50000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p50000_p51000"', ARRAY[1000], 'Check count from ID-taptest_Table_p50000_p51000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p50000_p52000"', ARRAY[1000], 'Check count from ID-taptest_Table_p50000_p52000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p50000_p53000"', ARRAY[1000], 'Check count from ID-taptest_Table_p50000_p53000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p50000_p54000"', ARRAY[1000], 'Check count from ID-taptest_Table_p50000_p54000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p50000_p55000"', ARRAY[1000], 'Check count from ID-taptest_Table_p50000_p55000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p50000_p56000"', ARRAY[1000], 'Check count from ID-taptest_Table_p50000_p56000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p50000_p57000"', ARRAY[1000], 'Check count from ID-taptest_Table_p50000_p57000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p50000_p58000"', ARRAY[1000], 'Check count from ID-taptest_Table_p50000_p58000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p50000_p59000"', ARRAY[1000], 'Check count from ID-taptest_Table_p50000_p59000'); SELECT is_empty('SELECT * FROM ONLY "Partman_test"."ID-taptest_Table_p60000"', 'Check that parent table ID-taptest_Table_p60000 has had data moved to partition '); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p60000_p60000"', ARRAY[1000], 'Check count from ID-taptest_Table_p60000_p60000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p60000_p61000"', ARRAY[1000], 'Check count from ID-taptest_Table_p60000_p61000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p60000_p62000"', ARRAY[1000], 'Check count from ID-taptest_Table_p60000_p62000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p60000_p63000"', ARRAY[1000], 'Check count from ID-taptest_Table_p60000_p63000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p60000_p64000"', ARRAY[1000], 'Check count from ID-taptest_Table_p60000_p64000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p60000_p65000"', ARRAY[1000], 'Check count from ID-taptest_Table_p60000_p65000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p60000_p66000"', ARRAY[1000], 'Check count from ID-taptest_Table_p60000_p66000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p60000_p67000"', ARRAY[1000], 'Check count from ID-taptest_Table_p60000_p67000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p60000_p68000"', ARRAY[1000], 'Check count from ID-taptest_Table_p60000_p68000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p60000_p69000"', ARRAY[1000], 'Check count from ID-taptest_Table_p60000_p69000'); SELECT is_empty('SELECT * FROM ONLY "Partman_test"."ID-taptest_Table_p70000"', 'Check that parent table ID-taptest_Table_p70000 has had data moved to partition '); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p70000_p70000"', ARRAY[1000], 'Check count from ID-taptest_Table_p70000_p70000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p70000_p71000"', ARRAY[1000], 'Check count from ID-taptest_Table_p70000_p71000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p70000_p72000"', ARRAY[1000], 'Check count from ID-taptest_Table_p70000_p72000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p70000_p73000"', ARRAY[1000], 'Check count from ID-taptest_Table_p70000_p73000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p70000_p74000"', ARRAY[1000], 'Check count from ID-taptest_Table_p70000_p74000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p70000_p75000"', ARRAY[1000], 'Check count from ID-taptest_Table_p70000_p75000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p70000_p76000"', ARRAY[1000], 'Check count from ID-taptest_Table_p70000_p76000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p70000_p77000"', ARRAY[1000], 'Check count from ID-taptest_Table_p70000_p77000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p70000_p78000"', ARRAY[1000], 'Check count from ID-taptest_Table_p70000_p78000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p70000_p79000"', ARRAY[1000], 'Check count from ID-taptest_Table_p70000_p79000'); SELECT is_empty('SELECT * FROM ONLY "Partman_test"."ID-taptest_Table_p80000"', 'Check that parent table ID-taptest_Table_p80000 has had data moved to partition '); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p80000_p80000"', ARRAY[1000], 'Check count from ID-taptest_Table_p80000_p80000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p80000_p81000"', ARRAY[1000], 'Check count from ID-taptest_Table_p80000_p81000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p80000_p82000"', ARRAY[1000], 'Check count from ID-taptest_Table_p80000_p82000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p80000_p83000"', ARRAY[1000], 'Check count from ID-taptest_Table_p80000_p83000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p80000_p84000"', ARRAY[1000], 'Check count from ID-taptest_Table_p80000_p84000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p80000_p85000"', ARRAY[1000], 'Check count from ID-taptest_Table_p80000_p85000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p80000_p86000"', ARRAY[1000], 'Check count from ID-taptest_Table_p80000_p86000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p80000_p87000"', ARRAY[1000], 'Check count from ID-taptest_Table_p80000_p87000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p80000_p88000"', ARRAY[1000], 'Check count from ID-taptest_Table_p80000_p88000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p80000_p89000"', ARRAY[1000], 'Check count from ID-taptest_Table_p80000_p89000'); SELECT is_empty('SELECT * FROM ONLY "Partman_test"."ID-taptest_Table_p90000"', 'Check that parent table ID-taptest_Table_p90000 has had data moved to partition '); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p90000_p90000"', ARRAY[1000], 'Check count from ID-taptest_Table_p90000_p90000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p90000_p91000"', ARRAY[1000], 'Check count from ID-taptest_Table_p90000_p91000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p90000_p92000"', ARRAY[1000], 'Check count from ID-taptest_Table_p90000_p92000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p90000_p93000"', ARRAY[1000], 'Check count from ID-taptest_Table_p90000_p93000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p90000_p94000"', ARRAY[1000], 'Check count from ID-taptest_Table_p90000_p94000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p90000_p95000"', ARRAY[1000], 'Check count from ID-taptest_Table_p90000_p95000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p90000_p96000"', ARRAY[1000], 'Check count from ID-taptest_Table_p90000_p96000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p90000_p97000"', ARRAY[1000], 'Check count from ID-taptest_Table_p90000_p97000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p90000_p98000"', ARRAY[1000], 'Check count from ID-taptest_Table_p90000_p98000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p90000_p99000"', ARRAY[1000], 'Check count from ID-taptest_Table_p90000_p99000'); SELECT is_empty('SELECT * FROM ONLY "Partman_test"."ID-taptest_Table_p100000"', 'Check that parent table ID-taptest_Table_p100000 has had data moved to partition '); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p100000_p100000"', ARRAY[1], 'Check count from ID-taptest_Table_p100000_p100000'); SELECT col_is_pk('Partman_test', 'ID-taptest_Table_p0_p0', ARRAY['COL1'], 'Check for primary key in ID-taptest_Table_p0_p0'); SELECT col_is_pk('Partman_test', 'ID-taptest_Table_p0_p1000', ARRAY['COL1'], 'Check for primary key in ID-taptest_Table_p0_p1000'); SELECT col_is_pk('Partman_test', 'ID-taptest_Table_p0_p2000', ARRAY['COL1'], 'Check for primary key in ID-taptest_Table_p0_p2000'); SELECT col_is_pk('Partman_test', 'ID-taptest_Table_p0_p3000', ARRAY['COL1'], 'Check for primary key in ID-taptest_Table_p0_p3000'); SELECT col_is_pk('Partman_test', 'ID-taptest_Table_p0_p4000', ARRAY['COL1'], 'Check for primary key in ID-taptest_Table_p0_p4000'); SELECT col_is_pk('Partman_test', 'ID-taptest_Table_p0_p5000', ARRAY['COL1'], 'Check for primary key in ID-taptest_Table_p0_p5000'); SELECT col_is_pk('Partman_test', 'ID-taptest_Table_p0_p6000', ARRAY['COL1'], 'Check for primary key in ID-taptest_Table_p0_p6000'); SELECT col_is_pk('Partman_test', 'ID-taptest_Table_p0_p7000', ARRAY['COL1'], 'Check for primary key in ID-taptest_Table_p0_p7000'); SELECT col_is_pk('Partman_test', 'ID-taptest_Table_p0_p8000', ARRAY['COL1'], 'Check for primary key in ID-taptest_Table_p0_p8000'); SELECT col_is_pk('Partman_test', 'ID-taptest_Table_p0_p9000', ARRAY['COL1'], 'Check for primary key in ID-taptest_Table_p0_p9000'); SELECT col_is_pk('Partman_test', 'ID-taptest_Table_p90000_p90000', ARRAY['COL1'], 'Check for primary key in ID-taptest_Table_p90000_p90000'); SELECT col_is_pk('Partman_test', 'ID-taptest_Table_p90000_p91000', ARRAY['COL1'], 'Check for primary key in ID-taptest_Table_p90000_p91000'); SELECT col_is_pk('Partman_test', 'ID-taptest_Table_p90000_p92000', ARRAY['COL1'], 'Check for primary key in ID-taptest_Table_p90000_p92000'); SELECT col_is_pk('Partman_test', 'ID-taptest_Table_p90000_p93000', ARRAY['COL1'], 'Check for primary key in ID-taptest_Table_p90000_p93000'); SELECT col_is_pk('Partman_test', 'ID-taptest_Table_p90000_p94000', ARRAY['COL1'], 'Check for primary key in ID-taptest_Table_p90000_p94000'); SELECT col_is_pk('Partman_test', 'ID-taptest_Table_p90000_p95000', ARRAY['COL1'], 'Check for primary key in ID-taptest_Table_p90000_p95000'); SELECT col_is_pk('Partman_test', 'ID-taptest_Table_p90000_p96000', ARRAY['COL1'], 'Check for primary key in ID-taptest_Table_p90000_p96000'); SELECT col_is_pk('Partman_test', 'ID-taptest_Table_p90000_p97000', ARRAY['COL1'], 'Check for primary key in ID-taptest_Table_p90000_p97000'); SELECT col_is_pk('Partman_test', 'ID-taptest_Table_p90000_p98000', ARRAY['COL1'], 'Check for primary key in ID-taptest_Table_p90000_p98000'); SELECT col_is_pk('Partman_test', 'ID-taptest_Table_p90000_p99000', ARRAY['COL1'], 'Check for primary key in ID-taptest_Table_p90000_p99000'); SELECT col_is_pk('Partman_test', 'ID-taptest_Table_p100000_p100000', ARRAY['COL1'], 'Check for primary key in ID-taptest_Table_p100000_p100000'); SELECT col_is_pk('Partman_test', 'ID-taptest_Table_p100000_p101000', ARRAY['COL1'], 'Check for primary key in ID-taptest_Table_p100000_p101000'); SELECT col_is_pk('Partman_test', 'ID-taptest_Table_p100000_p102000', ARRAY['COL1'], 'Check for primary key in ID-taptest_Table_p100000_p102000'); SELECT col_is_pk('Partman_test', 'ID-taptest_Table_p110000_p110000', ARRAY['COL1'], 'Check for primary key in ID-taptest_Table_p110000_p110000'); SELECT col_is_fk('Partman_test', 'ID-taptest_Table_p10000_p10000', ARRAY['col2', 'col4'], 'Check for inherited foreign key in ID-taptest_Table_p10000_10000'); SELECT col_is_fk('Partman_test', 'ID-taptest_Table_p10000_p11000', ARRAY['col2', 'col4'], 'Check for inherited foreign key in ID-taptest_Table_p10000_11000'); SELECT col_is_fk('Partman_test', 'ID-taptest_Table_p10000_p12000', ARRAY['col2', 'col4'], 'Check for inherited foreign key in ID-taptest_Table_p10000_12000'); SELECT col_is_fk('Partman_test', 'ID-taptest_Table_p10000_p13000', ARRAY['col2', 'col4'], 'Check for inherited foreign key in ID-taptest_Table_p10000_13000'); SELECT col_is_fk('Partman_test', 'ID-taptest_Table_p10000_p14000', ARRAY['col2', 'col4'], 'Check for inherited foreign key in ID-taptest_Table_p10000_14000'); SELECT col_is_fk('Partman_test', 'ID-taptest_Table_p10000_p15000', ARRAY['col2', 'col4'], 'Check for inherited foreign key in ID-taptest_Table_p10000_15000'); SELECT col_is_fk('Partman_test', 'ID-taptest_Table_p10000_p16000', ARRAY['col2', 'col4'], 'Check for inherited foreign key in ID-taptest_Table_p10000_16000'); SELECT col_is_fk('Partman_test', 'ID-taptest_Table_p10000_p17000', ARRAY['col2', 'col4'], 'Check for inherited foreign key in ID-taptest_Table_p10000_17000'); SELECT col_is_fk('Partman_test', 'ID-taptest_Table_p10000_p18000', ARRAY['col2', 'col4'], 'Check for inherited foreign key in ID-taptest_Table_p10000_18000'); SELECT col_is_fk('Partman_test', 'ID-taptest_Table_p10000_p19000', ARRAY['col2', 'col4'], 'Check for inherited foreign key in ID-taptest_Table_p10000_19000'); SELECT col_is_fk('Partman_test', 'ID-taptest_Table_p100000_p100000', ARRAY['col2', 'col4'], 'Check for inherited foreign key in ID-taptest_Table_p100000_100000'); SELECT col_is_fk('Partman_test', 'ID-taptest_Table_p100000_p101000', ARRAY['col2', 'col4'], 'Check for inherited foreign key in ID-taptest_Table_p100000_101000'); SELECT col_is_fk('Partman_test', 'ID-taptest_Table_p100000_p102000', ARRAY['col2', 'col4'], 'Check for inherited foreign key in ID-taptest_Table_p100000_102000'); SELECT col_is_fk('Partman_test', 'ID-taptest_Table_p110000_p110000', ARRAY['col2', 'col4'], 'Check for inherited foreign key in ID-taptest_Table_p110000_110000'); -- Remove & Replace some rows to test hybrid trigger dynamic fallback DELETE FROM "Partman_test"."ID-taptest_Table_p40000"; SELECT is_empty('SELECT * FROM "Partman_test"."ID-taptest_Table_p40000"', 'Make sure test table with deleted rows is empty'); SELECT is_empty('SELECT * FROM "Partman_test"."ID-taptest_Table_p40000_p40000"', 'Make sure test table with deleted rows is empty'); INSERT INTO "Partman_test"."ID-taptest_Table" ("COL1") VALUES (generate_series(40000,49999)); -- Check that all is back as it was SELECT is_empty('SELECT * FROM ONLY "Partman_test"."ID-taptest_Table"', 'Check that parent table top is empty'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table"', ARRAY[100000], 'Check count from parent table'); SELECT is_empty('SELECT * FROM ONLY "Partman_test"."ID-taptest_Table_p40000"', 'Check that parent table ID-taptest_Table_p40000 is empty'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p40000_p40000"', ARRAY[1000], 'Check count from ID-taptest_Table_p40000_p40000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p40000_p41000"', ARRAY[1000], 'Check count from ID-taptest_Table_p40000_p41000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p40000_p42000"', ARRAY[1000], 'Check count from ID-taptest_Table_p40000_p42000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p40000_p43000"', ARRAY[1000], 'Check count from ID-taptest_Table_p40000_p43000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p40000_p44000"', ARRAY[1000], 'Check count from ID-taptest_Table_p40000_p44000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p40000_p45000"', ARRAY[1000], 'Check count from ID-taptest_Table_p40000_p45000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p40000_p46000"', ARRAY[1000], 'Check count from ID-taptest_Table_p40000_p46000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p40000_p47000"', ARRAY[1000], 'Check count from ID-taptest_Table_p40000_p47000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p40000_p48000"', ARRAY[1000], 'Check count from ID-taptest_Table_p40000_p48000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p40000_p49000"', ARRAY[1000], 'Check count from ID-taptest_Table_p40000_p49000'); -- Check that static part of trigger is working INSERT INTO "Partman_test"."ID-taptest_Table" ("COL1") VALUES (generate_series(100001,102000)); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table"', ARRAY[102000], 'Check count from parent table'); SELECT is_empty('SELECT * FROM ONLY "Partman_test"."ID-taptest_Table"', 'Check that parent table top is empty'); SELECT is_empty('SELECT * FROM ONLY "Partman_test"."ID-taptest_Table_p100000"', 'Check that parent table ID-taptest_Table_p100000 is empty'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p100000_p100000"', ARRAY[1000], 'Check count from ID-taptest_Table_p100000_p100000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p100000_p101000"', ARRAY[1000], 'Check count from ID-taptest_Table_p100000_p101000'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."ID-taptest_Table_p100000_p102000"', ARRAY[1], 'Check count from ID-taptest_Table_p100000_p102000'); -- Run maintenance to check that tables are getting premade (since 50% trigger doesn't work on subpartitioning) SELECT run_maintenance(); SELECT has_table('Partman_test', 'ID-taptest_Table_p100000_p103000', 'Check ID-taptest_Table_p100000_p103000 exists'); SELECT has_table('Partman_test', 'ID-taptest_Table_p100000_p104000', 'Check ID-taptest_Table_p100000_p104000 exists'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p100000_p105000', 'Check ID-taptest_Table_p100000_p105000 does not exist'); SELECT col_is_pk('Partman_test', 'ID-taptest_Table_p100000_p103000', ARRAY['COL1'], 'Check for primary key in ID-taptest_Table_p100000_p103000'); SELECT col_is_pk('Partman_test', 'ID-taptest_Table_p100000_p104000', ARRAY['COL1'], 'Check for primary key in ID-taptest_Table_p100000_p104000'); SELECT col_is_fk('Partman_test', 'ID-taptest_Table_p100000_p103000', ARRAY['col2', 'col4'], 'Check for inherited foreign key in ID-taptest_Table_p100000_p103000'); SELECT col_is_fk('Partman_test', 'ID-taptest_Table_p100000_p104000', ARRAY['col2', 'col4'], 'Check for inherited foreign key in ID-taptest_Table_p100000_p104000'); SELECT has_table('Partman_test', 'ID-taptest_Table_p120000', 'Check "ID-taptest_Table_p120000 exists'); SELECT col_is_pk('Partman_test', 'ID-taptest_Table_p120000', ARRAY['COL1'], 'Check for primary key in ID-taptest_Table_p120000'); SELECT col_is_fk('Partman_test', 'ID-taptest_Table_p120000', ARRAY['col2', 'col4'], 'Check for inherited foreign key in ID-taptest_Table_p120000'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p130000', 'Check "ID-taptest_Table_p130000 does not exist'); -- Testing retention for 50000 on top parent -- Retention for the sub-partitions won't work due to the nature of how it's done (current partition max - retention value). The max in each partition has no bearing on the overall integer value unlike time subpartitioning UPDATE part_config SET retention = '50000', retention_keep_table = false WHERE parent_table = 'Partman_test.ID-taptest_Table'; SELECT run_maintenance(); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p0', 'Check ID-taptest_Table_p0 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p0_p0', 'Check ID-taptest_Table_p0_p0 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p0_p1000', 'Check ID-taptest_Table_p0_p1000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p0_p2000', 'Check ID-taptest_Table_p0_p2000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p0_p3000', 'Check ID-taptest_Table_p0_p3000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p0_p4000', 'Check ID-taptest_Table_p0_p4000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p0_p5000', 'Check ID-taptest_Table_p0_p5000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p0_p6000', 'Check ID-taptest_Table_p0_p6000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p0_p7000', 'Check ID-taptest_Table_p0_p7000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p0_p8000', 'Check ID-taptest_Table_p0_p8000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p0_p9000', 'Check ID-taptest_Table_p0_p9000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p10000', 'Check ID-taptest_Table_p10000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p10000_p10000', 'Check ID-taptest_Table_p10000_p10000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p10000_p11000', 'Check ID-taptest_Table_p10000_p11000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p10000_p12000', 'Check ID-taptest_Table_p10000_p12000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p10000_p13000', 'Check ID-taptest_Table_p10000_p13000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p10000_p14000', 'Check ID-taptest_Table_p10000_p14000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p10000_p15000', 'Check ID-taptest_Table_p10000_p15000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p10000_p16000', 'Check ID-taptest_Table_p10000_p16000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p10000_p17000', 'Check ID-taptest_Table_p10000_p17000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p10000_p18000', 'Check ID-taptest_Table_p10000_p18000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p10000_p19000', 'Check ID-taptest_Table_p10000_p19000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p20000', 'Check ID-taptest_Table_p20000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p20000_p20000', 'Check ID-taptest_Table_p20000_p20000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p20000_p21000', 'Check ID-taptest_Table_p20000_p21000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p20000_p22000', 'Check ID-taptest_Table_p20000_p22000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p20000_p23000', 'Check ID-taptest_Table_p20000_p23000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p20000_p24000', 'Check ID-taptest_Table_p20000_p24000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p20000_p25000', 'Check ID-taptest_Table_p20000_p25000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p20000_p26000', 'Check ID-taptest_Table_p20000_p26000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p20000_p27000', 'Check ID-taptest_Table_p20000_p27000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p20000_p28000', 'Check ID-taptest_Table_p20000_p28000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p20000_p29000', 'Check ID-taptest_Table_p20000_p29000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p30000', 'Check ID-taptest_Table_p30000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p30000_p30000', 'Check ID-taptest_Table_p30000_p30000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p30000_p31000', 'Check ID-taptest_Table_p30000_p31000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p30000_p32000', 'Check ID-taptest_Table_p30000_p32000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p30000_p33000', 'Check ID-taptest_Table_p30000_p33000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p30000_p34000', 'Check ID-taptest_Table_p30000_p34000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p30000_p35000', 'Check ID-taptest_Table_p30000_p35000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p30000_p36000', 'Check ID-taptest_Table_p30000_p36000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p30000_p37000', 'Check ID-taptest_Table_p30000_p37000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p30000_p38000', 'Check ID-taptest_Table_p30000_p38000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p30000_p39000', 'Check ID-taptest_Table_p30000_p39000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p40000', 'Check ID-taptest_Table_p40000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p40000_p40000', 'Check ID-taptest_Table_p40000_p40000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p40000_p41000', 'Check ID-taptest_Table_p40000_p41000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p40000_p42000', 'Check ID-taptest_Table_p40000_p42000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p40000_p43000', 'Check ID-taptest_Table_p40000_p43000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p40000_p44000', 'Check ID-taptest_Table_p40000_p44000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p40000_p45000', 'Check ID-taptest_Table_p40000_p45000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p40000_p46000', 'Check ID-taptest_Table_p40000_p46000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p40000_p47000', 'Check ID-taptest_Table_p40000_p47000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p40000_p48000', 'Check ID-taptest_Table_p40000_p48000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p40000_p49000', 'Check ID-taptest_Table_p40000_p49000 does not exist'); -- Make sure 50000 are still here SELECT has_table('Partman_test', 'ID-taptest_Table_p50000', 'Check ID-taptest_Table_p50000 still exists after maintenance'); SELECT has_table('Partman_test', 'ID-taptest_Table_p50000_p50000', 'Check ID-taptest_Table_p50000_p50000 still exists after maintenance'); SELECT undo_partition_id('Partman_test.ID-taptest_Table_p50000', 20, p_keep_table := false); SELECT undo_partition_id('Partman_test.ID-taptest_Table_p60000', 20, p_keep_table := false); SELECT undo_partition_id('Partman_test.ID-taptest_Table_p70000', 20, p_keep_table := false); SELECT undo_partition_id('Partman_test.ID-taptest_Table_p80000', 20, p_keep_table := false); SELECT undo_partition_id('Partman_test.ID-taptest_Table_p90000', 20, p_keep_table := false); SELECT undo_partition_id('Partman_test.ID-taptest_Table_p100000', 20, p_keep_table := false); SELECT undo_partition_id('Partman_test.ID-taptest_Table_p110000', 20, p_keep_table := false); SELECT undo_partition_id('Partman_test.ID-taptest_Table_p120000', 20, p_keep_table := false); SELECT undo_partition_id('Partman_test.ID-taptest_Table', 20, p_keep_table := false); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p50000', 'Check ID-taptest_Table_p50000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p50000_p50000', 'Check ID-taptest_Table_p50000_p50000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p50000_p51000', 'Check ID-taptest_Table_p50000_p51000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p50000_p52000', 'Check ID-taptest_Table_p50000_p52000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p50000_p53000', 'Check ID-taptest_Table_p50000_p53000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p50000_p54000', 'Check ID-taptest_Table_p50000_p44000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p50000_p55000', 'Check ID-taptest_Table_p50000_p55000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p50000_p56000', 'Check ID-taptest_Table_p50000_p56000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p50000_p57000', 'Check ID-taptest_Table_p50000_p57000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p50000_p58000', 'Check ID-taptest_Table_p50000_p58000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p50000_p59000', 'Check ID-taptest_Table_p50000_p59000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p60000', 'Check ID-taptest_Table_p60000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p60000_p60000', 'Check ID-taptest_Table_p60000_p60000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p60000_p61000', 'Check ID-taptest_Table_p60000_p61000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p60000_p62000', 'Check ID-taptest_Table_p60000_p62000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p60000_p63000', 'Check ID-taptest_Table_p60000_p63000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p60000_p64000', 'Check ID-taptest_Table_p60000_p64000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p60000_p65000', 'Check ID-taptest_Table_p60000_p65000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p60000_p66000', 'Check ID-taptest_Table_p60000_p66000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p60000_p67000', 'Check ID-taptest_Table_p60000_p67000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p60000_p68000', 'Check ID-taptest_Table_p60000_p68000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p60000_p69000', 'Check ID-taptest_Table_p60000_p69000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p70000', 'Check ID-taptest_Table_p70000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p70000_p70000', 'Check ID-taptest_Table_p70000_p70000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p70000_p71000', 'Check ID-taptest_Table_p70000_p71000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p70000_p72000', 'Check ID-taptest_Table_p70000_p72000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p70000_p73000', 'Check ID-taptest_Table_p70000_p73000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p70000_p74000', 'Check ID-taptest_Table_p70000_p74000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p70000_p75000', 'Check ID-taptest_Table_p70000_p75000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p70000_p76000', 'Check ID-taptest_Table_p70000_p76000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p70000_p77000', 'Check ID-taptest_Table_p70000_p77000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p70000_p78000', 'Check ID-taptest_Table_p70000_p78000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p70000_p79000', 'Check ID-taptest_Table_p70000_p79000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p80000', 'Check ID-taptest_Table_p80000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p80000_p80000', 'Check ID-taptest_Table_p80000_p80000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p80000_p81000', 'Check ID-taptest_Table_p80000_p81000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p80000_p82000', 'Check ID-taptest_Table_p80000_p82000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p80000_p83000', 'Check ID-taptest_Table_p80000_p83000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p80000_p84000', 'Check ID-taptest_Table_p80000_p84000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p80000_p85000', 'Check ID-taptest_Table_p80000_p85000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p80000_p86000', 'Check ID-taptest_Table_p80000_p86000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p80000_p87000', 'Check ID-taptest_Table_p80000_p87000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p80000_p88000', 'Check ID-taptest_Table_p80000_p88000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p80000_p89000', 'Check ID-taptest_Table_p80000_p89000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p90000', 'Check ID-taptest_Table_p90000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p90000_p90000', 'Check ID-taptest_Table_p90000_p90000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p90000_p91000', 'Check ID-taptest_Table_p90000_p91000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p90000_p92000', 'Check ID-taptest_Table_p90000_p92000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p90000_p93000', 'Check ID-taptest_Table_p90000_p93000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p90000_p94000', 'Check ID-taptest_Table_p90000_p94000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p90000_p95000', 'Check ID-taptest_Table_p90000_p95000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p90000_p96000', 'Check ID-taptest_Table_p90000_p96000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p90000_p97000', 'Check ID-taptest_Table_p90000_p97000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p90000_p98000', 'Check ID-taptest_Table_p90000_p98000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p90000_p99000', 'Check ID-taptest_Table_p90000_p99000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p100000', 'Check ID-taptest_Table_p100000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p100000_p100000', 'Check ID-taptest_Table_p100000_p100000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p100000_p101000', 'Check ID-taptest_Table_p100000_p101000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p100000_p102000', 'Check ID-taptest_Table_p100000_p102000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p100000_p103000', 'Check ID-taptest_Table_p100000_p103000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p100000_p104000', 'Check ID-taptest_Table_p100000_p104000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p110000', 'Check ID-taptest_Table_p110000 does not exist'); SELECT hasnt_table('Partman_test', 'ID-taptest_Table_p120000', 'Check ID-taptest_Table_p120000 does not exist'); SELECT is_empty('SELECT * FROM part_config WHERE parent_table LIKE ''Partman_test.ID-taptest_Table''', 'Check that part_config table is empty'); SELECT * FROM finish(); ROLLBACK; pg_partman-2.2.2/test/test-id-run-maint.sql000066400000000000000000000526241262146621700206460ustar00rootroot00000000000000-- ########## ID TESTS ########## -- Other tests: Single column Foreign Key, Use run_maintenance(), with OIDs, additional constraint single column, rows that hit dynamic fallback \set ON_ERROR_ROLLBACK 1 \set ON_ERROR_STOP true BEGIN; SELECT set_config('search_path','partman, public',false); SELECT plan(138); CREATE SCHEMA partman_test; CREATE ROLE partman_basic; CREATE ROLE partman_revoke; CREATE ROLE partman_owner; CREATE TABLE partman_test.fk_test_reference (col2 text unique not null); INSERT INTO partman_test.fk_test_reference VALUES ('stuff'); CREATE TABLE partman_test.id_taptest_table ( col1 int primary key , col2 text not null default 'stuff' references partman_test.fk_test_reference (col2) ON UPDATE CASCADE DEFERRABLE INITIALLY IMMEDIATE , col3 timestamptz DEFAULT now()) WITH OIDs; INSERT INTO partman_test.id_taptest_table (col1) VALUES (generate_series(1,9)); GRANT SELECT,INSERT,UPDATE ON partman_test.id_taptest_table TO partman_basic; GRANT ALL ON partman_test.id_taptest_table TO partman_revoke; SELECT create_parent('partman_test.id_taptest_table', 'col1', 'id', '10', '{"col3"}', p_use_run_maintenance := true); SELECT has_table('partman_test', 'id_taptest_table_p0', 'Check id_taptest_table_p0 exists'); SELECT has_table('partman_test', 'id_taptest_table_p10', 'Check id_taptest_table_p10 exists'); SELECT has_table('partman_test', 'id_taptest_table_p20', 'Check id_taptest_table_p20 exists'); SELECT has_table('partman_test', 'id_taptest_table_p30', 'Check id_taptest_table_p30 exists'); SELECT has_table('partman_test', 'id_taptest_table_p40', 'Check id_taptest_table_p40 exists'); SELECT hasnt_table('partman_test', 'id_taptest_table_p50', 'Check id_taptest_table_p50 doesn''t exists yet'); SELECT col_is_pk('partman_test', 'id_taptest_table_p0', ARRAY['col1'], 'Check for primary key in id_taptest_table_p0'); SELECT col_is_pk('partman_test', 'id_taptest_table_p10', ARRAY['col1'], 'Check for primary key in id_taptest_table_p10'); SELECT col_is_pk('partman_test', 'id_taptest_table_p20', ARRAY['col1'], 'Check for primary key in id_taptest_table_p20'); SELECT col_is_pk('partman_test', 'id_taptest_table_p30', ARRAY['col1'], 'Check for primary key in id_taptest_table_p30'); SELECT col_is_pk('partman_test', 'id_taptest_table_p40', ARRAY['col1'], 'Check for primary key in id_taptest_table_p40'); SELECT table_privs_are('partman_test', 'id_taptest_table_p0', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p0'); SELECT table_privs_are('partman_test', 'id_taptest_table_p10', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p10'); SELECT table_privs_are('partman_test', 'id_taptest_table_p20', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p20'); SELECT table_privs_are('partman_test', 'id_taptest_table_p30', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p30'); SELECT table_privs_are('partman_test', 'id_taptest_table_p40', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p40'); SELECT table_privs_are('partman_test', 'id_taptest_table_p0', 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of id_taptest_table_p0'); SELECT table_privs_are('partman_test', 'id_taptest_table_p10', 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of id_taptest_table_p10'); SELECT table_privs_are('partman_test', 'id_taptest_table_p20', 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of id_taptest_table_p20'); SELECT table_privs_are('partman_test', 'id_taptest_table_p30', 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of id_taptest_table_p30'); SELECT table_privs_are('partman_test', 'id_taptest_table_p40', 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of id_taptest_table_p40'); SELECT col_is_fk('partman_test', 'id_taptest_table_p0', 'col2', 'Check that id_taptest_table_p0 inherited foreign key'); SELECT col_is_fk('partman_test', 'id_taptest_table_p10', 'col2', 'Check that id_taptest_table_p10 inherited foreign key'); SELECT col_is_fk('partman_test', 'id_taptest_table_p20', 'col2', 'Check that id_taptest_table_p20 inherited foreign key'); SELECT col_is_fk('partman_test', 'id_taptest_table_p30', 'col2', 'Check that id_taptest_table_p30 inherited foreign key'); SELECT col_is_fk('partman_test', 'id_taptest_table_p40', 'col2', 'Check that id_taptest_table_p40 inherited foreign key'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table'')::int', ARRAY[9], 'Check that partitioning function returns correct count of rows moved'); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table', 'Check that parent table has had data moved to partition'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table', ARRAY[9], 'Check count from parent table'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0', ARRAY[9], 'Check count from id_taptest_table_p0'); REVOKE INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER ON partman_test.id_taptest_table FROM partman_revoke; INSERT INTO partman_test.id_taptest_table (col1) VALUES (generate_series(10,25)); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table', 'Check that parent table has had no data inserted to it'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10', ARRAY[10], 'Check count from id_taptest_table_p10'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20', ARRAY[6], 'Check count from id_taptest_table_p20'); SELECT hasnt_table('partman_test', 'id_taptest_table_p50', 'Check id_taptest_table_p50 doesn''t exists yet'); SELECT hasnt_table('partman_test', 'id_taptest_table_p60', 'Check id_taptest_table_p60 doesn''t exists yet'); SELECT run_maintenance(); SELECT has_table('partman_test', 'id_taptest_table_p50', 'Check id_taptest_table_p50 exists'); SELECT has_table('partman_test', 'id_taptest_table_p60', 'Check id_taptest_table_p60 exists'); SELECT hasnt_table('partman_test', 'id_taptest_table_p70', 'Check id_taptest_table_p70 doesn''t exist yet'); SELECT col_is_pk('partman_test', 'id_taptest_table_p50', ARRAY['col1'], 'Check for primary key in id_taptest_table_p50'); SELECT col_is_fk('partman_test', 'id_taptest_table_p50', 'col2', 'Check that id_taptest_table_p50 inherited foreign key'); SELECT col_is_fk('partman_test', 'id_taptest_table_p60', 'col2', 'Check that id_taptest_table_p60 inherited foreign key'); SELECT table_privs_are('partman_test', 'id_taptest_table_p0', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p0'); SELECT table_privs_are('partman_test', 'id_taptest_table_p10', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p10'); SELECT table_privs_are('partman_test', 'id_taptest_table_p20', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p20'); SELECT table_privs_are('partman_test', 'id_taptest_table_p30', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p30'); SELECT table_privs_are('partman_test', 'id_taptest_table_p40', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p40'); SELECT table_privs_are('partman_test', 'id_taptest_table_p50', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p50'); SELECT table_privs_are('partman_test', 'id_taptest_table_p50', 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of id_taptest_table_p50'); GRANT DELETE ON partman_test.id_taptest_table TO partman_basic; REVOKE ALL ON partman_test.id_taptest_table FROM partman_revoke; ALTER TABLE partman_test.id_taptest_table OWNER TO partman_owner; -- Skipped 26-29 to allow dynamic fallback insertion test below INSERT INTO partman_test.id_taptest_table (col1) VALUES (generate_series(30,60)); SELECT run_maintenance(); -- Check for additional constraint on date column SELECT col_has_check('partman_test', 'id_taptest_table_p0', 'col3', 'Check for additional constraint on col3 on id_taptest_table_p0'); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table', 'Check that parent table has had no data inserted to it'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table', ARRAY[56], 'Check count from id_taptest_table'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20', ARRAY[6], 'Check count from id_taptest_table_p20'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30', ARRAY[10], 'Check count from id_taptest_table_p30'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p40', ARRAY[10], 'Check count from id_taptest_table_p40'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p50', ARRAY[10], 'Check count from id_taptest_table_p50'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60', ARRAY[1], 'Check count from id_taptest_table_p60'); SELECT has_table('partman_test', 'id_taptest_table_p60', 'Check id_taptest_table_p60 exists'); SELECT has_table('partman_test', 'id_taptest_table_p70', 'Check id_taptest_table_p70 exists'); SELECT has_table('partman_test', 'id_taptest_table_p80', 'Check id_taptest_table_p80 exists'); SELECT has_table('partman_test', 'id_taptest_table_p90', 'Check id_taptest_table_p90 exists'); SELECT has_table('partman_test', 'id_taptest_table_p100', 'Check id_taptest_table_p100 exists'); SELECT hasnt_table('partman_test', 'id_taptest_table_p110', 'Check id_taptest_table_p110 doesn''t exists yet'); SELECT col_is_pk('partman_test', 'id_taptest_table_p60', ARRAY['col1'], 'Check for primary key in id_taptest_table_p60'); SELECT col_is_pk('partman_test', 'id_taptest_table_p70', ARRAY['col1'], 'Check for primary key in id_taptest_table_p70'); SELECT col_is_pk('partman_test', 'id_taptest_table_p80', ARRAY['col1'], 'Check for primary key in id_taptest_table_p80'); SELECT col_is_pk('partman_test', 'id_taptest_table_p90', ARRAY['col1'], 'Check for primary key in id_taptest_table_p90'); SELECT col_is_pk('partman_test', 'id_taptest_table_p100', ARRAY['col1'], 'Check for primary key in id_taptest_table_p100'); SELECT col_is_fk('partman_test', 'id_taptest_table_p60', 'col2', 'Check that id_taptest_table_p60 inherited foreign key'); SELECT col_is_fk('partman_test', 'id_taptest_table_p70', 'col2', 'Check that id_taptest_table_p70 inherited foreign key'); SELECT col_is_fk('partman_test', 'id_taptest_table_p80', 'col2', 'Check that id_taptest_table_p80 inherited foreign key'); SELECT col_is_fk('partman_test', 'id_taptest_table_p90', 'col2', 'Check that id_taptest_table_p90 inherited foreign key'); SELECT col_is_fk('partman_test', 'id_taptest_table_p100', 'col2', 'Check that id_taptest_table_p100 inherited foreign key'); SELECT table_privs_are('partman_test', 'id_taptest_table_p0', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p0'); SELECT table_privs_are('partman_test', 'id_taptest_table_p10', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p10'); SELECT table_privs_are('partman_test', 'id_taptest_table_p20', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p20'); SELECT table_privs_are('partman_test', 'id_taptest_table_p30', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p30'); SELECT table_privs_are('partman_test', 'id_taptest_table_p40', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p40'); SELECT table_privs_are('partman_test', 'id_taptest_table_p50', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p50'); SELECT table_privs_are('partman_test', 'id_taptest_table_p50', 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of id_taptest_table_p50'); SELECT table_privs_are('partman_test', 'id_taptest_table_p60', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p60'); SELECT table_privs_are('partman_test', 'id_taptest_table_p60', 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of id_taptest_table_p60'); SELECT table_privs_are('partman_test', 'id_taptest_table_p70', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE','DELETE'], 'Check partman_basic privileges of id_taptest_table_p70'); SELECT table_owner_is ('partman_test', 'id_taptest_table_p70', 'partman_owner', 'Check that ownership change worked for id_taptest_table_p70'); SELECT table_privs_are('partman_test', 'id_taptest_table_p70', 'partman_revoke', '{}'::text[], 'Check partman_revoke has no privileges on id_taptest_table_p70'); SELECT table_privs_are('partman_test', 'id_taptest_table_p80', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE','DELETE'], 'Check partman_basic privileges of id_taptest_table_p80'); SELECT table_owner_is ('partman_test', 'id_taptest_table_p80', 'partman_owner', 'Check that ownership change worked for id_taptest_table_p80'); SELECT table_privs_are('partman_test', 'id_taptest_table_p80', 'partman_revoke', '{}'::text[], 'Check partman_revoke has no privileges on id_taptest_table_p80'); SELECT table_privs_are('partman_test', 'id_taptest_table_p90', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE','DELETE'], 'Check partman_basic privileges of id_taptest_table_p90'); SELECT table_owner_is ('partman_test', 'id_taptest_table_p90', 'partman_owner', 'Check that ownership change worked for id_taptest_table_p90'); SELECT table_privs_are('partman_test', 'id_taptest_table_p90', 'partman_revoke', '{}'::text[], 'Check partman_revoke has no privileges on id_taptest_table_p90'); SELECT table_privs_are('partman_test', 'id_taptest_table_p100', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE','DELETE'], 'Check partman_basic privileges of id_taptest_table_p100'); SELECT table_owner_is ('partman_test', 'id_taptest_table_p100', 'partman_owner', 'Check that ownership change worked for id_taptest_table_p100'); SELECT table_privs_are('partman_test', 'id_taptest_table_p100', 'partman_revoke', '{}'::text[], 'Check partman_revoke has no privileges on id_taptest_table_p100'); -- Test for dynamic fallback insertion (static test should now cover 60-110) INSERT INTO partman_test.id_taptest_table (col1) VALUES (generate_series(26,29)); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table', 'Check that parent table has had no data inserted to it after dynamic fallback'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table', ARRAY[60], 'Check count from id_taptest_table after dynamic fallback'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20', ARRAY[10], 'Check count from id_taptest_table_p20 after dynamic fallback'); INSERT INTO partman_test.id_taptest_table (col1) VALUES (generate_series(200,210)); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table', ARRAY[11], 'Check that data outside trigger scope goes to parent'); SELECT reapply_privileges('partman_test.id_taptest_table'); SELECT table_privs_are('partman_test', 'id_taptest_table_p0', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE','DELETE'], 'Check partman_basic privileges of id_taptest_table_p0'); SELECT table_privs_are('partman_test', 'id_taptest_table_p10', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE','DELETE'], 'Check partman_basic privileges of id_taptest_table_p10'); SELECT table_privs_are('partman_test', 'id_taptest_table_p20', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE','DELETE'], 'Check partman_basic privileges of id_taptest_table_p20'); SELECT table_privs_are('partman_test', 'id_taptest_table_p30', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE','DELETE'], 'Check partman_basic privileges of id_taptest_table_p30'); SELECT table_privs_are('partman_test', 'id_taptest_table_p40', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE','DELETE'], 'Check partman_basic privileges of id_taptest_table_p40'); SELECT table_privs_are('partman_test', 'id_taptest_table_p50', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE','DELETE'], 'Check partman_basic privileges of id_taptest_table_p50'); SELECT table_privs_are('partman_test', 'id_taptest_table_p60', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE','DELETE'], 'Check partman_basic privileges of id_taptest_table_p60'); SELECT table_privs_are('partman_test', 'id_taptest_table_p70', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE','DELETE'], 'Check partman_basic privileges of id_taptest_table_p70'); SELECT table_privs_are('partman_test', 'id_taptest_table_p0', 'partman_revoke', '{}'::text[], 'Check partman_revoke has no privileges on id_taptest_table_p0'); SELECT table_privs_are('partman_test', 'id_taptest_table_p10', 'partman_revoke', '{}'::text[], 'Check partman_revoke has no privileges on id_taptest_table_p10'); SELECT table_privs_are('partman_test', 'id_taptest_table_p20', 'partman_revoke', '{}'::text[], 'Check partman_revoke has no privileges on id_taptest_table_p20'); SELECT table_privs_are('partman_test', 'id_taptest_table_p30', 'partman_revoke', '{}'::text[], 'Check partman_revoke has no privileges on id_taptest_table_p30'); SELECT table_privs_are('partman_test', 'id_taptest_table_p40', 'partman_revoke', '{}'::text[], 'Check partman_revoke has no privileges on id_taptest_table_p40'); SELECT table_privs_are('partman_test', 'id_taptest_table_p50', 'partman_revoke', '{}'::text[], 'Check partman_revoke has no privileges on id_taptest_table_p50'); SELECT table_privs_are('partman_test', 'id_taptest_table_p60', 'partman_revoke', '{}'::text[], 'Check partman_revoke has no privileges on id_taptest_table_p60'); SELECT table_privs_are('partman_test', 'id_taptest_table_p70', 'partman_revoke', '{}'::text[], 'Check partman_revoke has no privileges on id_taptest_table_p70'); SELECT table_owner_is ('partman_test', 'id_taptest_table_p0', 'partman_owner', 'Check that ownership change worked for id_taptest_table_p0'); SELECT table_owner_is ('partman_test', 'id_taptest_table_p10', 'partman_owner', 'Check that ownership change worked for id_taptest_table_p10'); SELECT table_owner_is ('partman_test', 'id_taptest_table_p20', 'partman_owner', 'Check that ownership change worked for id_taptest_table_p20'); SELECT table_owner_is ('partman_test', 'id_taptest_table_p30', 'partman_owner', 'Check that ownership change worked for id_taptest_table_p30'); SELECT table_owner_is ('partman_test', 'id_taptest_table_p40', 'partman_owner', 'Check that ownership change worked for id_taptest_table_p40'); SELECT table_owner_is ('partman_test', 'id_taptest_table_p50', 'partman_owner', 'Check that ownership change worked for id_taptest_table_p50'); SELECT table_owner_is ('partman_test', 'id_taptest_table_p60', 'partman_owner', 'Check that ownership change worked for id_taptest_table_p60'); SELECT table_owner_is ('partman_test', 'id_taptest_table_p70', 'partman_owner', 'Check that ownership change worked for id_taptest_table_p70'); SELECT undo_partition_id('partman_test.id_taptest_table', 10); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table', ARRAY[71], 'Check count from parent table after undo'); SELECT has_table('partman_test', 'id_taptest_table_p0', 'Check id_taptest_table_p0 still exists'); SELECT is_empty('SELECT * FROM partman_test.id_taptest_table_p0', 'Check child table had its data removed id_taptest_table_p0'); SELECT has_table('partman_test', 'id_taptest_table_p10', 'Check id_taptest_table_p10 still exists'); SELECT is_empty('SELECT * FROM partman_test.id_taptest_table_p10', 'Check child table had its data removed id_taptest_table_p10'); SELECT has_table('partman_test', 'id_taptest_table_p20', 'Check id_taptest_table_p20 still exists'); SELECT is_empty('SELECT * FROM partman_test.id_taptest_table_p20', 'Check child table had its data removed id_taptest_table_p20'); SELECT has_table('partman_test', 'id_taptest_table_p30', 'Check id_taptest_table_p30 still exists'); SELECT is_empty('SELECT * FROM partman_test.id_taptest_table_p30', 'Check child table had its data removed id_taptest_table_p30'); SELECT has_table('partman_test', 'id_taptest_table_p40', 'Check id_taptest_table_p40 still exists'); SELECT is_empty('SELECT * FROM partman_test.id_taptest_table_p40', 'Check child table had its data removed id_taptest_table_p40'); SELECT has_table('partman_test', 'id_taptest_table_p50', 'Check id_taptest_table_p50 still exists'); SELECT is_empty('SELECT * FROM partman_test.id_taptest_table_p50', 'Check child table had its data removed id_taptest_table_p50'); SELECT has_table('partman_test', 'id_taptest_table_p60', 'Check id_taptest_table_p60 still exists'); SELECT is_empty('SELECT * FROM partman_test.id_taptest_table_p60', 'Check child table had its data removed id_taptest_table_p60'); SELECT has_table('partman_test', 'id_taptest_table_p70', 'Check id_taptest_table_p70 still exists'); SELECT is_empty('SELECT * FROM partman_test.id_taptest_table_p70', 'Check child table had its data removed id_taptest_table_p70'); SELECT * FROM finish(); ROLLBACK; pg_partman-2.2.2/test/test-id-start-100.sql000066400000000000000000000273131262146621700203640ustar00rootroot00000000000000-- ########## ID TESTS ########## -- Other tests: additional constraints multi column with update, start @ value of 100 \set ON_ERROR_ROLLBACK 1 \set ON_ERROR_STOP true BEGIN; SELECT set_config('search_path','partman, public',false); SELECT plan(83); CREATE SCHEMA partman_test; CREATE ROLE partman_basic; CREATE ROLE partman_owner; CREATE TABLE partman_test.id_taptest_table (col1 int primary key, col2 text, col3 timestamptz DEFAULT now()); INSERT INTO partman_test.id_taptest_table (col1, col2) VALUES (generate_series(100,109), 'stuff'||generate_series(100,109)); GRANT SELECT,INSERT,UPDATE ON partman_test.id_taptest_table TO partman_basic; SELECT create_parent('partman_test.id_taptest_table', 'col1', 'id', '10'); -- Default optimize_constraint is 30, so set it equal to premake for when this test was originally written UPDATE partman.part_config SET constraint_cols = '{"col2", "col3"}', optimize_constraint = 4 WHERE parent_table = 'partman_test.id_taptest_table'; SELECT has_table('partman_test', 'id_taptest_table_p100', 'Check id_taptest_table_p100 exists'); SELECT has_table('partman_test', 'id_taptest_table_p110', 'Check id_taptest_table_p110 exists'); SELECT has_table('partman_test', 'id_taptest_table_p120', 'Check id_taptest_table_p120 exists'); SELECT has_table('partman_test', 'id_taptest_table_p130', 'Check id_taptest_table_p130 exists'); SELECT has_table('partman_test', 'id_taptest_table_p140', 'Check id_taptest_table_p140 exists'); SELECT has_table('partman_test', 'id_taptest_table_p90', 'Check id_taptest_table_p90 exists'); SELECT has_table('partman_test', 'id_taptest_table_p80', 'Check id_taptest_table_p80 exists'); SELECT has_table('partman_test', 'id_taptest_table_p70', 'Check id_taptest_table_p70 exists'); SELECT has_table('partman_test', 'id_taptest_table_p60', 'Check id_taptest_table_p60 exists'); SELECT hasnt_table('partman_test', 'id_taptest_table_p50', 'Check id_taptest_table_p50 doesn''t exists yet'); SELECT hasnt_table('partman_test', 'id_taptest_table_p150', 'Check id_taptest_table_p150 doesn''t exists yet'); SELECT col_is_pk('partman_test', 'id_taptest_table_p100', ARRAY['col1'], 'Check for primary key in id_taptest_table_p100'); SELECT col_is_pk('partman_test', 'id_taptest_table_p110', ARRAY['col1'], 'Check for primary key in id_taptest_table_p110'); SELECT col_is_pk('partman_test', 'id_taptest_table_p120', ARRAY['col1'], 'Check for primary key in id_taptest_table_p120'); SELECT col_is_pk('partman_test', 'id_taptest_table_p130', ARRAY['col1'], 'Check for primary key in id_taptest_table_p130'); SELECT col_is_pk('partman_test', 'id_taptest_table_p140', ARRAY['col1'], 'Check for primary key in id_taptest_table_p140'); SELECT col_is_pk('partman_test', 'id_taptest_table_p90', ARRAY['col1'], 'Check for primary key in id_taptest_table_p90'); SELECT col_is_pk('partman_test', 'id_taptest_table_p80', ARRAY['col1'], 'Check for primary key in id_taptest_table_p80'); SELECT col_is_pk('partman_test', 'id_taptest_table_p70', ARRAY['col1'], 'Check for primary key in id_taptest_table_p70'); SELECT col_is_pk('partman_test', 'id_taptest_table_p60', ARRAY['col1'], 'Check for primary key in id_taptest_table_p60'); SELECT table_privs_are('partman_test', 'id_taptest_table_p100', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p100'); SELECT table_privs_are('partman_test', 'id_taptest_table_p110', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p110'); SELECT table_privs_are('partman_test', 'id_taptest_table_p120', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p120'); SELECT table_privs_are('partman_test', 'id_taptest_table_p130', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p130'); SELECT table_privs_are('partman_test', 'id_taptest_table_p140', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p140'); SELECT table_privs_are('partman_test', 'id_taptest_table_p90', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p90'); SELECT table_privs_are('partman_test', 'id_taptest_table_p80', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p80'); SELECT table_privs_are('partman_test', 'id_taptest_table_p70', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p70'); SELECT table_privs_are('partman_test', 'id_taptest_table_p60', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p60'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table'')::int', ARRAY[10], 'Check that partitioning function returns correct count of rows moved'); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table', 'Check that parent table has had data moved to partition'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table', ARRAY[10], 'Check count from parent table'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100', ARRAY[10], 'Check count from id_taptest_table_p100'); INSERT INTO partman_test.id_taptest_table (col1, col2) VALUES (generate_series(60,99), 'stuff'||generate_series(60,99)); INSERT INTO partman_test.id_taptest_table (col1, col2) VALUES (generate_series(110,145), 'stuff'||generate_series(110,145)); -- Check for additional constraints on text & date columns SELECT col_has_check('partman_test', 'id_taptest_table_p60', 'col2', 'Check for additional constraint on col2 on id_taptest_table_p60'); SELECT col_has_check('partman_test', 'id_taptest_table_p60', 'col3', 'Check for additional constraint on col3 on id_taptest_table_p60'); SELECT col_has_check('partman_test', 'id_taptest_table_p70', 'col2', 'Check for additional constraint on col2 on id_taptest_table_p70'); SELECT col_has_check('partman_test', 'id_taptest_table_p70', 'col3', 'Check for additional constraint on col3 on id_taptest_table_p70'); SELECT col_has_check('partman_test', 'id_taptest_table_p80', 'col2', 'Check for additional constraint on col2 on id_taptest_table_p80'); SELECT col_has_check('partman_test', 'id_taptest_table_p80', 'col3', 'Check for additional constraint on col3 on id_taptest_table_p80'); SELECT has_table('partman_test', 'id_taptest_table_p150', 'Check id_taptest_table_p150 exists'); SELECT has_table('partman_test', 'id_taptest_table_p160', 'Check id_taptest_table_p160 exists'); SELECT has_table('partman_test', 'id_taptest_table_p170', 'Check id_taptest_table_p170 exists'); SELECT hasnt_table('partman_test', 'id_taptest_table_p50', 'Check id_taptest_table_p180 doesn''t exists yet'); SELECT col_is_pk('partman_test', 'id_taptest_table_p150', ARRAY['col1'], 'Check for primary key in id_taptest_table_p150'); SELECT col_is_pk('partman_test', 'id_taptest_table_p160', ARRAY['col1'], 'Check for primary key in id_taptest_table_p160'); SELECT col_is_pk('partman_test', 'id_taptest_table_p170', ARRAY['col1'], 'Check for primary key in id_taptest_table_p170'); SELECT table_privs_are('partman_test', 'id_taptest_table_p150', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p150'); SELECT table_privs_are('partman_test', 'id_taptest_table_p160', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p160'); SELECT table_privs_are('partman_test', 'id_taptest_table_p170', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p170'); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table', 'Check that parent table has had no data inserted to it'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table', ARRAY[86], 'Check count from parent table'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60', ARRAY[10], 'Check count from id_taptest_table_p60'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70', ARRAY[10], 'Check count from id_taptest_table_p70'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80', ARRAY[10], 'Check count from id_taptest_table_p80'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90', ARRAY[10], 'Check count from id_taptest_table_p90'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100', ARRAY[10], 'Check count from id_taptest_table_p100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p110', ARRAY[10], 'Check count from id_taptest_table_p110'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p120', ARRAY[10], 'Check count from id_taptest_table_p120'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p130', ARRAY[10], 'Check count from id_taptest_table_p130'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p140', ARRAY[6], 'Check count from id_taptest_table_p140'); SELECT is_empty('SELECT * FROM partman_test.id_taptest_table_p150', 'Check that next is empty'); SELECT undo_partition('partman_test.id_taptest_table', 20); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table', ARRAY[86], 'Check count from parent table after undo'); SELECT has_table('partman_test', 'id_taptest_table_p60', 'Check id_taptest_table_p60 still exists'); SELECT has_table('partman_test', 'id_taptest_table_p70', 'Check id_taptest_table_p70 still exists'); SELECT has_table('partman_test', 'id_taptest_table_p80', 'Check id_taptest_table_p80 still exists'); SELECT has_table('partman_test', 'id_taptest_table_p90', 'Check id_taptest_table_p90 still exists'); SELECT has_table('partman_test', 'id_taptest_table_p100', 'Check id_taptest_table_p100 still exists'); SELECT has_table('partman_test', 'id_taptest_table_p110', 'Check id_taptest_table_p110 still exists'); SELECT has_table('partman_test', 'id_taptest_table_p120', 'Check id_taptest_table_p120 still exists'); SELECT has_table('partman_test', 'id_taptest_table_p130', 'Check id_taptest_table_p130 still exists'); SELECT has_table('partman_test', 'id_taptest_table_p140', 'Check id_taptest_table_p140 still exists'); SELECT has_table('partman_test', 'id_taptest_table_p150', 'Check id_taptest_table_p140 still exists'); SELECT has_table('partman_test', 'id_taptest_table_p160', 'Check id_taptest_table_p140 still exists'); SELECT has_table('partman_test', 'id_taptest_table_p170', 'Check id_taptest_table_p140 still exists'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p60', ARRAY[10], 'Check count from id_taptest_table_p60'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p70', ARRAY[10], 'Check count from id_taptest_table_p70'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p80', ARRAY[10], 'Check count from id_taptest_table_p80'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p90', ARRAY[10], 'Check count from id_taptest_table_p90'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p100', ARRAY[10], 'Check count from id_taptest_table_p100'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p110', ARRAY[10], 'Check count from id_taptest_table_p110'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p120', ARRAY[10], 'Check count from id_taptest_table_p120'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p130', ARRAY[10], 'Check count from id_taptest_table_p130'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p140', ARRAY[6], 'Check count from id_taptest_table_p140'); SELECT * FROM finish(); ROLLBACK; pg_partman-2.2.2/test/test-id-start-partition.sql000066400000000000000000000153751262146621700221020ustar00rootroot00000000000000-- ########## ID TESTS ########## -- Test p_start_partition parameter, mixed case/special char test \set ON_ERROR_ROLLBACK 1 \set ON_ERROR_STOP true BEGIN; SELECT set_config('search_path','partman, public',false); SELECT plan(46); CREATE SCHEMA partman_test; CREATE SCHEMA partman_retention_test; CREATE TABLE partman_test.id_taptest_table (col1 int primary key, col2 text, col3 timestamptz DEFAULT now()); INSERT INTO partman_test.id_taptest_table (col1) VALUES (generate_series(151,159)); SELECT create_parent('partman_test.id_taptest_table', 'col1', 'id', '10', p_start_partition := '150'); SELECT has_table('partman_test', 'id_taptest_table_p150', 'Check id_taptest_table_p150 exists'); SELECT has_table('partman_test', 'id_taptest_table_p160', 'Check id_taptest_table_p160 exists'); SELECT has_table('partman_test', 'id_taptest_table_p170', 'Check id_taptest_table_p170 exists'); SELECT has_table('partman_test', 'id_taptest_table_p180', 'Check id_taptest_table_p180 exists'); SELECT has_table('partman_test', 'id_taptest_table_p190', 'Check id_taptest_table_p190 exists'); SELECT hasnt_table('partman_test', 'id_taptest_table_p200', 'Check id_taptest_table_p200 doesn''t exists yet'); SELECT col_is_pk('partman_test', 'id_taptest_table_p150', ARRAY['col1'], 'Check for primary key in id_taptest_table_p150'); SELECT col_is_pk('partman_test', 'id_taptest_table_p160', ARRAY['col1'], 'Check for primary key in id_taptest_table_p160'); SELECT col_is_pk('partman_test', 'id_taptest_table_p170', ARRAY['col1'], 'Check for primary key in id_taptest_table_p170'); SELECT col_is_pk('partman_test', 'id_taptest_table_p180', ARRAY['col1'], 'Check for primary key in id_taptest_table_p180'); SELECT col_is_pk('partman_test', 'id_taptest_table_p190', ARRAY['col1'], 'Check for primary key in id_taptest_table_p190'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table'')::int', ARRAY[9], 'Check that partitioning function returns correct count of rows moved'); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table', 'Check that parent table has had data moved to partition'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table', ARRAY[9], 'Check count from parent table'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p150', ARRAY[9], 'Check count from id_taptest_table_p150'); INSERT INTO partman_test.id_taptest_table (col1) VALUES (generate_series(160,175)); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table', 'Check that parent table has had no data inserted to it'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p160', ARRAY[10], 'Check count from id_taptest_table_p160'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p170', ARRAY[6], 'Check count from id_taptest_table_p170'); SELECT has_table('partman_test', 'id_taptest_table_p200', 'Check id_taptest_table_p200 exists'); SELECT hasnt_table('partman_test', 'id_taptest_table_p210', 'Check id_taptest_table_p210 doesn''t exists yet'); SELECT col_is_pk('partman_test', 'id_taptest_table_p200', ARRAY['col1'], 'Check for primary key in id_taptest_table_p200'); INSERT INTO partman_test.id_taptest_table (col1) VALUES (generate_series(176,205)); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table', 'Check that parent table has had no data inserted to it'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table', ARRAY[55], 'Check count from parent table'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p170', ARRAY[10], 'Check count from id_taptest_table_p170'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p180', ARRAY[10], 'Check count from id_taptest_table_p180'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p190', ARRAY[10], 'Check count from id_taptest_table_p190'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p200', ARRAY[6], 'Check count from id_taptest_table_p200'); SELECT has_table('partman_test', 'id_taptest_table_p210', 'Check id_taptest_table_p210 exists'); SELECT has_table('partman_test', 'id_taptest_table_p220', 'Check id_taptest_table_p220 exists'); SELECT has_table('partman_test', 'id_taptest_table_p230', 'Check id_taptest_table_p230 exists'); SELECT hasnt_table('partman_test', 'id_taptest_table_p240', 'Check id_taptest_table_p240 doesn''t exists yet'); SELECT col_is_pk('partman_test', 'id_taptest_table_p210', ARRAY['col1'], 'Check for primary key in id_taptest_table_p210'); SELECT col_is_pk('partman_test', 'id_taptest_table_p220', ARRAY['col1'], 'Check for primary key in id_taptest_table_p220'); SELECT col_is_pk('partman_test', 'id_taptest_table_p230', ARRAY['col1'], 'Check for primary key in id_taptest_table_p230'); INSERT INTO partman_test.id_taptest_table (col1) VALUES (generate_series(300,310)); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table', ARRAY[11], 'Check that data outside trigger scope goes to parent'); -- Remove rows in parent so drop function test can work properly DELETE FROM ONLY partman_test.id_taptest_table; SELECT drop_partition_id('partman_test.id_taptest_table', '45', p_keep_table := false); SELECT hasnt_table('partman_test', 'id_taptest_table_p150', 'Check id_taptest_table_p150 doesn''t exists anymore'); UPDATE part_config SET retention = '35' WHERE parent_table = 'partman_test.id_taptest_table'; SELECT drop_partition_id('partman_test.id_taptest_table', p_retention_schema := 'partman_retention_test'); SELECT hasnt_table('partman_test', 'id_taptest_table_p160', 'Check id_taptest_table_p160 doesn''t exists anymore'); SELECT has_table('partman_retention_test', 'id_taptest_table_p160', 'Check id_taptest_table_p160 got moved to new schema'); SELECT undo_partition_id('partman_test.id_taptest_table', 10, p_keep_table := false); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table', ARRAY[36], 'Check count from parent table after undo'); SELECT hasnt_table('partman_test', 'id_taptest_table_p170', 'Check id_taptest_table_p170 doesn''t exists anymore'); SELECT hasnt_table('partman_test', 'id_taptest_table_p180', 'Check id_taptest_table_p180 doesn''t exists anymore'); SELECT hasnt_table('partman_test', 'id_taptest_table_p190', 'Check id_taptest_table_p190 doesn''t exists anymore'); SELECT hasnt_table('partman_test', 'id_taptest_table_p200', 'Check id_taptest_table_p200 doesn''t exists anymore'); SELECT hasnt_table('partman_test', 'id_taptest_table_p210', 'Check id_taptest_table_p210 doesn''t exists anymore'); SELECT hasnt_table('partman_test', 'id_taptest_table_p220', 'Check id_taptest_table_p220 doesn''t exists anymore'); SELECT hasnt_table('partman_test', 'id_taptest_table_p230', 'Check id_taptest_table_p230 doesn''t exists anymore'); SELECT * FROM finish(); ROLLBACK; pg_partman-2.2.2/test/test-id-time-subpart.sql000066400000000000000000001574631262146621700213570ustar00rootroot00000000000000-- ########## ID PARENT / TIME SUBPARENT DYNAMIC TESTS ########## \set ON_ERROR_ROLLBACK 1 \set ON_ERROR_STOP true BEGIN; SELECT set_config('search_path','partman, public',false); SELECT plan(258); CREATE SCHEMA partman_test; CREATE TABLE partman_test.id_taptest_table (col1 int primary key, col2 text, col3 timestamptz NOT NULL DEFAULT now()); INSERT INTO partman_test.id_taptest_table (col1) VALUES (generate_series(1,9)); SELECT partman.create_parent('partman_test.id_taptest_table', 'col1', 'id', '10', '{"col3"}', p_use_run_maintenance := true, p_jobmon := false); SELECT has_table('partman_test', 'id_taptest_table_p0', 'Check id_taptest_table_p0 exists'); SELECT has_table('partman_test', 'id_taptest_table_p10', 'Check id_taptest_table_p10 exists'); SELECT has_table('partman_test', 'id_taptest_table_p20', 'Check id_taptest_table_p20 exists'); SELECT has_table('partman_test', 'id_taptest_table_p30', 'Check id_taptest_table_p30 exists'); SELECT has_table('partman_test', 'id_taptest_table_p40', 'Check id_taptest_table_p40 exists'); SELECT hasnt_table('partman_test', 'id_taptest_table_p50', 'Check id_taptest_table_p50 doesn''t exists yet'); SELECT col_is_pk('partman_test', 'id_taptest_table_p0', ARRAY['col1'], 'Check for primary key in id_taptest_table_p0'); SELECT col_is_pk('partman_test', 'id_taptest_table_p10', ARRAY['col1'], 'Check for primary key in id_taptest_table_p10'); SELECT col_is_pk('partman_test', 'id_taptest_table_p20', ARRAY['col1'], 'Check for primary key in id_taptest_table_p20'); SELECT col_is_pk('partman_test', 'id_taptest_table_p30', ARRAY['col1'], 'Check for primary key in id_taptest_table_p30'); SELECT col_is_pk('partman_test', 'id_taptest_table_p40', ARRAY['col1'], 'Check for primary key in id_taptest_table_p40'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table'')::int', ARRAY[9], 'Check that partitioning function returns correct count of rows moved'); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table', 'Check that parent table has had data moved to partition'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table', ARRAY[9], 'Check count from parent table'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0', ARRAY[9], 'Check count from id_taptest_table_p0'); -- Create subpartition SELECT partman.create_sub_parent('partman_test.id_taptest_table', 'col3', 'time', 'daily'); -- p0 SELECT has_table('partman_test', 'id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'Check id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT hasnt_table('partman_test', 'id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT has_table('partman_test', 'id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT hasnt_table('partman_test', 'id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP-'5 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP-'5 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT col_is_pk('partman_test', 'id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in d_dynamic_table_p0_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); -- Move data to new subpartitions SELECT results_eq('SELECT partition_data_time(''partman_test.id_taptest_table_p0'', p_batch_count := 5)::int', ARRAY[9], 'Check that partitioning function returns correct count of rows moved'); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table', 'Check that parent table has had data moved to partition'); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p0', 'Check that subparent table has had data moved to partition'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0', ARRAY[9], 'Check count from parent table'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), ARRAY[9], 'Check count from id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT is_empty('SELECT * FROM partman_test.id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'Check count from id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||' (should be empty)'); -- p10 SELECT has_table('partman_test', 'id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'Check id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT hasnt_table('partman_test', 'id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT has_table('partman_test', 'id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT hasnt_table('partman_test', 'id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP-'5 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP-'5 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT col_is_pk('partman_test', 'id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in d_dynamic_table_p10_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p10', 'Check that subparent table has had data moved to partition'); SELECT is_empty('SELECT * FROM partman_test.id_taptest_table_p10', 'Check count from parent table _p10 (should be empty)'); -- p20 SELECT has_table('partman_test', 'id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'Check id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT hasnt_table('partman_test', 'id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT has_table('partman_test', 'id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT hasnt_table('partman_test', 'id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP-'5 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP-'5 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT col_is_pk('partman_test', 'id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in d_dynamic_table_p20_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p20', 'Check that subparent table itself _p20 is empty'); SELECT is_empty('SELECT * FROM partman_test.id_taptest_table_p20', 'Check count from parent table _p20 (should be empty)'); -- p30 SELECT has_table('partman_test', 'id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'Check id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT hasnt_table('partman_test', 'id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT has_table('partman_test', 'id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT hasnt_table('partman_test', 'id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP-'5 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP-'5 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT col_is_pk('partman_test', 'id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in d_dynamic_table_p30_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p30', 'Check that subparent table itself _p30 is empty'); SELECT is_empty('SELECT * FROM partman_test.id_taptest_table_p30', 'Check count from parent table _p30 (should be empty)'); -- p40 SELECT has_table('partman_test', 'id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'Check id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT hasnt_table('partman_test', 'id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT has_table('partman_test', 'id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT hasnt_table('partman_test', 'id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP-'5 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP-'5 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT col_is_pk('partman_test', 'id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in d_dynamic_table_p40_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p40', 'Check that subparent table itself _p40 is empty'); SELECT is_empty('SELECT * FROM partman_test.id_taptest_table_p40', 'Check count from parent table _p40 (should be empty)'); -- insertion round 2 INSERT INTO partman_test.id_taptest_table (col1, col3) VALUES (generate_series(10,20), CURRENT_TIMESTAMP+'1 day'::interval); SELECT run_maintenance(); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table', 'Check that parent table has had data moved to partition'); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p0', 'Check that subparent table has had data moved to partition'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0', ARRAY[9], 'Check count from parent table partman_test.id_taptest_table_p0'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), ARRAY[9], 'Check count from id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p10', 'Check that subparent table has had data moved to partition'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10', ARRAY[10], 'Check count from parent table partman_test.id_taptest_table_p10'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), ARRAY[10], 'Check count from id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p20', 'Check that subparent table has had data moved to partition'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20', ARRAY[1], 'Check count from parent table partman_test.id_taptest_table_p20'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), ARRAY[1], 'Check count from id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); -- p50 SELECT has_table('partman_test', 'id_taptest_table_p50', 'Check id_taptest_table_p50 exists'); SELECT col_is_pk('partman_test', 'id_taptest_table_p50', ARRAY['col1'], 'Check for primary key in id_taptest_table_p50'); SELECT has_table('partman_test', 'id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'Check id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT hasnt_table('partman_test', 'id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT has_table('partman_test', 'id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT hasnt_table('partman_test', 'id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP-'5 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP-'5 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT col_is_pk('partman_test', 'id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in d_dynamic_table_p50_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table', 'Check that parent table has had data moved to partition'); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p50', 'Check that subparent table itself _p50 is empty'); SELECT is_empty('SELECT * FROM partman_test.id_taptest_table_p50', 'Check count from parent table _p50 (should be empty)'); -- p60 SELECT has_table('partman_test', 'id_taptest_table_p60', 'Check id_taptest_table_p60 exists'); SELECT col_is_pk('partman_test', 'id_taptest_table_p60', ARRAY['col1'], 'Check for primary key in id_taptest_table_p60'); SELECT has_table('partman_test', 'id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'Check id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT hasnt_table('partman_test', 'id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT has_table('partman_test', 'id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT hasnt_table('partman_test', 'id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP-'5 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP-'5 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT col_is_pk('partman_test', 'id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in d_dynamic_table_p60_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table', 'Check that parent table has had data moved to partition'); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p60', 'Check that subparent table itself _p60 is empty'); SELECT is_empty('SELECT * FROM partman_test.id_taptest_table_p60', 'Check count from parent table _p60 (should be empty)'); SELECT hasnt_table('partman_test', 'id_taptest_table_p70', 'Check id_taptest_table_p70 doesn''t exists yet'); -- make sure new data is where it should be SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_p10', 'Check count from only parent table _p10 (should be empty)'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10', ARRAY[10], 'Check count from subparent table id_taptest_table_p10'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), ARRAY[10], 'Check count from id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20', ARRAY[1], 'Check count from subparent table id_taptest_table_p20'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), ARRAY[1], 'Check count from id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); -- Ensure time partitioning works for all sub partitions UPDATE part_config SET premake = 5, optimize_trigger = 5 WHERE parent_table ~ 'partman_test.id_taptest_table_p' AND partition_type = 'time'; SELECT run_maintenance(); -- Check for new time sub-partitions -- Time data only exists for now() in _p0, so only one additional table will be created SELECT has_table('partman_test', 'id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT hasnt_table('partman_test', 'id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT col_is_pk('partman_test', 'id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')); -- Time data exists for now+1 day in p10, so 2 additional tables will be created SELECT has_table('partman_test', 'id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT hasnt_table('partman_test', 'id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT col_is_pk('partman_test', 'id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')); -- Time data exists for now+1 day in p20, so 2 additional tables will be created SELECT has_table('partman_test', 'id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT hasnt_table('partman_test', 'id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT col_is_pk('partman_test', 'id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')); -- No time data has been inserted for p30 and higher, so no more partitions should be created for them SELECT hasnt_table('partman_test', 'id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' does not exist'); -- Test dropping without retention set SELECT drop_partition_time ('partman_test.id_taptest_table_p0', '2 days', p_keep_table := false); SELECT hasnt_table('partman_test', 'id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP-'5 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP-'5 days'::interval, 'YYYY_MM_DD')||' does not exist'); UPDATE part_config SET retention = '10', retention_keep_table = false WHERE parent_table = 'partman_test.id_taptest_table'; UPDATE part_config SET retention = '2 days', retention_keep_table = false WHERE parent_table = 'partman_test.id_taptest_table_p0'; UPDATE part_config SET retention = '2 days', retention_keep_table = false WHERE parent_table = 'partman_test.id_taptest_table_p10'; UPDATE part_config SET retention = '2 days', retention_keep_table = false WHERE parent_table = 'partman_test.id_taptest_table_p20'; UPDATE part_config SET retention = '2 days', retention_keep_table = false WHERE parent_table = 'partman_test.id_taptest_table_p30'; UPDATE part_config SET retention = '2 days', retention_keep_table = false WHERE parent_table = 'partman_test.id_taptest_table_p40'; UPDATE part_config SET retention = '2 days', retention_keep_table = false WHERE parent_table = 'partman_test.id_taptest_table_p50'; UPDATE part_config SET retention = '2 days', retention_keep_table = false WHERE parent_table = 'partman_test.id_taptest_table_p60'; -- Test dropping with it set SELECT run_maintenance(); SELECT hasnt_table('partman_test', 'id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'Check id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP-'1 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP-'1 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p0_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP-'5 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP-'5 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP-'5 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP-'5 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP-'5 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP-'5 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP-'5 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP-'5 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP-'5 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP-'5 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP-'5 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP-'5 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT undo_partition_time('partman_test.id_taptest_table_p10', 20, p_keep_table := false); SELECT undo_partition_time('partman_test.id_taptest_table_p20', 20, p_keep_table := false); SELECT undo_partition_time('partman_test.id_taptest_table_p30', 20, p_keep_table := false); SELECT undo_partition_time('partman_test.id_taptest_table_p40', 20, p_keep_table := false); SELECT undo_partition_time('partman_test.id_taptest_table_p50', 20, p_keep_table := false); SELECT undo_partition_time('partman_test.id_taptest_table_p60', 20, p_keep_table := false); SELECT hasnt_table('partman_test', 'id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'Check id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP-'1 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP-'1 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p10_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'Check id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP-'1 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP-'1 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p20_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'Check id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP-'1 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP-'1 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p30_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'Check id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP-'1 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP-'1 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p40_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'Check id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP-'1 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP-'1 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p50_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'Check id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP-'1 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP-'1 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), 'Check id_taptest_table_p60_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT is_empty('SELECT parent_table from part_config where parent_table = ''partman_test.id_taptest_table_p0''', 'Check that partman_test.id_taptest_table_p0 was removed from part_config'); SELECT is_empty('SELECT parent_table from part_config where parent_table = ''partman_test.id_taptest_table_p10''', 'Check that partman_test.id_taptest_table_p10 was removed from part_config'); SELECT is_empty('SELECT parent_table from part_config where parent_table = ''partman_test.id_taptest_table_p20''', 'Check that partman_test.id_taptest_table_p20 was removed from part_config'); SELECT is_empty('SELECT parent_table from part_config where parent_table = ''partman_test.id_taptest_table_p30''', 'Check that partman_test.id_taptest_table_p30 was removed from part_config'); SELECT is_empty('SELECT parent_table from part_config where parent_table = ''partman_test.id_taptest_table_p40''', 'Check that partman_test.id_taptest_table_p40 was removed from part_config'); SELECT is_empty('SELECT parent_table from part_config where parent_table = ''partman_test.id_taptest_table_p50''', 'Check that partman_test.id_taptest_table_p50 was removed from part_config'); SELECT is_empty('SELECT parent_table from part_config where parent_table = ''partman_test.id_taptest_table_p60''', 'Check that partman_test.id_taptest_table_p60 was removed from part_config'); -- Check top parent is still empty SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table', 'Check that top parent table has not had any data moved to it'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10', ARRAY[10], 'Check count from subparent table'); SELECT undo_partition_id('partman_test.id_taptest_table', 20, p_keep_table := false); SELECT hasnt_table('partman_test', 'id_taptest_table_p0', 'Check id_taptest_table_p60 does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p10', 'Check id_taptest_table_p60 does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p20', 'Check id_taptest_table_p60 does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p30', 'Check id_taptest_table_p60 does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p40', 'Check id_taptest_table_p60 does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p50', 'Check id_taptest_table_p60 does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p60', 'Check id_taptest_table_p60 does not exist'); SELECT is_empty('SELECT parent_table from part_config where parent_table = ''partman_test.id_taptest_table''', 'Check that partman_test.id_taptest_table was removed from part_config'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table', ARRAY[11], 'Check count from final unpartitioned table'); SELECT * FROM finish(); ROLLBACK; pg_partman-2.2.2/test/test-id-trunc.sql000066400000000000000000000616461262146621700200730ustar00rootroot00000000000000-- ########## ID TESTS ########## -- Other tests: Long name truncation \set ON_ERROR_ROLLBACK 1 \set ON_ERROR_STOP true BEGIN; SELECT set_config('search_path','partman, public',false); SELECT plan(102); CREATE SCHEMA partman_test; CREATE ROLE partman_basic; CREATE ROLE partman_revoke; CREATE ROLE partman_owner; CREATE TABLE partman_test.id_taptest_table_123456789012345678901234567890123456789012345 (col1 int primary key, col2 text, col3 timestamptz DEFAULT now()); INSERT INTO partman_test.id_taptest_table_123456789012345678901234567890123456789012345 (col1) VALUES (generate_series(1,9)); GRANT SELECT,INSERT,UPDATE ON partman_test.id_taptest_table_123456789012345678901234567890123456789012345 TO partman_basic; GRANT ALL ON partman_test.id_taptest_table_123456789012345678901234567890123456789012345 TO partman_revoke; SELECT create_parent('partman_test.id_taptest_table_123456789012345678901234567890123456789012345', 'col1', 'id', '10'); SELECT has_table('partman_test', 'id_taptest_table_1234567890123456789012345678901234567890123_p0', 'Check id_taptest_table_1234567890123456789012345678901234567890123_p0 exists'); SELECT has_table('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p10', 'Check id_taptest_table_123456789012345678901234567890123456789012_p10 exists'); SELECT has_table('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p20', 'Check id_taptest_table_123456789012345678901234567890123456789012_p20 exists'); SELECT has_table('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p30', 'Check id_taptest_table_123456789012345678901234567890123456789012_p30 exists'); SELECT has_table('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p40', 'Check id_taptest_table_123456789012345678901234567890123456789012_p40 exists'); SELECT hasnt_table('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p50', 'Check id_taptest_table_123456789012345678901234567890123456789012_p50 doesn''t exists yet'); SELECT col_is_pk('partman_test', 'id_taptest_table_1234567890123456789012345678901234567890123_p0', ARRAY['col1'], 'Check for primary key in id_taptest_table_1234567890123456789012345678901234567890123_p0'); SELECT col_is_pk('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p10', ARRAY['col1'], 'Check for primary key in id_taptest_table_123456789012345678901234567890123456789012_p10'); SELECT col_is_pk('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p20', ARRAY['col1'], 'Check for primary key in id_taptest_table_123456789012345678901234567890123456789012_p20'); SELECT col_is_pk('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p30', ARRAY['col1'], 'Check for primary key in id_taptest_table_123456789012345678901234567890123456789012_p30'); SELECT col_is_pk('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p40', ARRAY['col1'], 'Check for primary key in id_taptest_table_123456789012345678901234567890123456789012_p40'); SELECT table_privs_are('partman_test', 'id_taptest_table_1234567890123456789012345678901234567890123_p0', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_1234567890123456789012345678901234567890123_p0'); SELECT table_privs_are('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p10', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_123456789012345678901234567890123456789012_p10'); SELECT table_privs_are('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p20', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_123456789012345678901234567890123456789012_p20'); SELECT table_privs_are('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p30', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_123456789012345678901234567890123456789012_p30'); SELECT table_privs_are('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p40', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_123456789012345678901234567890123456789012_p40'); SELECT table_privs_are('partman_test', 'id_taptest_table_1234567890123456789012345678901234567890123_p0', 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of id_taptest_table_1234567890123456789012345678901234567890123_p0'); SELECT table_privs_are('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p10', 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of id_taptest_table_123456789012345678901234567890123456789012_p10'); SELECT table_privs_are('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p20', 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of id_taptest_table_123456789012345678901234567890123456789012_p20'); SELECT table_privs_are('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p30', 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of id_taptest_table_123456789012345678901234567890123456789012_p30'); SELECT table_privs_are('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p40', 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of id_taptest_table_123456789012345678901234567890123456789012_p40'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table_123456789012345678901234567890123456789012345'')::int', ARRAY[9], 'Check that partitioning function returns correct count of rows moved'); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_123456789012345678901234567890123456789012345', 'Check that parent table has had data moved to partition'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_123456789012345678901234567890123456789012345', ARRAY[9], 'Check count from parent table'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_1234567890123456789012345678901234567890123_p0', ARRAY[9], 'Check count from id_taptest_table_1234567890123456789012345678901234567890123_p0'); REVOKE INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER ON partman_test.id_taptest_table_123456789012345678901234567890123456789012345 FROM partman_revoke; INSERT INTO partman_test.id_taptest_table_123456789012345678901234567890123456789012345 (col1) VALUES (generate_series(10,25)); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_123456789012345678901234567890123456789012345', 'Check that parent table has had no data inserted to it'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_123456789012345678901234567890123456789012_p10', ARRAY[10], 'Check count from id_taptest_table_123456789012345678901234567890123456789012_p10'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_123456789012345678901234567890123456789012_p20', ARRAY[6], 'Check count from id_taptest_table_123456789012345678901234567890123456789012_p20'); SELECT has_table('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p50', 'Check id_taptest_table_123456789012345678901234567890123456789012_p50 exists'); SELECT hasnt_table('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p60', 'Check id_taptest_table_123456789012345678901234567890123456789012_p60 doesn''t exists yet'); SELECT col_is_pk('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p50', ARRAY['col1'], 'Check for primary key in id_taptest_table_123456789012345678901234567890123456789012_p50'); SELECT table_privs_are('partman_test', 'id_taptest_table_1234567890123456789012345678901234567890123_p0', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_1234567890123456789012345678901234567890123_p0'); SELECT table_privs_are('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p10', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_123456789012345678901234567890123456789012_p10'); SELECT table_privs_are('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p20', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_123456789012345678901234567890123456789012_p20'); SELECT table_privs_are('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p30', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_123456789012345678901234567890123456789012_p30'); SELECT table_privs_are('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p40', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_123456789012345678901234567890123456789012_p40'); SELECT table_privs_are('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p50', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_123456789012345678901234567890123456789012_p50'); SELECT table_privs_are('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p50', 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of id_taptest_table_123456789012345678901234567890123456789012_p50'); GRANT DELETE ON partman_test.id_taptest_table_123456789012345678901234567890123456789012345 TO partman_basic; REVOKE ALL ON partman_test.id_taptest_table_123456789012345678901234567890123456789012345 FROM partman_revoke; ALTER TABLE partman_test.id_taptest_table_123456789012345678901234567890123456789012345 OWNER TO partman_owner; INSERT INTO partman_test.id_taptest_table_123456789012345678901234567890123456789012345 (col1) VALUES (generate_series(26,38)); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table_123456789012345678901234567890123456789012345', 'Check that parent table has had no data inserted to it'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_123456789012345678901234567890123456789012345', ARRAY[38], 'Check count from id_taptest_table'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_123456789012345678901234567890123456789012_p20', ARRAY[10], 'Check count from id_taptest_table_123456789012345678901234567890123456789012_p20'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_123456789012345678901234567890123456789012_p30', ARRAY[9], 'Check count from id_taptest_table_123456789012345678901234567890123456789012_p30'); SELECT has_table('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p60', 'Check id_taptest_table_123456789012345678901234567890123456789012_p60 exists'); SELECT has_table('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p70', 'Check id_taptest_table_123456789012345678901234567890123456789012_p70 exists'); SELECT hasnt_table('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p80', 'Check id_taptest_table_123456789012345678901234567890123456789012_p80 doesn''t exists yet'); SELECT col_is_pk('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p60', ARRAY['col1'], 'Check for primary key in id_taptest_table_123456789012345678901234567890123456789012_p60'); SELECT col_is_pk('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p70', ARRAY['col1'], 'Check for primary key in id_taptest_table_123456789012345678901234567890123456789012_p70'); SELECT table_privs_are('partman_test', 'id_taptest_table_1234567890123456789012345678901234567890123_p0', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_1234567890123456789012345678901234567890123_p0'); SELECT table_privs_are('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p10', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_123456789012345678901234567890123456789012_p10'); SELECT table_privs_are('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p20', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_123456789012345678901234567890123456789012_p20'); SELECT table_privs_are('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p30', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_123456789012345678901234567890123456789012_p30'); SELECT table_privs_are('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p40', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_123456789012345678901234567890123456789012_p40'); SELECT table_privs_are('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p50', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_123456789012345678901234567890123456789012_p50'); SELECT table_privs_are('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p50', 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of id_taptest_table_123456789012345678901234567890123456789012_p50'); SELECT table_privs_are('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p60', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE','DELETE'], 'Check partman_basic privileges of id_taptest_table_123456789012345678901234567890123456789012_p60'); SELECT table_privs_are('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p70', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE','DELETE'], 'Check partman_basic privileges of id_taptest_table_123456789012345678901234567890123456789012_p70'); SELECT table_owner_is ('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p60', 'partman_owner', 'Check that ownership change worked for id_taptest_table_123456789012345678901234567890123456789012_p60'); SELECT table_owner_is ('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p70', 'partman_owner', 'Check that ownership change worked for id_taptest_table_123456789012345678901234567890123456789012_p70'); SELECT table_privs_are('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p60', 'partman_revoke', '{}'::text[], 'Check partman_revoke has no privileges on id_taptest_table_123456789012345678901234567890123456789012_p60'); SELECT table_privs_are('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p70', 'partman_revoke', '{}'::text[], 'Check partman_revoke has no privileges on id_taptest_table_123456789012345678901234567890123456789012_p70'); INSERT INTO partman_test.id_taptest_table_123456789012345678901234567890123456789012345 (col1) VALUES (generate_series(200,210)); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_123456789012345678901234567890123456789012345', ARRAY[11], 'Check that data outside trigger scope goes to parent'); SELECT reapply_privileges('partman_test.id_taptest_table_123456789012345678901234567890123456789012345'); SELECT table_privs_are('partman_test', 'id_taptest_table_1234567890123456789012345678901234567890123_p0', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE','DELETE'], 'Check partman_basic privileges of id_taptest_table_1234567890123456789012345678901234567890123_p0'); SELECT table_privs_are('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p10', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE','DELETE'], 'Check partman_basic privileges of id_taptest_table_123456789012345678901234567890123456789012_p10'); SELECT table_privs_are('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p20', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE','DELETE'], 'Check partman_basic privileges of id_taptest_table_123456789012345678901234567890123456789012_p20'); SELECT table_privs_are('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p30', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE','DELETE'], 'Check partman_basic privileges of id_taptest_table_123456789012345678901234567890123456789012_p30'); SELECT table_privs_are('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p40', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE','DELETE'], 'Check partman_basic privileges of id_taptest_table_123456789012345678901234567890123456789012_p40'); SELECT table_privs_are('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p50', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE','DELETE'], 'Check partman_basic privileges of id_taptest_table_123456789012345678901234567890123456789012_p50'); SELECT table_privs_are('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p60', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE','DELETE'], 'Check partman_basic privileges of id_taptest_table_123456789012345678901234567890123456789012_p60'); SELECT table_privs_are('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p70', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE','DELETE'], 'Check partman_basic privileges of id_taptest_table_123456789012345678901234567890123456789012_p70'); SELECT table_privs_are('partman_test', 'id_taptest_table_1234567890123456789012345678901234567890123_p0', 'partman_revoke', '{}'::text[], 'Check partman_revoke has no privileges on id_taptest_table_1234567890123456789012345678901234567890123_p0'); SELECT table_privs_are('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p10', 'partman_revoke', '{}'::text[], 'Check partman_revoke has no privileges on id_taptest_table_123456789012345678901234567890123456789012_p10'); SELECT table_privs_are('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p20', 'partman_revoke', '{}'::text[], 'Check partman_revoke has no privileges on id_taptest_table_123456789012345678901234567890123456789012_p20'); SELECT table_privs_are('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p30', 'partman_revoke', '{}'::text[], 'Check partman_revoke has no privileges on id_taptest_table_123456789012345678901234567890123456789012_p30'); SELECT table_privs_are('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p40', 'partman_revoke', '{}'::text[], 'Check partman_revoke has no privileges on id_taptest_table_123456789012345678901234567890123456789012_p40'); SELECT table_privs_are('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p50', 'partman_revoke', '{}'::text[], 'Check partman_revoke has no privileges on id_taptest_table_123456789012345678901234567890123456789012_p50'); SELECT table_privs_are('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p60', 'partman_revoke', '{}'::text[], 'Check partman_revoke has no privileges on id_taptest_table_123456789012345678901234567890123456789012_p60'); SELECT table_privs_are('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p70', 'partman_revoke', '{}'::text[], 'Check partman_revoke has no privileges on id_taptest_table_123456789012345678901234567890123456789012_p70'); SELECT table_owner_is ('partman_test', 'id_taptest_table_1234567890123456789012345678901234567890123_p0', 'partman_owner', 'Check that ownership change worked for id_taptest_table_1234567890123456789012345678901234567890123_p0'); SELECT table_owner_is ('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p10', 'partman_owner', 'Check that ownership change worked for id_taptest_table_123456789012345678901234567890123456789012_p10'); SELECT table_owner_is ('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p20', 'partman_owner', 'Check that ownership change worked for id_taptest_table_123456789012345678901234567890123456789012_p20'); SELECT table_owner_is ('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p30', 'partman_owner', 'Check that ownership change worked for id_taptest_table_123456789012345678901234567890123456789012_p30'); SELECT table_owner_is ('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p40', 'partman_owner', 'Check that ownership change worked for id_taptest_table_123456789012345678901234567890123456789012_p40'); SELECT table_owner_is ('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p50', 'partman_owner', 'Check that ownership change worked for id_taptest_table_123456789012345678901234567890123456789012_p50'); SELECT table_owner_is ('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p60', 'partman_owner', 'Check that ownership change worked for id_taptest_table_123456789012345678901234567890123456789012_p60'); SELECT table_owner_is ('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p70', 'partman_owner', 'Check that ownership change worked for id_taptest_table_123456789012345678901234567890123456789012_p70'); SELECT undo_partition_id('partman_test.id_taptest_table_123456789012345678901234567890123456789012345', 10); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table_123456789012345678901234567890123456789012345', ARRAY[49], 'Check count from parent table after undo'); SELECT has_table('partman_test', 'id_taptest_table_1234567890123456789012345678901234567890123_p0', 'Check id_taptest_table_1234567890123456789012345678901234567890123_p0 still exists'); SELECT is_empty('SELECT * FROM partman_test.id_taptest_table_1234567890123456789012345678901234567890123_p0', 'Check child table had its data removed id_taptest_table_1234567890123456789012345678901234567890123_p0'); SELECT has_table('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p10', 'Check id_taptest_table_123456789012345678901234567890123456789012_p10 still exists'); SELECT is_empty('SELECT * FROM partman_test.id_taptest_table_123456789012345678901234567890123456789012_p10', 'Check child table had its data removed id_taptest_table_123456789012345678901234567890123456789012_p10'); SELECT has_table('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p20', 'Check id_taptest_table_123456789012345678901234567890123456789012_p20 still exists'); SELECT is_empty('SELECT * FROM partman_test.id_taptest_table_123456789012345678901234567890123456789012_p20', 'Check child table had its data removed id_taptest_table_123456789012345678901234567890123456789012_p20'); SELECT has_table('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p30', 'Check id_taptest_table_123456789012345678901234567890123456789012_p30 still exists'); SELECT is_empty('SELECT * FROM partman_test.id_taptest_table_123456789012345678901234567890123456789012_p30', 'Check child table had its data removed id_taptest_table_123456789012345678901234567890123456789012_p30'); SELECT has_table('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p40', 'Check id_taptest_table_123456789012345678901234567890123456789012_p40 still exists'); SELECT is_empty('SELECT * FROM partman_test.id_taptest_table_123456789012345678901234567890123456789012_p40', 'Check child table had its data removed id_taptest_table_123456789012345678901234567890123456789012_p40'); SELECT has_table('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p50', 'Check id_taptest_table_123456789012345678901234567890123456789012_p50 still exists'); SELECT is_empty('SELECT * FROM partman_test.id_taptest_table_123456789012345678901234567890123456789012_p50', 'Check child table had its data removed id_taptest_table_123456789012345678901234567890123456789012_p50'); SELECT has_table('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p60', 'Check id_taptest_table_123456789012345678901234567890123456789012_p60 still exists'); SELECT is_empty('SELECT * FROM partman_test.id_taptest_table_123456789012345678901234567890123456789012_p60', 'Check child table had its data removed id_taptest_table_123456789012345678901234567890123456789012_p60'); SELECT has_table('partman_test', 'id_taptest_table_123456789012345678901234567890123456789012_p70', 'Check id_taptest_table_123456789012345678901234567890123456789012_p70 still exists'); SELECT is_empty('SELECT * FROM partman_test.id_taptest_table_123456789012345678901234567890123456789012_p70', 'Check child table had its data removed id_taptest_table_123456789012345678901234567890123456789012_p70'); SELECT * FROM finish(); ROLLBACK; pg_partman-2.2.2/test/test-id.sql000066400000000000000000000472731262146621700167420ustar00rootroot00000000000000-- ########## ID DYNAMIC TESTS ########## -- Additional tests: turn off pg_jobmon logging, UNLOGGED, Make sure option to not inherit foreign keys works, larger than necessary p_batch_count to partition_data_id(), retention \set ON_ERROR_ROLLBACK 1 \set ON_ERROR_STOP true BEGIN; SELECT set_config('search_path','partman, public',false); SELECT plan(118); CREATE SCHEMA partman_test; CREATE SCHEMA partman_retention_test; CREATE ROLE partman_basic; CREATE ROLE partman_revoke; CREATE ROLE partman_owner; CREATE TABLE partman_test.fk_test_reference (col2 text unique not null); INSERT INTO partman_test.fk_test_reference VALUES ('stuff'); CREATE UNLOGGED TABLE partman_test.id_taptest_table ( col1 int primary key , col2 text not null default 'stuff' references partman_test.fk_test_reference (col2) , col3 timestamptz DEFAULT now()); INSERT INTO partman_test.id_taptest_table (col1) VALUES (generate_series(1,9)); GRANT SELECT,INSERT,UPDATE ON partman_test.id_taptest_table TO partman_basic; GRANT ALL ON partman_test.id_taptest_table TO partman_revoke; SELECT results_eq('SELECT create_parent(''partman_test.id_taptest_table'', ''col1'', ''id'', ''10'', p_inherit_fk := false, p_jobmon := false)::text', ARRAY['true'], 'Check that create_parent() returns true'); SELECT has_table('partman_test', 'id_taptest_table_p0', 'Check id_taptest_table_p0 exists'); SELECT has_table('partman_test', 'id_taptest_table_p10', 'Check id_taptest_table_p10 exists'); SELECT has_table('partman_test', 'id_taptest_table_p20', 'Check id_taptest_table_p20 exists'); SELECT has_table('partman_test', 'id_taptest_table_p30', 'Check id_taptest_table_p30 exists'); SELECT has_table('partman_test', 'id_taptest_table_p40', 'Check id_taptest_table_p40 exists'); SELECT hasnt_table('partman_test', 'id_taptest_table_p50', 'Check id_taptest_table_p50 doesn''t exists yet'); SELECT col_is_pk('partman_test', 'id_taptest_table_p0', ARRAY['col1'], 'Check for primary key in id_taptest_table_p0'); SELECT col_is_pk('partman_test', 'id_taptest_table_p10', ARRAY['col1'], 'Check for primary key in id_taptest_table_p10'); SELECT col_is_pk('partman_test', 'id_taptest_table_p20', ARRAY['col1'], 'Check for primary key in id_taptest_table_p20'); SELECT col_is_pk('partman_test', 'id_taptest_table_p30', ARRAY['col1'], 'Check for primary key in id_taptest_table_p30'); SELECT col_is_pk('partman_test', 'id_taptest_table_p40', ARRAY['col1'], 'Check for primary key in id_taptest_table_p40'); SELECT col_isnt_fk('partman_test', 'id_taptest_table_p0', 'col2', 'Check that foreign key was NOT inherited to id_taptest_table_p0'); SELECT col_isnt_fk('partman_test', 'id_taptest_table_p10', 'col2', 'Check that foreign key was NOT inherited to id_taptest_table_p10'); SELECT col_isnt_fk('partman_test', 'id_taptest_table_p20', 'col2', 'Check that foreign key was NOT inherited to id_taptest_table_p20'); SELECT col_isnt_fk('partman_test', 'id_taptest_table_p30', 'col2', 'Check that foreign key was NOT inherited to id_taptest_table_p30'); SELECT col_isnt_fk('partman_test', 'id_taptest_table_p40', 'col2', 'Check that foreign key was NOT inherited to id_taptest_table_p40'); SELECT table_privs_are('partman_test', 'id_taptest_table_p0', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p0'); SELECT table_privs_are('partman_test', 'id_taptest_table_p10', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p10'); SELECT table_privs_are('partman_test', 'id_taptest_table_p20', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p20'); SELECT table_privs_are('partman_test', 'id_taptest_table_p30', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p30'); SELECT table_privs_are('partman_test', 'id_taptest_table_p40', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p40'); SELECT table_privs_are('partman_test', 'id_taptest_table_p0', 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of id_taptest_table_p0'); SELECT table_privs_are('partman_test', 'id_taptest_table_p10', 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of id_taptest_table_p10'); SELECT table_privs_are('partman_test', 'id_taptest_table_p20', 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of id_taptest_table_p20'); SELECT table_privs_are('partman_test', 'id_taptest_table_p30', 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of id_taptest_table_p30'); SELECT table_privs_are('partman_test', 'id_taptest_table_p40', 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of id_taptest_table_p40'); SELECT results_eq('SELECT relpersistence::text FROM pg_catalog.pg_class WHERE oid::regclass = ''partman_test.id_taptest_table''::regclass', ARRAY['u'], 'Check that parent table is unlogged'); SELECT results_eq('SELECT relpersistence::text FROM pg_catalog.pg_class WHERE oid::regclass = ''partman_test.id_taptest_table_p0''::regclass', ARRAY['u'], 'Check that id_taptest_table_p0 is unlogged'); SELECT results_eq('SELECT relpersistence::text FROM pg_catalog.pg_class WHERE oid::regclass = ''partman_test.id_taptest_table_p10''::regclass', ARRAY['u'], 'Check that id_taptest_table_p10 is unlogged'); SELECT results_eq('SELECT relpersistence::text FROM pg_catalog.pg_class WHERE oid::regclass = ''partman_test.id_taptest_table_p20''::regclass', ARRAY['u'], 'Check that id_taptest_table_p20 is unlogged'); SELECT results_eq('SELECT relpersistence::text FROM pg_catalog.pg_class WHERE oid::regclass = ''partman_test.id_taptest_table_p30''::regclass', ARRAY['u'], 'Check that id_taptest_table_p30 is unlogged'); SELECT results_eq('SELECT relpersistence::text FROM pg_catalog.pg_class WHERE oid::regclass = ''partman_test.id_taptest_table_p40''::regclass', ARRAY['u'], 'Check that id_taptest_table_p40 is unlogged'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table'', p_batch_count := 5)::int', ARRAY[9], 'Check that partitioning function returns correct count of rows moved'); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table', 'Check that parent table has had data moved to partition'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table', ARRAY[9], 'Check count from parent table'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0', ARRAY[9], 'Check count from id_taptest_table_p0'); REVOKE INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER ON partman_test.id_taptest_table FROM partman_revoke; INSERT INTO partman_test.id_taptest_table (col1) VALUES (generate_series(10,25)); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table', 'Check that parent table has had no data inserted to it'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10', ARRAY[10], 'Check count from id_taptest_table_p10'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20', ARRAY[6], 'Check count from id_taptest_table_p20'); SELECT has_table('partman_test', 'id_taptest_table_p50', 'Check id_taptest_table_p50 exists'); SELECT results_eq('SELECT relpersistence::text FROM pg_catalog.pg_class WHERE oid::regclass = ''partman_test.id_taptest_table_p50''::regclass', ARRAY['u'], 'Check that id_taptest_table_p50 is unlogged'); SELECT hasnt_table('partman_test', 'id_taptest_table_p60', 'Check id_taptest_table_p60 doesn''t exists yet'); SELECT col_is_pk('partman_test', 'id_taptest_table_p50', ARRAY['col1'], 'Check for primary key in id_taptest_table_p50'); SELECT col_isnt_fk('partman_test', 'id_taptest_table_p50', 'col2', 'Check that foreign key was NOT inherited to id_taptest_table_p50'); SELECT table_privs_are('partman_test', 'id_taptest_table_p0', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p0'); SELECT table_privs_are('partman_test', 'id_taptest_table_p10', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p10'); SELECT table_privs_are('partman_test', 'id_taptest_table_p20', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p20'); SELECT table_privs_are('partman_test', 'id_taptest_table_p30', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p30'); SELECT table_privs_are('partman_test', 'id_taptest_table_p40', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p40'); SELECT table_privs_are('partman_test', 'id_taptest_table_p50', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p50'); SELECT table_privs_are('partman_test', 'id_taptest_table_p50', 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of id_taptest_table_p50'); GRANT DELETE ON partman_test.id_taptest_table TO partman_basic; REVOKE ALL ON partman_test.id_taptest_table FROM partman_revoke; ALTER TABLE partman_test.id_taptest_table OWNER TO partman_owner; INSERT INTO partman_test.id_taptest_table (col1) VALUES (generate_series(26,38)); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table', 'Check that parent table has had no data inserted to it'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table', ARRAY[38], 'Check count from id_taptest_table'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20', ARRAY[10], 'Check count from id_taptest_table_p20'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30', ARRAY[9], 'Check count from id_taptest_table_p30'); SELECT has_table('partman_test', 'id_taptest_table_p60', 'Check id_taptest_table_p60 exists'); SELECT results_eq('SELECT relpersistence::text FROM pg_catalog.pg_class WHERE oid::regclass = ''partman_test.id_taptest_table_p60''::regclass', ARRAY['u'], 'Check that id_taptest_table_p60 is unlogged'); SELECT has_table('partman_test', 'id_taptest_table_p70', 'Check id_taptest_table_p70 exists'); SELECT results_eq('SELECT relpersistence::text FROM pg_catalog.pg_class WHERE oid::regclass = ''partman_test.id_taptest_table_p70''::regclass', ARRAY['u'], 'Check that id_taptest_table_p70 is unlogged'); SELECT hasnt_table('partman_test', 'id_taptest_table_p80', 'Check id_taptest_table_p80 doesn''t exists yet'); SELECT col_is_pk('partman_test', 'id_taptest_table_p60', ARRAY['col1'], 'Check for primary key in id_taptest_table_p60'); SELECT col_is_pk('partman_test', 'id_taptest_table_p70', ARRAY['col1'], 'Check for primary key in id_taptest_table_p70'); SELECT col_isnt_fk('partman_test', 'id_taptest_table_p60', 'col2', 'Check that foreign key was NOT inherited to id_taptest_table_p60'); SELECT col_isnt_fk('partman_test', 'id_taptest_table_p70', 'col2', 'Check that foreign key was NOT inherited to id_taptest_table_p70'); SELECT table_privs_are('partman_test', 'id_taptest_table_p0', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p0'); SELECT table_privs_are('partman_test', 'id_taptest_table_p10', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p10'); SELECT table_privs_are('partman_test', 'id_taptest_table_p20', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p20'); SELECT table_privs_are('partman_test', 'id_taptest_table_p30', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p30'); SELECT table_privs_are('partman_test', 'id_taptest_table_p40', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p40'); SELECT table_privs_are('partman_test', 'id_taptest_table_p50', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p50'); SELECT table_privs_are('partman_test', 'id_taptest_table_p50', 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of id_taptest_table_p50'); SELECT table_privs_are('partman_test', 'id_taptest_table_p60', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE','DELETE'], 'Check partman_basic privileges of id_taptest_table_p60'); SELECT table_privs_are('partman_test', 'id_taptest_table_p70', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE','DELETE'], 'Check partman_basic privileges of id_taptest_table_p70'); SELECT table_owner_is ('partman_test', 'id_taptest_table_p60', 'partman_owner', 'Check that ownership change worked for id_taptest_table_p60'); SELECT table_owner_is ('partman_test', 'id_taptest_table_p70', 'partman_owner', 'Check that ownership change worked for id_taptest_table_p70'); SELECT table_privs_are('partman_test', 'id_taptest_table_p60', 'partman_revoke', '{}'::text[], 'Check partman_revoke has no privileges on id_taptest_table_p60'); SELECT table_privs_are('partman_test', 'id_taptest_table_p70', 'partman_revoke', '{}'::text[], 'Check partman_revoke has no privileges on id_taptest_table_p70'); INSERT INTO partman_test.id_taptest_table (col1) VALUES (generate_series(200,210)); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table', ARRAY[11], 'Check that data outside trigger scope goes to parent'); SELECT reapply_privileges('partman_test.id_taptest_table'); SELECT table_privs_are('partman_test', 'id_taptest_table_p0', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE','DELETE'], 'Check partman_basic privileges of id_taptest_table_p0'); SELECT table_privs_are('partman_test', 'id_taptest_table_p10', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE','DELETE'], 'Check partman_basic privileges of id_taptest_table_p10'); SELECT table_privs_are('partman_test', 'id_taptest_table_p20', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE','DELETE'], 'Check partman_basic privileges of id_taptest_table_p20'); SELECT table_privs_are('partman_test', 'id_taptest_table_p30', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE','DELETE'], 'Check partman_basic privileges of id_taptest_table_p30'); SELECT table_privs_are('partman_test', 'id_taptest_table_p40', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE','DELETE'], 'Check partman_basic privileges of id_taptest_table_p40'); SELECT table_privs_are('partman_test', 'id_taptest_table_p50', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE','DELETE'], 'Check partman_basic privileges of id_taptest_table_p50'); SELECT table_privs_are('partman_test', 'id_taptest_table_p60', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE','DELETE'], 'Check partman_basic privileges of id_taptest_table_p60'); SELECT table_privs_are('partman_test', 'id_taptest_table_p70', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE','DELETE'], 'Check partman_basic privileges of id_taptest_table_p70'); SELECT table_privs_are('partman_test', 'id_taptest_table_p0', 'partman_revoke', '{}'::text[], 'Check partman_revoke has no privileges on id_taptest_table_p0'); SELECT table_privs_are('partman_test', 'id_taptest_table_p10', 'partman_revoke', '{}'::text[], 'Check partman_revoke has no privileges on id_taptest_table_p10'); SELECT table_privs_are('partman_test', 'id_taptest_table_p20', 'partman_revoke', '{}'::text[], 'Check partman_revoke has no privileges on id_taptest_table_p20'); SELECT table_privs_are('partman_test', 'id_taptest_table_p30', 'partman_revoke', '{}'::text[], 'Check partman_revoke has no privileges on id_taptest_table_p30'); SELECT table_privs_are('partman_test', 'id_taptest_table_p40', 'partman_revoke', '{}'::text[], 'Check partman_revoke has no privileges on id_taptest_table_p40'); SELECT table_privs_are('partman_test', 'id_taptest_table_p50', 'partman_revoke', '{}'::text[], 'Check partman_revoke has no privileges on id_taptest_table_p50'); SELECT table_privs_are('partman_test', 'id_taptest_table_p60', 'partman_revoke', '{}'::text[], 'Check partman_revoke has no privileges on id_taptest_table_p60'); SELECT table_privs_are('partman_test', 'id_taptest_table_p70', 'partman_revoke', '{}'::text[], 'Check partman_revoke has no privileges on id_taptest_table_p70'); SELECT table_owner_is ('partman_test', 'id_taptest_table_p0', 'partman_owner', 'Check that ownership change worked for id_taptest_table_p0'); SELECT table_owner_is ('partman_test', 'id_taptest_table_p10', 'partman_owner', 'Check that ownership change worked for id_taptest_table_p10'); SELECT table_owner_is ('partman_test', 'id_taptest_table_p20', 'partman_owner', 'Check that ownership change worked for id_taptest_table_p20'); SELECT table_owner_is ('partman_test', 'id_taptest_table_p30', 'partman_owner', 'Check that ownership change worked for id_taptest_table_p30'); SELECT table_owner_is ('partman_test', 'id_taptest_table_p40', 'partman_owner', 'Check that ownership change worked for id_taptest_table_p40'); SELECT table_owner_is ('partman_test', 'id_taptest_table_p50', 'partman_owner', 'Check that ownership change worked for id_taptest_table_p50'); SELECT table_owner_is ('partman_test', 'id_taptest_table_p60', 'partman_owner', 'Check that ownership change worked for id_taptest_table_p60'); SELECT table_owner_is ('partman_test', 'id_taptest_table_p70', 'partman_owner', 'Check that ownership change worked for id_taptest_table_p70'); -- Max value is 38 above SELECT drop_partition_id('partman_test.id_taptest_table', '20', p_keep_table := false); SELECT hasnt_table('partman_test', 'id_taptest_table_p0', 'Check id_taptest_table_p0 doesn''t exists anymore'); UPDATE part_config SET retention = '10' WHERE parent_table = 'partman_test.id_taptest_table'; SELECT drop_partition_id('partman_test.id_taptest_table', p_retention_schema := 'partman_retention_test'); SELECT hasnt_table('partman_test', 'id_taptest_table_p10', 'Check id_taptest_table_p10 doesn''t exists anymore'); SELECT has_table('partman_retention_test', 'id_taptest_table_p10', 'Check id_taptest_table_p10 got moved to new schema'); -- Make above work -- Has to run twice because second time around is when it sees the partition is empty & drops it SELECT undo_partition_id('partman_test.id_taptest_table', 2, p_keep_table := false); SELECT hasnt_table('partman_test', 'id_taptest_table_p20', 'Check id_taptest_table_p20 does not exist'); SELECT undo_partition_id('partman_test.id_taptest_table', 10); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table', ARRAY[30], 'Check count from parent table after undo'); SELECT has_table('partman_test', 'id_taptest_table_p30', 'Check id_taptest_table_p30 still exists'); SELECT is_empty('SELECT * FROM partman_test.id_taptest_table_p30', 'Check child table had its data removed id_taptest_table_p30'); SELECT has_table('partman_test', 'id_taptest_table_p40', 'Check id_taptest_table_p40 still exists'); SELECT is_empty('SELECT * FROM partman_test.id_taptest_table_p40', 'Check child table had its data removed id_taptest_table_p40'); SELECT has_table('partman_test', 'id_taptest_table_p50', 'Check id_taptest_table_p50 still exists'); SELECT is_empty('SELECT * FROM partman_test.id_taptest_table_p50', 'Check child table had its data removed id_taptest_table_p50'); SELECT has_table('partman_test', 'id_taptest_table_p60', 'Check id_taptest_table_p60 still exists'); SELECT is_empty('SELECT * FROM partman_test.id_taptest_table_p60', 'Check child table had its data removed id_taptest_table_p60'); SELECT has_table('partman_test', 'id_taptest_table_p70', 'Check id_taptest_table_p70 still exists'); SELECT is_empty('SELECT * FROM partman_test.id_taptest_table_p70', 'Check child table had its data removed id_taptest_table_p70'); SELECT * FROM finish(); ROLLBACK; pg_partman-2.2.2/test/test-time-daily-trunc.sql000066400000000000000000001124451262146621700215270ustar00rootroot00000000000000-- ########## TIME TESTS ########## -- Other tests: Long name truncation, privilege changes, undo partitioning but keep tables \set ON_ERROR_ROLLBACK 1 \set ON_ERROR_STOP true BEGIN; SELECT set_config('search_path','partman, public',false); SELECT plan(108); CREATE SCHEMA partman_test; CREATE ROLE partman_basic; CREATE ROLE partman_revoke; CREATE ROLE partman_owner; CREATE TABLE partman_test.time_taptest_table_1234567890123456789012345678901234567890 (col1 int primary key, col2 text, col3 timestamptz NOT NULL DEFAULT now()); INSERT INTO partman_test.time_taptest_table_1234567890123456789012345678901234567890 (col1, col3) VALUES (generate_series(1,10), CURRENT_TIMESTAMP); GRANT SELECT,INSERT,UPDATE ON partman_test.time_taptest_table_1234567890123456789012345678901234567890 TO partman_basic; GRANT ALL ON partman_test.time_taptest_table_1234567890123456789012345678901234567890 TO partman_revoke; SELECT create_parent('partman_test.time_taptest_table_1234567890123456789012345678901234567890', 'col3', 'time', 'daily'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT partition_data_time(''partman_test.time_taptest_table_1234567890123456789012345678901234567890'')::int', ARRAY[10], 'Check that partitioning function returns correct count of rows moved'); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table_1234567890123456789012345678901234567890', 'Check that parent table has had data moved to partition'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_1234567890123456789012345678901234567890', ARRAY[10], 'Check count from parent table'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), ARRAY[10], 'Check count from time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); REVOKE INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER ON partman_test.time_taptest_table_1234567890123456789012345678901234567890 FROM partman_revoke; INSERT INTO partman_test.time_taptest_table_1234567890123456789012345678901234567890 (col1, col3) VALUES (generate_series(11,20), CURRENT_TIMESTAMP + '1 day'::interval); INSERT INTO partman_test.time_taptest_table_1234567890123456789012345678901234567890 (col1, col3) VALUES (generate_series(21,25), CURRENT_TIMESTAMP + '2 days'::interval); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table_1234567890123456789012345678901234567890', 'Check that parent table has had no data inserted to it'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_1234567890123456789012345678901234567890', ARRAY[25], 'Check count from time_taptest_table_1234567890123456789012345678901234567890'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), ARRAY[10], 'Check count from time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), ARRAY[5], 'Check count from time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')); UPDATE part_config SET premake = 5, optimize_trigger = 5 WHERE parent_table = 'partman_test.time_taptest_table_1234567890123456789012345678901234567890'; SELECT run_maintenance(); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')||' does not exists'); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')); GRANT DELETE ON partman_test.time_taptest_table_1234567890123456789012345678901234567890 TO partman_basic; REVOKE ALL ON partman_test.time_taptest_table_1234567890123456789012345678901234567890 FROM partman_revoke; ALTER TABLE partman_test.time_taptest_table_1234567890123456789012345678901234567890 OWNER TO partman_owner; UPDATE part_config SET premake = 6, optimize_trigger = 6 WHERE parent_table = 'partman_test.time_taptest_table_1234567890123456789012345678901234567890'; SELECT run_maintenance(); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')); INSERT INTO partman_test.time_taptest_table_1234567890123456789012345678901234567890 (col1, col3) VALUES (generate_series(200,210), CURRENT_TIMESTAMP + '20 days'::interval); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table_1234567890123456789012345678901234567890', ARRAY[11], 'Check that data outside trigger scope goes to parent'); SELECT reapply_privileges('partman_test.time_taptest_table_1234567890123456789012345678901234567890'); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')); SELECT undo_partition_time('partman_test.time_taptest_table_1234567890123456789012345678901234567890', 20); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_12345678901234567890123456789012_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')||' is empty'); SELECT * FROM finish(); ROLLBACK; pg_partman-2.2.2/test/test-time-daily.sql000066400000000000000000001703561262146621700204030ustar00rootroot00000000000000-- ########## TIME STATIC TESTS ########## -- Other tests: With OIDS, run_maintenance(p_analyze := false), check that maintenance catches up if tables are missing \set ON_ERROR_ROLLBACK 1 \set ON_ERROR_STOP true BEGIN; SELECT set_config('search_path','partman, public',false); SELECT plan(210); CREATE SCHEMA partman_test; CREATE SCHEMA partman_retention_test; CREATE ROLE partman_basic; CREATE ROLE partman_revoke; CREATE ROLE partman_owner; CREATE TABLE partman_test.time_taptest_table (col1 int primary key, col2 text, col3 timestamptz NOT NULL DEFAULT now()) WITH (OIDS); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(1,10), CURRENT_TIMESTAMP); 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', 'time', 'daily'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT partition_data_time(''partman_test.time_taptest_table'')::int', ARRAY[10], 'Check that partitioning function returns correct count of rows moved'); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table', 'Check that parent table has had data moved to partition'); 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, 'YYYY_MM_DD'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); REVOKE INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER ON partman_test.time_taptest_table FROM partman_revoke; INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(11,20), CURRENT_TIMESTAMP + '1 day'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(21,25), CURRENT_TIMESTAMP + '2 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(26,30), CURRENT_TIMESTAMP + '3 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(31,37), CURRENT_TIMESTAMP + '4 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(40,49), CURRENT_TIMESTAMP - '1 day'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(50,70), CURRENT_TIMESTAMP - '2 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(71,85), CURRENT_TIMESTAMP - '3 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(86,100), CURRENT_TIMESTAMP - '4 days'::interval); 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+'1 day'::interval, 'YYYY_MM_DD'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), ARRAY[5], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), ARRAY[5], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), ARRAY[7], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), ARRAY[21], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), ARRAY[15], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), ARRAY[15], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')); UPDATE part_config SET premake = 5, optimize_trigger = 5 WHERE parent_table = 'partman_test.time_taptest_table'; -- Run to get +5 trigger in plae SELECT run_maintenance(p_analyze := false); -- Insert after maintenance so new +5 day trigger is in place INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(101,122), CURRENT_TIMESTAMP + '5 days'::interval); -- Run again to create proper future partitions SELECT run_maintenance(p_analyze := false); -- Data exists for +5 days, with 5 premake so +10 day table should exist SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD')); 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+'5 days'::interval, 'YYYY_MM_DD'), ARRAY[22], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')); GRANT DELETE ON partman_test.time_taptest_table TO partman_basic; REVOKE ALL ON partman_test.time_taptest_table FROM partman_revoke; ALTER TABLE partman_test.time_taptest_table OWNER TO partman_owner; UPDATE part_config SET premake = 6, optimize_trigger = 6 WHERE parent_table = 'partman_test.time_taptest_table'; SELECT run_maintenance(p_analyze := false); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(123,150), CURRENT_TIMESTAMP + '6 days'::interval); SELECT run_maintenance(p_analyze := false); 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', ARRAY[148], 'Check count from parent table'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), ARRAY[28], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'13 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'13 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD')); SELECT reapply_privileges('partman_test.time_taptest_table'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')); -- Test that maintenance will catch up DO $$ BEGIN EXECUTE 'DROP TABLE partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'); EXECUTE 'DROP TABLE partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 days'::interval, 'YYYY_MM_DD'); EXECUTE 'DROP TABLE partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'); EXECUTE 'DROP TABLE partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 days'::interval, 'YYYY_MM_DD'); EXECUTE 'DROP TABLE partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'); EXECUTE 'DROP TABLE partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'); EXECUTE 'DROP TABLE partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'); EXECUTE 'DROP TABLE partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'); EXECUTE 'DROP TABLE partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'); EXECUTE 'DROP TABLE partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD'); EXECUTE 'DROP TABLE partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'); EXECUTE 'DROP TABLE partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD'); EXECUTE 'DROP TABLE partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD'); EXECUTE 'DROP TABLE partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD'); EXECUTE 'DROP TABLE partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD'); END $$; SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT run_maintenance(); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||' does exist'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 days'::interval, 'YYYY_MM_DD')||' does exist'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||' does exist'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 days'::interval, 'YYYY_MM_DD')||' does exist'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||' does exist'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||' does exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||' does not exist'); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(11,20), CURRENT_TIMESTAMP + '1 day'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(21,25), CURRENT_TIMESTAMP + '2 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(26,30), CURRENT_TIMESTAMP + '3 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(1,10), CURRENT_TIMESTAMP); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(40,49), CURRENT_TIMESTAMP - '1 day'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(50,70), CURRENT_TIMESTAMP - '2 days'::interval); SELECT run_maintenance(); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||' does exist'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' does exist'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')||' does exist'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')||' does exist'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')||' does exist'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD')||' does exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD')||' does not exist'); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(31,37), CURRENT_TIMESTAMP + '4 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(101,122), CURRENT_TIMESTAMP + '5 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(123,150), CURRENT_TIMESTAMP + '6 days'::interval); SELECT run_maintenance(); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD')||' does exist'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD')||' does exist'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD')||' does exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'13 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'13 days'::interval, 'YYYY_MM_DD')||' does not exist'); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(200,210), CURRENT_TIMESTAMP + '20 days'::interval); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table', ARRAY[11], 'Check that data outside trigger scope goes to parent'); SELECT drop_partition_time('partman_test.time_taptest_table', '3 days', p_keep_table := false); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||' does not exist'); UPDATE part_config SET retention = '2 days'::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 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT has_table('partman_retention_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||' got moved to new schema'); SELECT undo_partition_time('partman_test.time_taptest_table', 20, p_keep_table := false); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table', ARRAY[129], 'Check count from parent table after undo'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT * FROM finish(); ROLLBACK; pg_partman-2.2.2/test/test-time-epoch-time-time-subpart.sql000066400000000000000000000725771262146621700237530ustar00rootroot00000000000000-- ########## TIME EPOCH PARENT / TIME EPOCH SUBPARENT / TIME EPOCH SUB-SUB-PARENT DYNAMIC ########## -- Currently tests 23, 39, 47 & 67 may fail around new years boundaries \set ON_ERROR_ROLLBACK 1 \set ON_ERROR_STOP true BEGIN; SELECT set_config('search_path','partman, public',false); SELECT plan(72); CREATE SCHEMA partman_test; CREATE TABLE partman_test.time_taptest_table (col1 int primary key, col2 text, col3 bigint NOT NULL DEFAULT extract('epoch' from now())); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(1,10), extract('epoch' from CURRENT_TIMESTAMP)); -- yearly SELECT create_parent('partman_test.time_taptest_table', 'col3', 'time', 'yearly', p_premake := 2, p_epoch := true); -- Make sure optimize values can be different UPDATE part_config SET optimize_trigger = 5, optimize_constraint = 10 WHERE parent_table = 'partman_test.time_taptest_table'; SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')||' does not exist'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 years'::interval, 'YYYY'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 years'::interval, 'YYYY')||' does not exist'); -- Move data from parent SELECT results_eq('SELECT partition_data_time(''partman_test.time_taptest_table'')::int', ARRAY[10], 'Check that partitioning function returns correct count of rows moved'); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table', 'Check that yearly parent table has had data moved to partition'); 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, 'YYYY'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')); -- monthly SELECT create_sub_parent('partman_test.time_taptest_table', 'col3', 'time', 'monthly', p_premake := 2, p_epoch := true); -- Make sure optimize values can be different UPDATE part_config_sub SET sub_optimize_trigger = 5, sub_optimize_constraint = 10, sub_retention_keep_table = false WHERE sub_parent = 'partman_test.time_taptest_table'; SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 month'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'1 month'::interval, 'YYYY_MM'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 month'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'1 month'::interval, 'YYYY_MM')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY_MM')||' exists'); -- Near end of year (Oct) following may fail since next hear's minimum month table will be created SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM')||' does not exist (this test may fail around year boundary. See comment in test code)'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 month'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'1 month'::interval, 'YYYY_MM'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 month'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'1 month'::interval, 'YYYY_MM')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 months'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'2 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 months'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'2 months'::interval, 'YYYY_MM')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY_MM')||' does not exist'); -- Check that previous and future years had the minimal partition made -- year +/- 1 tests may fail around year boundary. Tables may or may not exist depending on premake. That's fine. Should be ok for further in the future. SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY')||'_01', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY')||'_01 exists (this test may fail around year boundary. See comment in test code)'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY')||'_02', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY')||'_02 does not exists (this test may fail around year boundary. See comment in test code)'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||'_01', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||'_01 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||'_02', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||'_02 does not exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||'_01', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||'_01 exists (this test may fail around year boundary. See comment in test code)'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||'_02', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||'_02 exists (this test may fail around year boundary. See comment in test code)'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')||'_01', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'2 year'::interval, 'YYYY')||'_01 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')||'_02', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'2 year'::interval, 'YYYY')||'_02 exists'); SELECT results_eq('SELECT partition_data_time(''partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')||''')::int', ARRAY[10], 'Check that partitioning function returns correct count of rows moved (yearly subparent)'); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY'), 'Check data got moved out of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM')); -- Check subpart config SELECT results_eq('SELECT sub_parent FROM part_config_sub ORDER BY sub_parent', ARRAY['partman_test.time_taptest_table'], 'Check that part_config_sub has all tables configured as needed'); -- daily SELECT results_eq('SELECT create_sub_parent(''partman_test.time_taptest_table_p''||to_char(CURRENT_TIMESTAMP, ''YYYY''), ''col3'', ''time'', ''daily'', p_premake := 2, p_epoch := true)', ARRAY[true], 'Subpartitioning partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')||' should return true'); SELECT results_eq('SELECT create_sub_parent(''partman_test.time_taptest_table_p''||to_char(CURRENT_TIMESTAMP+''1 year''::interval, ''YYYY''), ''col3'', ''time'', ''daily'', p_premake := 2, p_epoch := true)', ARRAY[true], 'Subpartitioning partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY')||' should return true'); SELECT results_eq('SELECT create_sub_parent(''partman_test.time_taptest_table_p''||to_char(CURRENT_TIMESTAMP+''2 years''::interval, ''YYYY''), ''col3'', ''time'', ''daily'', p_premake := 2, p_epoch := true)', ARRAY[true], 'Subpartitioning partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||' should return true'); SELECT results_eq('SELECT create_sub_parent(''partman_test.time_taptest_table_p''||to_char(CURRENT_TIMESTAMP-''1 year''::interval, ''YYYY''), ''col3'', ''time'', ''daily'', p_premake := 2, p_epoch := true)', ARRAY[true], 'Subpartitioning partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||' should return true'); SELECT results_eq('SELECT create_sub_parent(''partman_test.time_taptest_table_p''||to_char(CURRENT_TIMESTAMP-''2 years''::interval, ''YYYY''), ''col3'', ''time'', ''daily'', p_premake := 2, p_epoch := true)', ARRAY[true], 'Subpartitioning partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')||' should return true'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM')||'_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM')||' exists')||'_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM')||'_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM')||'_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM')||'_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM')||'_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||' does not exists'); -- This test may fail around the end of the year or the end of some months since the minimal partition for the next year or next month was created. That's fine SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM')||'_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM')||'_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||' does not exist (this test may fail around year or month boundaries. See comment in test code)'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM')||'_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM')||'_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM')||'_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM')||'_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM')||'_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM')||'_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||' does not exist'); -- Check that previous and future years had the minimal partition made -- year +/- 1 tests may fail around year boundary. Tables may or may not exist depending on premake. That's fine. Should be ok for further in the future. SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'1 years'::interval, 'YYYY')||'_01_p'||to_char(CURRENT_TIMESTAMP+'1 years'::interval, 'YYYY')||'_01_01', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'1 years'::interval, 'YYYY')||'_01_p'||to_char(CURRENT_TIMESTAMP+'1 years'::interval, 'YYYY')||'_01_01 exists (this test may fail around year boundary. See comment in test code)'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'1 years'::interval, 'YYYY')||'_01_p'||to_char(CURRENT_TIMESTAMP+'1 years'::interval, 'YYYY')||'_01_02', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'1 years'::interval, 'YYYY')||'_01_p'||to_char(CURRENT_TIMESTAMP+'1 years'::interval, 'YYYY')||'_01_02 does not exists (this test may fail around year boundary. See comment in test code)'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||'_01_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||'_01_01', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||'_01_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||'_01_01 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||'_01_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||'_01_02', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||'_01_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||'_01_02 does not exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||'_01_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||'_01_01', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||'_01_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||'_01_01 exists (this test may fail around year boundary. See comment in test code)'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||'_01_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||'_01_02', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||'_01_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||'_01_02 exists (this test may fail around year boundary. See comment in test code)'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')||'_01_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')||'_01_01', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'2 year'::interval, 'YYYY')||'_01_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')||'_01_01 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')||'_01_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')||'_01_02', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'2 year'::interval, 'YYYY')||'_01_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')||'_01_02 does not exists'); SELECT results_eq('SELECT partition_data_time(''partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM')||''')::int', ARRAY[10], 'Check that partitioning function returns correct count of rows moved (monthly subparent)'); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM'), 'Check data got moved out of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM')||'_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM')||'_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); -- Check subpart config SELECT results_eq('SELECT sub_parent FROM part_config_sub ORDER BY sub_parent', ARRAY['partman_test.time_taptest_table', 'partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY'), 'partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY'), 'partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY'), 'partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY'), 'partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')], 'Check that part_config_sub has all tables configured as needed'); INSERT INTO partman_test.time_taptest_table (col1, col2, col3) VALUES (generate_series(11,20), 'stuff', extract('epoch' from CURRENT_TIMESTAMP+'1 day'::interval)); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table', 'Check new data did not go into parent time_taptest_table'); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day', 'YYYY'), 'Check new data did not go into subparent time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day', 'YYYY')); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day', 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'1 day', 'YYYY_MM'), 'Check new data did not go into subparent time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day', 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'1 day', 'YYYY_MM')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day', 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'1 day', 'YYYY_MM')||'_p'||to_char(CURRENT_TIMESTAMP+'1 day', 'YYYY_MM_DD'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day', 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'1 day', 'YYYY_MM')||'_p'||to_char(CURRENT_TIMESTAMP+'1 day', 'YYYY_MM_DD')); UPDATE part_config SET premake = 3, optimize_trigger = 3 WHERE parent_table LIKE 'partman_test.time_taptest_table%' AND partition_type = 'time'; SELECT run_maintenance(); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM')||'_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM')||'_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||' exists'); -- Check that future year had the minimal partition made SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')||'_01', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')||'_01 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')||'_01_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')||'_01_01', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')||'_01_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')||'_01_01 exists'); -- Check subpart config SELECT results_eq('SELECT sub_parent FROM part_config_sub ORDER BY sub_parent', ARRAY['partman_test.time_taptest_table', 'partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY'), 'partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY'), 'partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY'), 'partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY'), 'partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY'), 'partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')], 'Check that part_config_sub has all tables configured as needed'); INSERT INTO partman_test.time_taptest_table (col1, col2, col3) VALUES (generate_series(21,30), 'stuff', extract('epoch' from CURRENT_TIMESTAMP+'3 years'::interval)); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table', 'Check new data did not go into parent time_taptest_table'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years', 'YYYY'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years', 'YYYY')||'. Data should have gone here since monthly subpartition for it does not exist. This test may fail in January since that monthly partition should exist.'); -- Move data from yearly parent table and create appropriate monthly child for it SELECT results_eq('SELECT partition_data_time(''partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')||''')::int', ARRAY[10], 'Check that partitioning function returns correct count of rows moved from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')||'. This test may fail in January since that monthly partition should exist.'); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years', 'YYYY'), 'Check new data did not go into subparent time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years', 'YYYY')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years', 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'3 years', 'YYYY_MM'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years', 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'3 years', 'YYYY_MM')||'. Data should have gone here since daily subpartition for it does not exist.'); -- Move data from monthly parent table and create appropriate daily child for it SELECT results_eq('SELECT partition_data_time(''partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'3 years', 'YYYY_MM')||''')::int', ARRAY[10], 'Check that partitioning function returns correct count of rows moved from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'3 years', 'YYYY_MM')); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years', 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'3 years', 'YYYY_MM'), 'Check new data did not go into subparent time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years', 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'3 years', 'YYYY_MM')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years', 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'3 years', 'YYYY_MM')||'_p'||to_char(CURRENT_TIMESTAMP+'3 years', 'YYYY_MM_DD'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years', 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'3 years', 'YYYY_MM')||'_p'||to_char(CURRENT_TIMESTAMP+'3 years', 'YYYY_MM_DD')); /* -- Disabled test for now. New years makes testing undo functions hard. Would be calling undo on year+1_01 twice and second one would fail. SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table', ARRAY[30], 'Check count from top parent'); SELECT throws_ok('SELECT undo_partition_time(''partman_test.time_taptest_table_p''||to_char(CURRENT_TIMESTAMP+''3 years''::interval, ''YYYY''), 20, p_keep_table := false)', 'P0001', 'Child table for this parent has child table(s) itself (partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')||'_01). Run undo partitioning on this table or remove inheritance first to ensure all data is properly moved to parent CONTEXT: SQL statement "SELECT undo_partition_time(''partman_test.time_taptest_table_p''||to_char(CURRENT_TIMESTAMP+''3 years''::interval, ''YYYY''), 20, p_keep_table := false)" PL/pgSQL function throws_ok(text,character,text,text) line 16 at EXECUTE statement DETAIL: HINT: ', 'Check that undoing partitions is prevented if subpartitions still exist'); */ SELECT * FROM finish(); ROLLBACK; pg_partman-2.2.2/test/test-time-epoch-weekly.sql000066400000000000000000000520211262146621700216610ustar00rootroot00000000000000-- ########## 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 optimize_trigger higher than premake. No real way to test that this works (due to hybrid dynamic function), but doing it to test that it makes a proper trigger without any issues. \set ON_ERROR_ROLLBACK 1 \set ON_ERROR_STOP true BEGIN; SELECT set_config('search_path','partman, public',false); SELECT plan(68); CREATE SCHEMA partman_test; CREATE SCHEMA partman_retention_test; CREATE TABLE partman_test.time_taptest_table (col1 int primary key, col2 text, col3 int NOT NULL DEFAULT extract('epoch' from CURRENT_TIMESTAMP)::int); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(1,10), extract('epoch' from CURRENT_TIMESTAMP - '8 weeks'::interval)::int); SELECT create_parent('partman_test.time_taptest_table', 'col3', 'time', 'weekly', '{"col1"}', p_epoch := true , p_premake := 2, p_start_partition := to_char(CURRENT_TIMESTAMP-'8 weeks'::interval, 'YYYY-MM-DD HH24:MI:SS')); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW')||' exists (+1 weeks)'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')||' exists (+2 weeks)'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW')||' does not exist (+3 weeks)'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW')||' exists (-1 weeks)'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')||' exists (-2 weeks)'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')||' exists (-3 weeks)'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')||' exists (-4 weeks)'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW')||' exists (-5 weeks)'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW')||' exists (-6 weeks)'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'IYYY"w"IW')||' exists (-7 weeks)'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'8 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'8 weeks'::interval, 'IYYY"w"IW')||' exists (-8 weeks)'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'9 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'9 weeks'::interval, 'IYYY"w"IW')||' does not exist (-9 weeks)'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW')||' (+1 weeks)'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')||' (+2 weeks)'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW')||' (-1 weeks)'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')||' (-2 weeks)'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')||' (-3 weeks)'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')||' (-4 weeks)'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW')||' (-5 weeks)'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW')||' (-6 weeks)'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'IYYY"w"IW'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'IYYY"w"IW')||' (-7 weeks)'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'8 weeks'::interval, 'IYYY"w"IW'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'8 weeks'::interval, 'IYYY"w"IW')||' (-8 weeks)'); SELECT results_eq('SELECT partition_data_time(''partman_test.time_taptest_table'')::int', ARRAY[10], 'Check that partitioning function returns correct count of rows moved'); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table', 'Check that parent table has had data moved to partition'); 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, 'IYYY"w"IW'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'8 weeks'::interval, 'IYYY"w"IW')||' (-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 partition_data_time('partman_test.time_taptest_table', 20); 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, 'IYYY"w"IW'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'IYYY"w"IW')||' (-7 weeks)'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW'), ARRAY[5], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW')||' (-6 weeks)'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW'), ARRAY[5], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW')||' (-5 weeks)'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW'), ARRAY[7], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')||' (-4 weeks)'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW'), ARRAY[12], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')||' (-3 weeks)'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW'), ARRAY[21], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')||' (-2 weeks)'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW'), ARRAY[15], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW')||' (-1 weeks)'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW'), ARRAY[15], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW')||' (+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 -- Set optimize_trigger higher than premake to ensure this works as intended UPDATE part_config SET premake = 3, optimize_constraint = 5, optimize_trigger = 8 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, 'IYYY"w"IW'), 'col1' , 'Check for additional constraint on col1 on time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')||' (-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, 'IYYY"w"IW')); SELECT apply_constraints('partman_test.time_taptest_table', 'partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'IYYY"w"IW')); SELECT apply_constraints('partman_test.time_taptest_table', 'partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW')); SELECT apply_constraints('partman_test.time_taptest_table', 'partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW')); SELECT col_has_check('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'8 weeks'::interval, 'IYYY"w"IW'), 'col1' , 'Check for additional constraint on col1 on time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'8 weeks'::interval, 'IYYY"w"IW')); SELECT col_has_check('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'IYYY"w"IW'), 'col1' , 'Check for additional constraint on col1 on time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'IYYY"w"IW')); SELECT col_has_check('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW'), 'col1' , 'Check for additional constraint on col1 on time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW')); SELECT col_has_check('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW'), 'col1' , 'Check for additional constraint on col1 on time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW')); 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, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')||' 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, 'IYYY"w"IW'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')||' (+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, 'IYYY"w"IW'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')||' (+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', 'Check that parent 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, 'IYYY"w"IW'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')||' (+4 weeks)'); SELECT col_has_check('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW'), 'col1' , 'Check for additional constraint on col1 on time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')||' exists (+4 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+'4 weeks'::interval, 'IYYY"w"IW'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')||' (+4 weeks)'); 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 ONLY partman_test.time_taptest_table', 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, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')||' does not exist (-4 weeks)'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW')||' does not exist (-5 weeks)'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW')||' does not exist (-6 weeks)'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'IYYY"w"IW')||' does not exist (-7 weeks)'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'8 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'8 weeks'::interval, 'IYYY"w"IW')||' 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, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')||' does not exist (-3 weeks)'); SELECT has_table('partman_retention_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')||' got moved to new schema (-3 weeks)'); SELECT undo_partition_time('partman_test.time_taptest_table', 20, p_keep_table := false); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table', ARRAY[92], 'Check count from parent table after undo'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW')||' does not exist (now)'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW')||' does not exist (+1 weeks)'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')||' does not exist (+2 weeks)'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')||' does not exist (+3 weeks)'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')||' does not exist (+4 weeks)'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW')||' does not exist (-1 weeks)'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')||' does not exist (-2 weeks)'); SELECT * FROM finish(); ROLLBACK; pg_partman-2.2.2/test/test-time-half-hour-trunc.sql000066400000000000000000002514031262146621700223100ustar00rootroot00000000000000-- ########## TIME HALF HOUR TESTS ########## -- Other test: Long name truncation \set ON_ERROR_ROLLBACK 1 \set ON_ERROR_STOP true BEGIN; SELECT set_config('search_path','partman, public',false); SELECT plan(151); CREATE SCHEMA partman_test; CREATE SCHEMA partman_retention_test; CREATE ROLE partman_basic; CREATE ROLE partman_revoke; CREATE ROLE partman_owner; CREATE TABLE partman_test.time_taptest_table_123456789012345678901234567901234567890 (col1 int primary key, col2 text, col3 timestamptz NOT NULL DEFAULT now()); INSERT INTO partman_test.time_taptest_table_123456789012345678901234567901234567890 (col1, col3) VALUES (generate_series(1,10), CURRENT_TIMESTAMP); GRANT SELECT,INSERT,UPDATE ON partman_test.time_taptest_table_123456789012345678901234567901234567890 TO partman_basic; GRANT ALL ON partman_test.time_taptest_table_123456789012345678901234567901234567890 TO partman_revoke; SELECT create_parent('partman_test.time_taptest_table_123456789012345678901234567901234567890', 'col3', 'time', 'half-hour'); -- Must run_maintenance because when interval time is between 1 hour and 1 minute, the first partition name done by above is always the nearest hour rounded down SELECT run_maintenance(); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP), 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI')||' does not exist'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'30 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'30 mins'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'60 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'60 mins'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'90 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'90 mins'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'120 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'120 mins'::interval, 'YYYY_MM_DD_HH24MI')||' 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_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'150 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'150 mins'::interval, 'YYYY_MM_DD_HH24MI')||' does not exist'); */ SELECT col_is_pk('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'30 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'30 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'60 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'60 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'90 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'90 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'120 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'120 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'30 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'30 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'60 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'60 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'90 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'90 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'120 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'120 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'30 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'30 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'60 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'60 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'90 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'90 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'120 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'120 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT results_eq('SELECT partition_data_time(''partman_test.time_taptest_table_123456789012345678901234567901234567890'')::int', ARRAY[10], 'Check that partitioning function returns correct count of rows moved'); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table_123456789012345678901234567901234567890', 'Check that parent table has had data moved to partition'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_123456789012345678901234567901234567890', ARRAY[10], 'Check count from parent table'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYY_MM_DD_HH24MI'), ARRAY[10], 'Check count from time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYY_MM_DD_HH24MI')); REVOKE INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER ON partman_test.time_taptest_table_123456789012345678901234567901234567890 FROM partman_revoke; INSERT INTO partman_test.time_taptest_table_123456789012345678901234567901234567890 (col1, col3) VALUES (generate_series(11,20), CURRENT_TIMESTAMP + '30 mins'::interval); INSERT INTO partman_test.time_taptest_table_123456789012345678901234567901234567890 (col1, col3) VALUES (generate_series(21,25), CURRENT_TIMESTAMP + '60 mins'::interval); INSERT INTO partman_test.time_taptest_table_123456789012345678901234567901234567890 (col1, col3) VALUES (generate_series(26,30), CURRENT_TIMESTAMP + '90 mins'::interval); INSERT INTO partman_test.time_taptest_table_123456789012345678901234567901234567890 (col1, col3) VALUES (generate_series(31,37), CURRENT_TIMESTAMP + '120 mins'::interval); INSERT INTO partman_test.time_taptest_table_123456789012345678901234567901234567890 (col1, col3) VALUES (generate_series(40,49), CURRENT_TIMESTAMP - '30 mins'::interval); INSERT INTO partman_test.time_taptest_table_123456789012345678901234567901234567890 (col1, col3) VALUES (generate_series(50,70), CURRENT_TIMESTAMP - '60 mins'::interval); INSERT INTO partman_test.time_taptest_table_123456789012345678901234567901234567890 (col1, col3) VALUES (generate_series(71,85), CURRENT_TIMESTAMP - '90 mins'::interval); INSERT INTO partman_test.time_taptest_table_123456789012345678901234567901234567890 (col1, col3) VALUES (generate_series(86,100), CURRENT_TIMESTAMP - '120 mins'::interval); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table_123456789012345678901234567901234567890', 'Check that parent table has had no data inserted to it'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY[10], 'Check count from time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY[5], 'Check count from time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY[5], 'Check count from time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY[7], 'Check count from time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'30 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY[10], 'Check count from time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'30 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'60 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY[21], 'Check count from time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'60 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'90 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY[15], 'Check count from time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'90 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'120 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY[15], 'Check count from time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'120 mins'::interval, 'YYYY_MM_DD_HH24MI')); UPDATE part_config SET premake = 5, optimize_trigger = 5 WHERE parent_table = 'partman_test.time_taptest_table_123456789012345678901234567901234567890'; SELECT run_maintenance(); INSERT INTO partman_test.time_taptest_table_123456789012345678901234567901234567890 (col1, col3) VALUES (generate_series(101,122), CURRENT_TIMESTAMP + '150 mins'::interval); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 mins'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 mins'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 mins'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'270 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'270 mins'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 mins'::interval, 'YYYY_MM_DD_HH24MI')||' does not exist'); SELECT col_is_pk('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'270 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'270 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table_123456789012345678901234567901234567890', 'Check that parent table has had no data inserted to it'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY[22], 'Check count from time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'270 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'270 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'270 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'270 mins'::interval, 'YYYY_MM_DD_HH24MI')); GRANT DELETE ON partman_test.time_taptest_table_123456789012345678901234567901234567890 TO partman_basic; REVOKE ALL ON partman_test.time_taptest_table_123456789012345678901234567901234567890 FROM partman_revoke; ALTER TABLE partman_test.time_taptest_table_123456789012345678901234567901234567890 OWNER TO partman_owner; UPDATE part_config SET premake = 6, optimize_trigger = 6 WHERE parent_table = 'partman_test.time_taptest_table_123456789012345678901234567901234567890'; SELECT run_maintenance(); INSERT INTO partman_test.time_taptest_table_123456789012345678901234567901234567890 (col1, col3) VALUES (generate_series(123,150), CURRENT_TIMESTAMP + '180 mins'::interval); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table_123456789012345678901234567901234567890', 'Check that parent table has had no data inserted to it'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_123456789012345678901234567901234567890', ARRAY[148], 'Check count from parent table'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY[28], 'Check count from time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 mins'::interval, 'YYYY_MM_DD_HH24MI')); -- Inserted new data above, so 2 additional future partitions were made instead of just 1 SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 mins'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'330 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'330 mins'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'360 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'360 mins'::interval, 'YYYY_MM_DD_HH24MI')||' does not exists'); SELECT col_is_pk('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'330 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'330 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'330 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'330 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'330 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'330 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'330 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'330 mins'::interval, 'YYYY_MM_DD_HH24MI')); INSERT INTO partman_test.time_taptest_table_123456789012345678901234567901234567890 (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_123456789012345678901234567901234567890', ARRAY[11], 'Check that data outside trigger scope goes to parent'); SELECT reapply_privileges('partman_test.time_taptest_table_123456789012345678901234567901234567890'); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'30 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'30 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'60 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'60 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'90 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'90 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'120 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'120 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'270 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'270 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'330 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'330 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'270 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'270 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'330 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'330 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'30 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'30 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'60 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'60 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'90 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'90 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'120 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'120 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'270 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'270 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'330 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'330 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT drop_partition_time('partman_test.time_taptest_table_123456789012345678901234567901234567890', '90 mins', p_keep_table := false); SELECT hasnt_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'120 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'120 mins'::interval, 'YYYY_MM_DD_HH24MI')||' does not exist'); UPDATE part_config SET retention = '60 mins'::interval WHERE parent_table = 'partman_test.time_taptest_table_123456789012345678901234567901234567890'; SELECT drop_partition_time('partman_test.time_taptest_table_123456789012345678901234567901234567890', p_retention_schema := 'partman_retention_test'); SELECT hasnt_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'90 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'90 mins'::interval, 'YYYY_MM_DD_HH24MI')||' does not exist'); SELECT has_table('partman_retention_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'90 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'90 mins'::interval, 'YYYY_MM_DD_HH24MI')||' got moved to new schema'); SELECT undo_partition_time('partman_test.time_taptest_table_123456789012345678901234567901234567890', 20, p_keep_table := false); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table_123456789012345678901234567901234567890', ARRAY[129], 'Check count from parent table after undo'); SELECT hasnt_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYY_MM_DD_HH24MI')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'30 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'30 mins'::interval, 'YYYY_MM_DD_HH24MI')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'60 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)-'60 mins'::interval, 'YYYY_MM_DD_HH24MI')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 mins'::interval, 'YYYY_MM_DD_HH24MI')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 mins'::interval, 'YYYY_MM_DD_HH24MI')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 mins'::interval, 'YYYY_MM_DD_HH24MI')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'270 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'270 mins'::interval, 'YYYY_MM_DD_HH24MI')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 mins'::interval, 'YYYY_MM_DD_HH24MI')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'330 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'330 mins'::interval, 'YYYY_MM_DD_HH24MI')||' does not exist'); SELECT * FROM finish(); --ROLLBACK; pg_partman-2.2.2/test/test-time-half-hour.sql000066400000000000000000001352351262146621700211630ustar00rootroot00000000000000-- ########## TIME HALF-HOUR TESTS ########## \set ON_ERROR_ROLLBACK 1 \set ON_ERROR_STOP true BEGIN; SELECT set_config('search_path','partman, public',false); SELECT plan(94); CREATE SCHEMA partman_test; CREATE ROLE partman_basic; CREATE ROLE partman_revoke; CREATE ROLE partman_owner; CREATE TABLE partman_test.time_taptest_table (col1 int primary key, col2 text, col3 timestamptz NOT NULL DEFAULT now()); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(1,10), CURRENT_TIMESTAMP); 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', 'time', 'half-hour'); -- Must run_maintenance because when interval time is between 1 hour and 1 minute, the first partition name done by above is always the nearest hour rounded down SELECT run_maintenance(); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI')||' does not exist'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT results_eq('SELECT partition_data_time(''partman_test.time_taptest_table'')::int', ARRAY[10], 'Check that partitioning function returns correct count of rows moved'); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table', 'Check that parent table has had data moved to partition'); 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('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYY_MM_DD_HH24MI'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYY_MM_DD_HH24MI')); REVOKE INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER ON partman_test.time_taptest_table FROM partman_revoke; INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(11,20), CURRENT_TIMESTAMP + '30 mins'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(21,25), CURRENT_TIMESTAMP + '60 mins'::interval); 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', ARRAY[25], 'Check count from time_taptest_table'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY[5], 'Check count from time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI')); UPDATE part_config SET premake = 5, optimize_trigger = 5 WHERE parent_table = 'partman_test.time_taptest_table'; SELECT run_maintenance(); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 mins'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 mins'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 mins'::interval, 'YYYY_MM_DD_HH24MI')||' does not exist'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 mins'::interval, 'YYYY_MM_DD_HH24MI')); GRANT DELETE ON partman_test.time_taptest_table TO partman_basic; REVOKE ALL ON partman_test.time_taptest_table FROM partman_revoke; ALTER TABLE partman_test.time_taptest_table OWNER TO partman_owner; UPDATE part_config SET premake = 6, optimize_trigger = 6 WHERE parent_table = 'partman_test.time_taptest_table'; SELECT run_maintenance(); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 mins'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'300 mins'::interval, 'YYYY_MM_DD_HH24MI')||' does not exists'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 mins'::interval, 'YYYY_MM_DD_HH24MI')); 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', ARRAY[11], 'Check that data outside trigger scope goes to parent'); SELECT reapply_privileges('partman_test.time_taptest_table'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT undo_partition_time('partman_test.time_taptest_table', 20); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 mins'::interval, 'YYYY_MM_DD_HH24MI')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'180 mins'::interval, 'YYYY_MM_DD_HH24MI')||' is empty'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'210 mins'::interval, 'YYYY_MM_DD_HH24MI')||' is empty'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'240 mins'::interval, 'YYYY_MM_DD_HH24MI')||' is empty'); SELECT * FROM finish(); ROLLBACK; pg_partman-2.2.2/test/test-time-hourly-trunc.sql000066400000000000000000001344701262146621700217510ustar00rootroot00000000000000-- ########## TIME TESTS ########## -- Other tests: Long name truncation \set ON_ERROR_ROLLBACK 1 \set ON_ERROR_STOP true BEGIN; SELECT set_config('search_path','partman, public',false); SELECT plan(122); CREATE SCHEMA partman_test; CREATE ROLE partman_basic; CREATE ROLE partman_revoke; CREATE ROLE partman_owner; CREATE TABLE partman_test.time_taptest_table_1234567890123456789012345678901234567890 (col1 int primary key, col2 text, col3 timestamptz NOT NULL DEFAULT now()); INSERT INTO partman_test.time_taptest_table_1234567890123456789012345678901234567890 (col1, col3) VALUES (generate_series(1,10), CURRENT_TIMESTAMP); GRANT SELECT,INSERT,UPDATE ON partman_test.time_taptest_table_1234567890123456789012345678901234567890 TO partman_basic; GRANT ALL ON partman_test.time_taptest_table_1234567890123456789012345678901234567890 TO partman_revoke; SELECT create_parent('partman_test.time_taptest_table_1234567890123456789012345678901234567890', 'col3', 'time', 'hourly'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP), 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP), 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'1 hour'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'1 hour'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'2 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'2 hours'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'3 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'3 hours'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'4 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'4 hours'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'1 hour'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'1 hour'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'2 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'2 hours'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'3 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'3 hours'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'4 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'4 hours'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'5 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'5 hours'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT col_is_pk('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP), 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP), 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'1 hour'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'1 hour'::interval, 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'2 hours'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'2 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'3 hours'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'3 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'4 hours'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'4 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'1 hour'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'1 hour'::interval, 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'2 hours'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'2 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'3 hours'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'3 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'4 hours'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'4 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP), 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP), 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'1 hour'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'1 hour'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'2 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'2 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'3 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'3 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'4 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'4 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'1 hour'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'1 hour'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'2 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'2 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'3 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'3 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'4 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'4 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP), 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP), 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'1 hour'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'1 hour'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'2 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'2 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'3 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'3 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'4 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'4 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT results_eq('SELECT partition_data_time(''partman_test.time_taptest_table_1234567890123456789012345678901234567890'')::int', ARRAY[10], 'Check that partitioning function returns correct count of rows moved'); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table_1234567890123456789012345678901234567890', 'Check that parent table has had data moved to partition'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_1234567890123456789012345678901234567890', ARRAY[10], 'Check count from parent table'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP), 'YYYY_MM_DD_HH24MI'), ARRAY[10], 'Check count from time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP), 'YYYY_MM_DD_HH24MI')); REVOKE INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER ON partman_test.time_taptest_table_1234567890123456789012345678901234567890 FROM partman_revoke; INSERT INTO partman_test.time_taptest_table_1234567890123456789012345678901234567890 (col1, col3) VALUES (generate_series(11,20), CURRENT_TIMESTAMP + '1 hour'::interval); INSERT INTO partman_test.time_taptest_table_1234567890123456789012345678901234567890 (col1, col3) VALUES (generate_series(21,25), CURRENT_TIMESTAMP + '2 hours'::interval); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table_1234567890123456789012345678901234567890', 'Check that parent table has had no data inserted to it'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_1234567890123456789012345678901234567890', ARRAY[25], 'Check count from time_taptest_table'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'1 hour'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY[10], 'Check count from time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'1 hour'::interval, 'YYYY_MM_DD_HH24MI')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'2 hours'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY[5], 'Check count from time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'2 hours'::interval, 'YYYY_MM_DD_HH24MI')); UPDATE part_config SET premake = 5, optimize_trigger = 5 WHERE parent_table = 'partman_test.time_taptest_table_1234567890123456789012345678901234567890'; SELECT run_maintenance(); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'5 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'5 hours'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'6 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'6 hours'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'7 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'7 hours'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'8 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'8 hours'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT col_is_pk('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'5 hours'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'5 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'6 hours'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'6 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'7 hours'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'7 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'5 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'5 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'6 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'6 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'7 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'7 hours'::interval, 'YYYY_MM_DD_HH24MI')); GRANT DELETE ON partman_test.time_taptest_table_1234567890123456789012345678901234567890 TO partman_basic; REVOKE ALL ON partman_test.time_taptest_table_1234567890123456789012345678901234567890 FROM partman_revoke; ALTER TABLE partman_test.time_taptest_table_1234567890123456789012345678901234567890 OWNER TO partman_owner; UPDATE part_config SET premake = 6, optimize_trigger = 6 WHERE parent_table = 'partman_test.time_taptest_table_1234567890123456789012345678901234567890'; SELECT run_maintenance(); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'8 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'8 hours'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'9 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'9 hours'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT col_is_pk('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'8 hours'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'8 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP), 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP), 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'8 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'8 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'8 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'8 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'8 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'8 hours'::interval, 'YYYY_MM_DD_HH24MI')); INSERT INTO partman_test.time_taptest_table_1234567890123456789012345678901234567890 (col1, col3) VALUES (generate_series(200,210), CURRENT_TIMESTAMP + '20 hours'::interval); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table_1234567890123456789012345678901234567890', ARRAY[11], 'Check that data outside trigger scope goes to parent'); SELECT reapply_privileges('partman_test.time_taptest_table_1234567890123456789012345678901234567890'); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP), 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP), 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'1 hour'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'1 hour'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'2 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'2 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'3 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'3 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'4 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'4 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'1 hour'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'1 hour'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'2 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'2 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'3 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'3 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'4 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'4 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'5 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'5 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'6 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'6 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'7 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'7 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'8 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'8 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP), 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP), 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'1 hour'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'1 hour'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'2 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'2 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'3 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'3 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'4 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'4 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'1 hour'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'1 hour'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'2 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'2 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'3 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'3 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'4 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'4 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'5 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'5 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'6 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'6 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'7 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'7 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'8 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'8 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP), 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP), 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'1 hour'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'1 hour'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'2 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'2 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'3 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'3 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'4 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'4 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'1 hour'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'1 hour'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'2 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'2 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'3 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'3 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'4 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'4 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'5 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'5 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'6 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'6 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'7 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'7 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'8 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'8 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT undo_partition_time('partman_test.time_taptest_table_1234567890123456789012345678901234567890', 20); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'1 hour'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'1 hour'::interval, 'YYYY_MM_DD_HH24MI')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'1 hour'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'1 hour'::interval, 'YYYY_MM_DD_HH24MI')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'2 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'2 hours'::interval, 'YYYY_MM_DD_HH24MI')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'2 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'2 hours'::interval, 'YYYY_MM_DD_HH24MI')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'3 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'3 hours'::interval, 'YYYY_MM_DD_HH24MI')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'3 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'3 hours'::interval, 'YYYY_MM_DD_HH24MI')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'4 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'4 hours'::interval, 'YYYY_MM_DD_HH24MI')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'4 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'4 hours'::interval, 'YYYY_MM_DD_HH24MI')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'1 hour'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'1 hour'::interval, 'YYYY_MM_DD_HH24MI')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'1 hour'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'1 hour'::interval, 'YYYY_MM_DD_HH24MI')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'2 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'2 hours'::interval, 'YYYY_MM_DD_HH24MI')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'2 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'2 hours'::interval, 'YYYY_MM_DD_HH24MI')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'3 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'3 hours'::interval, 'YYYY_MM_DD_HH24MI')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'3 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'3 hours'::interval, 'YYYY_MM_DD_HH24MI')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'4 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'4 hours'::interval, 'YYYY_MM_DD_HH24MI')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'4 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'4 hours'::interval, 'YYYY_MM_DD_HH24MI')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'5 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'5 hours'::interval, 'YYYY_MM_DD_HH24MI')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'5 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'5 hours'::interval, 'YYYY_MM_DD_HH24MI')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'6 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'6 hours'::interval, 'YYYY_MM_DD_HH24MI')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'6 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'6 hours'::interval, 'YYYY_MM_DD_HH24MI')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'7 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'7 hours'::interval, 'YYYY_MM_DD_HH24MI')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'7 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'7 hours'::interval, 'YYYY_MM_DD_HH24MI')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'8 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'8 hours'::interval, 'YYYY_MM_DD_HH24MI')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'8 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_123456789012345678901234567_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'8 hours'::interval, 'YYYY_MM_DD_HH24MI')||' is empty'); SELECT * FROM finish(); ROLLBACK; pg_partman-2.2.2/test/test-time-hourly.sql000066400000000000000000001170611262146621700206150ustar00rootroot00000000000000-- ########## TIME HOURLY TESTS ########## -- Additional tests: Undo partitioning but keep tables \set ON_ERROR_ROLLBACK 1 \set ON_ERROR_STOP true BEGIN; SELECT set_config('search_path','partman, public',false); SELECT plan(123); CREATE SCHEMA partman_test; CREATE ROLE partman_basic; CREATE ROLE partman_revoke; CREATE ROLE partman_owner; CREATE TABLE partman_test.time_taptest_table (col1 int primary key, col2 text, col3 timestamptz NOT NULL DEFAULT now()); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(1,10), CURRENT_TIMESTAMP); 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', 'time', 'hourly'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP), 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP), 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'1 hour'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'1 hour'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'2 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'2 hours'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'3 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'3 hours'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'4 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'4 hours'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'5 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'5 hours'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'1 hour'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'1 hour'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'2 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'2 hours'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'3 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'3 hours'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'4 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'4 hours'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'5 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'5 hours'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP), 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP), 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'1 hour'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'1 hour'::interval, 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'2 hours'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'2 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'3 hours'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'3 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'4 hours'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'4 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP), 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP), 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'1 hour'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'1 hour'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'2 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'2 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'3 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'3 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'4 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'4 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP), 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP), 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'1 hour'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'1 hour'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'2 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'2 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'3 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'3 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'4 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'4 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT results_eq('SELECT partition_data_time(''partman_test.time_taptest_table'')::int', ARRAY[10], 'Check that partitioning function returns correct count of rows moved'); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table', 'Check that parent table has had data moved to partition'); 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('hour', CURRENT_TIMESTAMP), 'YYYY_MM_DD_HH24MI'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP), 'YYYY_MM_DD_HH24MI')); REVOKE INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER ON partman_test.time_taptest_table FROM partman_revoke; INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(11,20), CURRENT_TIMESTAMP + '1 hour'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(21,25), CURRENT_TIMESTAMP + '2 hours'::interval); 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', ARRAY[25], 'Check count from time_taptest_table'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'1 hour'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'1 hour'::interval, 'YYYY_MM_DD_HH24MI')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'2 hours'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY[5], 'Check count from time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'2 hours'::interval, 'YYYY_MM_DD_HH24MI')); UPDATE part_config SET premake = 5, optimize_trigger = 5 WHERE parent_table = 'partman_test.time_taptest_table'; SELECT run_maintenance(); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'5 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'5 hours'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'6 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'6 hours'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'7 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'7 hours'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'8 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'8 hours'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'5 hours'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'5 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'6 hours'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'6 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'7 hours'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'7 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP), 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP), 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'1 hour'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'1 hour'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'2 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'2 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'3 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'3 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'4 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'4 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'5 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'5 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'6 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'6 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'7 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'7 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'5 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'5 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'6 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'6 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'7 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'7 hours'::interval, 'YYYY_MM_DD_HH24MI')); GRANT DELETE ON partman_test.time_taptest_table TO partman_basic; REVOKE ALL ON partman_test.time_taptest_table FROM partman_revoke; ALTER TABLE partman_test.time_taptest_table OWNER TO partman_owner; UPDATE part_config SET premake = 6, optimize_trigger = 6 WHERE parent_table = 'partman_test.time_taptest_table'; SELECT run_maintenance(); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'8 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'8 hours'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'9 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'9 hours'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'8 hours'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'8 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP), 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP), 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'8 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'8 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'8 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'8 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'8 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'8 hours'::interval, 'YYYY_MM_DD_HH24MI')); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(200,210), CURRENT_TIMESTAMP + '20 hours'::interval); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table', ARRAY[11], 'Check that data outside trigger scope goes to parent'); SELECT reapply_privileges('partman_test.time_taptest_table'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP), 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP), 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'1 hour'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'1 hour'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'2 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'2 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'3 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'3 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'4 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'4 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'1 hour'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'1 hour'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'2 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'2 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'3 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'3 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'4 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'4 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'5 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'5 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'6 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'6 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'7 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'7 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'8 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'8 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP), 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP), 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'1 hour'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'1 hour'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'2 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'2 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'3 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'3 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'4 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'4 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'1 hour'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'1 hour'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'2 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'2 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'3 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'3 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'4 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'4 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'5 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'5 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'6 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'6 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'7 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'7 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'8 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'8 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP), 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP), 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'1 hour'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'1 hour'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'2 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'2 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'3 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'3 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'4 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'4 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'1 hour'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'1 hour'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'2 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'2 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'3 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'3 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'4 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'4 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'5 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'5 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'6 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'6 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'7 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'7 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'8 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'8 hours'::interval, 'YYYY_MM_DD_HH24MI')); SELECT undo_partition_time('partman_test.time_taptest_table', 20); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'1 hour'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'1 hour'::interval, 'YYYY_MM_DD_HH24MI')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'1 hour'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'1 hour'::interval, 'YYYY_MM_DD_HH24MI')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'2 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'2 hours'::interval, 'YYYY_MM_DD_HH24MI')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'2 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'2 hours'::interval, 'YYYY_MM_DD_HH24MI')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'3 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'3 hours'::interval, 'YYYY_MM_DD_HH24MI')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'3 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'3 hours'::interval, 'YYYY_MM_DD_HH24MI')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'4 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'4 hours'::interval, 'YYYY_MM_DD_HH24MI')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'4 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)-'4 hours'::interval, 'YYYY_MM_DD_HH24MI')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'1 hour'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'1 hour'::interval, 'YYYY_MM_DD_HH24MI')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'1 hour'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'1 hour'::interval, 'YYYY_MM_DD_HH24MI')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'2 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'2 hours'::interval, 'YYYY_MM_DD_HH24MI')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'2 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'2 hours'::interval, 'YYYY_MM_DD_HH24MI')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'3 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'3 hours'::interval, 'YYYY_MM_DD_HH24MI')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'3 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'3 hours'::interval, 'YYYY_MM_DD_HH24MI')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'4 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'4 hours'::interval, 'YYYY_MM_DD_HH24MI')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'4 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'4 hours'::interval, 'YYYY_MM_DD_HH24MI')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'5 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'5 hours'::interval, 'YYYY_MM_DD_HH24MI')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'5 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'5 hours'::interval, 'YYYY_MM_DD_HH24MI')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'6 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'6 hours'::interval, 'YYYY_MM_DD_HH24MI')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'6 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'6 hours'::interval, 'YYYY_MM_DD_HH24MI')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'7 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'7 hours'::interval, 'YYYY_MM_DD_HH24MI')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'7 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'7 hours'::interval, 'YYYY_MM_DD_HH24MI')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'8 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'8 hours'::interval, 'YYYY_MM_DD_HH24MI')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'8 hours'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP)+'8 hours'::interval, 'YYYY_MM_DD_HH24MI')||' is empty'); SELECT * FROM finish(); ROLLBACK; pg_partman-2.2.2/test/test-time-id-subpart.sql000066400000000000000000002605651262146621700213550ustar00rootroot00000000000000-- ########## TIME PARENT / ID SUBPARENT STATIC TESTS ########## \set ON_ERROR_ROLLBACK 1 \set ON_ERROR_STOP true BEGIN; SELECT set_config('search_path','partman, public',false); SELECT plan(325); CREATE SCHEMA partman_test; CREATE TABLE partman_test.time_taptest_table (col1 int primary key, col2 text, col3 timestamptz NOT NULL DEFAULT now()) WITH (OIDS); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(1,10), CURRENT_TIMESTAMP); SELECT create_parent('partman_test.time_taptest_table', 'col3', 'time', 'daily'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT partition_data_time(''partman_test.time_taptest_table'')::int', ARRAY[10], 'Check that partitioning function returns correct count of rows moved'); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table', 'Check that parent table has had data moved to partition'); 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, 'YYYY_MM_DD'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); -- Create subpartition SELECT partman.create_sub_parent('partman_test.time_taptest_table', 'col1', 'id', '20'); -- Move data to new subpartitions SELECT results_eq('SELECT partition_data_id(''partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||''', p_batch_count := 5)::int', ARRAY[10], 'Check that partitioning function returns correct count of rows moved'); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table', 'Check that parent table has had no data inserted to it'); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'Check that subparent table has had no data inserted to it'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p0', ARRAY[10], 'Check count from partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p0'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p0', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p0 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p20', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p20 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p40', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p40 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p60', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p60 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p80', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p80 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p100', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p100 does not exist'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p0', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p0 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p20', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p20 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p40', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p40 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p60', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p60 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p80', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p80 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p100', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p100 does not exist'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||'_p0', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||'_p0 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||'_p20', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||'_p20 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||'_p40', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||'_p40 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||'_p60', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||'_p60 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||'_p80', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||'_p80 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||'_p0', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||'_p0 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||'_p20', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||'_p20 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||'_p40', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||'_p40 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||'_p60', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||'_p60 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||'_p80', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||'_p80 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||'_p0', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||'_p0 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||'_p20', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||'_p20 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||'_p40', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||'_p40 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||'_p60', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||'_p60 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||'_p80', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||'_p80 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||'_p0', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||'_p0 does not exist'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||'_p0', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||'_p0 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||'_p20', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||'_p20 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||'_p40', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||'_p40 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||'_p60', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||'_p60 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||'_p80', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||'_p80 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||'_p100', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||'_p100 does not exist'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||'_p0', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||'_p0 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||'_p20', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||'_p20 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||'_p40', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||'_p40 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||'_p60', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||'_p60 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||'_p80', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||'_p80 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p0', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p0 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p20', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p20 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p40', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p40 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p60', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p60 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p80', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p80 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p0', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p0 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p20', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p20 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p40', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p40 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p60', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p60 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p80', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p80 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 days'::interval, 'YYYY_MM_DD')||'_p0', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 days'::interval, 'YYYY_MM_DD')||'_p0 does not exist'); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(11,35), CURRENT_TIMESTAMP); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(1,35), CURRENT_TIMESTAMP + '1 day'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(1,35), CURRENT_TIMESTAMP + '2 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(1,35), CURRENT_TIMESTAMP + '3 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(1,35), CURRENT_TIMESTAMP + '4 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(1,35), CURRENT_TIMESTAMP - '1 day'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(1,35), CURRENT_TIMESTAMP - '2 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(1,35), CURRENT_TIMESTAMP - '3 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(1,35), CURRENT_TIMESTAMP - '4 days'::interval); SELECT run_maintenance(); -- Check row counts from all levels SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table', 'Check that parent table has no data'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table', ARRAY[315], 'Check count from parent table'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), ARRAY[35], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), ARRAY[35], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), ARRAY[35], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), ARRAY[35], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), ARRAY[35], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD'), ARRAY[35], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), ARRAY[35], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), ARRAY[35], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), ARRAY[35], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p0', ARRAY[19], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p0'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p20', ARRAY[16], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p20'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p0', ARRAY[19], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p0'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p20', ARRAY[16], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p20'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||'_p0', ARRAY[19], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||'_p0'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||'_p20', ARRAY[16], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||'_p20'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||'_p0', ARRAY[19], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||'_p0'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||'_p20', ARRAY[16], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||'_p20'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||'_p0', ARRAY[19], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||'_p0'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||'_p20', ARRAY[16], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||'_p20'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||'_p0', ARRAY[19], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||'_p0'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||'_p20', ARRAY[16], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||'_p20'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||'_p0', ARRAY[19], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||'_p0'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||'_p20', ARRAY[16], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||'_p20'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p0', ARRAY[19], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p0'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p20', ARRAY[16], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p20'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p0', ARRAY[19], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p0'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p20', ARRAY[16], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p20'); -- Check that new serial child partition was created for each time sub-parent SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p100', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p100 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p100', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p100 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||'_p100', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||'_p100 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||'_p100', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||'_p100 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||'_p100', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||'_p100 exists'); UPDATE part_config SET premake = 5, optimize_trigger = 5 WHERE parent_table = 'partman_test.time_taptest_table'; SELECT run_maintenance(); -- Check for +5,+6,+7,+8,+9 days (since +4 days data exists) new subparent & children. Should only have 0,20,40,60,80 because subparent is empty and starting from zero SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||'_p0', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||'_p0 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||'_p20', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||'_p20 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||'_p40', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||'_p40 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||'_p60', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||'_p60 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||'_p80', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||'_p80 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||'_p100', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||'_p100 does not exist'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')||'_p0', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')||'_p0 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')||'_p20', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')||'_p20 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')||'_p40', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')||'_p40 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')||'_p60', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')||'_p60 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')||'_p80', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')||'_p80 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')||'_p100', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')||'_p100 does not exist'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')||'_p0', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')||'_p0 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')||'_p20', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')||'_p20 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')||'_p40', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')||'_p40 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')||'_p60', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')||'_p60 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')||'_p80', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')||'_p80 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')||'_p100', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')||'_p100 does not exist'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')||'_p0', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')||'_p0 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')||'_p20', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')||'_p20 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')||'_p40', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')||'_p40 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')||'_p60', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')||'_p60 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')||'_p80', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')||'_p80 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')||'_p100', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')||'_p100 does not exist'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD')||'_p0', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD')||'_p0 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD')||'_p20', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD')||'_p20 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD')||'_p40', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD')||'_p40 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD')||'_p60', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD')||'_p60 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD')||'_p80', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD')||'_p80 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD')||'_p100', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD')||'_p100 does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD')||'_p0', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD')||'_p0 does not exist'); -- insert batch 2 INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(36,50), CURRENT_TIMESTAMP); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(36,50), CURRENT_TIMESTAMP + '1 day'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(36,50), CURRENT_TIMESTAMP + '2 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(36,50), CURRENT_TIMESTAMP + '3 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(36,50), CURRENT_TIMESTAMP + '4 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(1,50), CURRENT_TIMESTAMP + '5 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(36,50), CURRENT_TIMESTAMP - '1 day'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(36,50), CURRENT_TIMESTAMP - '2 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(36,50), CURRENT_TIMESTAMP - '3 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(36,50), CURRENT_TIMESTAMP - '4 days'::interval); SELECT run_maintenance(); -- All sub partition sets should be the same now SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||'_p100', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||'_p100 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p120', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p120 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p120', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p120 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||'_p120', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||'_p120 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||'_p120', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||'_p120 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||'_p120', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||'_p120 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||'_p120', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||'_p120 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||'_p120', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||'_p120 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||'_p120', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||'_p120 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p120', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p120 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p120', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p120 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p140', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p140 does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p140', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p140 does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||'_p140', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||'_p140 does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||'_p140', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||'_p140 does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||'_p140', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||'_p140 does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||'_p140', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||'_p140 does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||'_p140', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||'_p140 does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||'_p140', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||'_p140 does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p140', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p140 does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p140', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p140 does not exist'); -- Check row counts from all levels SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table', 'Check that parent table has no data'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table', ARRAY[500], 'Check count from parent table'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), ARRAY[50], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), ARRAY[50], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), ARRAY[50], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), ARRAY[50], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), ARRAY[50], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), ARRAY[50], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD'), ARRAY[50], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), ARRAY[50], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), ARRAY[50], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), ARRAY[50], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p0', ARRAY[19], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p0'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p20', ARRAY[20], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p20'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p40', ARRAY[11], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p40'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p0', ARRAY[19], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p0'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p20', ARRAY[20], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p20'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p40', ARRAY[11], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p40'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||'_p0', ARRAY[19], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||'_p0'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||'_p20', ARRAY[20], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||'_p20'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||'_p40', ARRAY[11], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||'_p40'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||'_p0', ARRAY[19], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||'_p0'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||'_p20', ARRAY[20], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||'_p20'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||'_p40', ARRAY[11], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||'_p40'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||'_p0', ARRAY[19], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||'_p0'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||'_p20', ARRAY[20], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||'_p20'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||'_p40', ARRAY[11], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||'_p40'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||'_p0', ARRAY[19], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||'_p0'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||'_p20', ARRAY[20], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||'_p20'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||'_p40', ARRAY[11], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||'_p40'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||'_p0', ARRAY[19], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||'_p0'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||'_p20', ARRAY[20], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||'_p20'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||'_p40', ARRAY[11], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||'_p40'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||'_p0', ARRAY[19], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||'_p0'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||'_p20', ARRAY[20], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||'_p20'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||'_p40', ARRAY[11], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||'_p40'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p0', ARRAY[19], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p0'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p20', ARRAY[20], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p20'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p40', ARRAY[11], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p40'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p0', ARRAY[19], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p0'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p20', ARRAY[20], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p20'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p40', ARRAY[11], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p40'); -- Insert data outside of id subpartition scope, but still in top time partition scope INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(200,210), CURRENT_TIMESTAMP); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(200,210), CURRENT_TIMESTAMP + '1 day'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(200,210), CURRENT_TIMESTAMP + '2 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(200,210), CURRENT_TIMESTAMP + '3 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(200,210), CURRENT_TIMESTAMP + '4 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(200,210), CURRENT_TIMESTAMP + '5 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(200,210), CURRENT_TIMESTAMP - '1 day'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(200,210), CURRENT_TIMESTAMP - '2 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(200,210), CURRENT_TIMESTAMP - '3 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(200,210), CURRENT_TIMESTAMP - '4 days'::interval); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table', 'Check that top parent table has no data even tho sub-parents should now have some.'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), ARRAY[11], 'Check count from ONLY time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), ARRAY[11], 'Check count from ONLY time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||' (out of scope test)'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), ARRAY[11], 'Check count from ONLY time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||' (out of scope test)'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), ARRAY[11], 'Check count from ONLY time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||' (out of scope test)'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), ARRAY[11], 'Check count from ONLY time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||' (out of scope test)'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), ARRAY[11], 'Check count from ONLY time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' (out of scope test)'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD'), ARRAY[11], 'Check count from ONLY time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||' (out of scope test)'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), ARRAY[11], 'Check count from ONLY time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||' (out of scope test)'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), ARRAY[11], 'Check count from ONLY time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||' (out of scope test)'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), ARRAY[11], 'Check count from ONLY time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||' (out of scope test)'); -- Insert data outside top time partition scope INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(211,220), CURRENT_TIMESTAMP - '20 days'::interval); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table', ARRAY[10], 'Check count from ONLY time_taptest_table (out of scope test)'); -- Remove above data so that retention can be tested DELETE FROM partman_test.time_taptest_table WHERE col1 >= 200 and col1 <= 220; -- Test dropping without retention set SELECT drop_partition_id ('partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), '20', p_keep_table := false); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p0', 'Check that partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p0 was dropped with direct drop call'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p20', 'Check that partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p20 was not dropped with direct drop call'); UPDATE part_config SET retention = '2 days', retention_keep_table = false WHERE parent_table = 'partman_test.time_taptest_table'; UPDATE part_config SET retention = '25', retention_keep_table = false WHERE parent_table = 'partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'); UPDATE part_config SET retention = '25', retention_keep_table = false WHERE parent_table = 'partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'); UPDATE part_config SET retention = '25', retention_keep_table = false WHERE parent_table = 'partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'); UPDATE part_config SET retention = '25', retention_keep_table = false WHERE parent_table = 'partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'); UPDATE part_config SET retention = '25', retention_keep_table = false WHERE parent_table = 'partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'); UPDATE part_config SET retention = '25', retention_keep_table = false WHERE parent_table = 'partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 days'::interval, 'YYYY_MM_DD'); UPDATE part_config SET retention = '25', retention_keep_table = false WHERE parent_table = 'partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'); UPDATE part_config SET retention = '25', retention_keep_table = false WHERE parent_table = 'partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'); UPDATE part_config SET retention = '25', retention_keep_table = false WHERE parent_table = 'partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'); -- Test dropping with it set SELECT drop_partition_id ('partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), p_keep_table := false); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p0', 'Check that partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p0 was dropped with direct drop call'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p20', 'Check that partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p20 was not dropped with direct drop call'); -- Test retention with run_maintenance() SELECT run_maintenance(); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||' was dropped'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p0', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p0 was dropped'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p20', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p20 was dropped'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p40', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p40 was dropped'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p60', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p60 was dropped'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p80', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p80 was dropped'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p100', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p100 was dropped'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p120', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p120 was dropped'); SELECT is_empty('SELECT parent_table FROM part_config WHERE parent_table = ''time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'''', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||' was removed from part_config'); SELECT is_empty('SELECT parent_table FROM part_config WHERE parent_table = ''time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p0''', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p0 was removed from part_config'); SELECT is_empty('SELECT parent_table FROM part_config WHERE parent_table = ''time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p20''', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p20 was removed from part_config'); SELECT is_empty('SELECT parent_table FROM part_config WHERE parent_table = ''time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p40''', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p40 was removed from part_config'); SELECT is_empty('SELECT parent_table FROM part_config WHERE parent_table = ''time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p60''', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p60 was removed from part_config'); SELECT is_empty('SELECT parent_table FROM part_config WHERE parent_table = ''time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p80''', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p80 was removed from part_config'); SELECT is_empty('SELECT parent_table FROM part_config WHERE parent_table = ''time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p100''', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p100 was removed from part_config'); SELECT is_empty('SELECT parent_table FROM part_config WHERE parent_table = ''time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p120''', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||'_p120 was removed from part_config'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||' was dropped'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p0', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p0 was dropped'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p20', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p20 was dropped'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p40', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p40 was dropped'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p60', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p60 was dropped'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p80', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p80 was dropped'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p100', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p100 was dropped'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p120', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p120 was dropped'); SELECT is_empty('SELECT parent_table FROM part_config WHERE parent_table = ''time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'''', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||' was removed from part_config'); SELECT is_empty('SELECT parent_table FROM part_config WHERE parent_table = ''time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p0''', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p0 was removed from part_config'); SELECT is_empty('SELECT parent_table FROM part_config WHERE parent_table = ''time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p20''', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p20 was removed from part_config'); SELECT is_empty('SELECT parent_table FROM part_config WHERE parent_table = ''time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p40''', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p40 was removed from part_config'); SELECT is_empty('SELECT parent_table FROM part_config WHERE parent_table = ''time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p60''', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p60 was removed from part_config'); SELECT is_empty('SELECT parent_table FROM part_config WHERE parent_table = ''time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p80''', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p80 was removed from part_config'); SELECT is_empty('SELECT parent_table FROM part_config WHERE parent_table = ''time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p100''', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p100 was removed from part_config'); SELECT is_empty('SELECT parent_table FROM part_config WHERE parent_table = ''time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p120''', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||'_p120 was removed from part_config'); -- First undo all subpartitions SELECT undo_partition_id('partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 20, p_keep_table := false); SELECT undo_partition_id('partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 20, p_keep_table := false); SELECT undo_partition_id('partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 20, p_keep_table := false); SELECT undo_partition_id('partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 20, p_keep_table := false); SELECT undo_partition_id('partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 20, p_keep_table := false); SELECT undo_partition_id('partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 20, p_keep_table := false); SELECT undo_partition_id('partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), 20, p_keep_table := false); SELECT undo_partition_id('partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD'), 20, p_keep_table := false); SELECT undo_partition_id('partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'), 20, p_keep_table := false); SELECT undo_partition_id('partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD'), 20, p_keep_table := false); SELECT undo_partition_id('partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD'), 20, p_keep_table := false); SELECT undo_partition_id('partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), 20, p_keep_table := false); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p0', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p0 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p20', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p20 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p40', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p40 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p60', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p60 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p80', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p80 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p100', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p100 does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||'_p0', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||'_p0 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||'_p20', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||'_p20 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||'_p40', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||'_p40 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||'_p60', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||'_p60 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||'_p80', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||'_p80 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||'_p100', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||'_p100 does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||'_p0', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||'_p0 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||'_p20', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||'_p20 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||'_p40', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||'_p40 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||'_p60', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||'_p60 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||'_p80', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||'_p80 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p0', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p0 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p20', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p20 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p40', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p40 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p60', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p60 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p80', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p80 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p100', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p100 does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||'_p0', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||'_p0 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||'_p20', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||'_p20 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||'_p40', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||'_p40 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||'_p60', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||'_p60 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||'_p80', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||'_p80 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||'_p0', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||'_p0 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||'_p20', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||'_p20 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||'_p40', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||'_p40 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||'_p60', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||'_p60 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||'_p80', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||'_p80 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||'_p0', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||'_p0 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||'_p20', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||'_p20 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||'_p40', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||'_p40 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||'_p60', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||'_p60 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||'_p80', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||'_p80 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||'_p0', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||'_p0 does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||'_p0', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||'_p0 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||'_p20', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||'_p20 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||'_p40', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||'_p40 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||'_p60', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||'_p60 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||'_p80', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||'_p80 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||'_p100', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||'_p100 does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p100', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'_p100 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p100', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'_p100 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||'_p100', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||'_p100 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||'_p100', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||'_p100 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||'_p100', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||'_p100 exists'); SELECT is_empty('SELECT parent_table from part_config where parent_table = ''partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||'''', 'Check that partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||' was removed from part_config'); SELECT is_empty('SELECT parent_table from part_config where parent_table = ''partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||'''', 'Check that partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||' was removed from part_config'); SELECT is_empty('SELECT parent_table from part_config where parent_table = ''partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||'''', 'Check that partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||' was removed from part_config'); SELECT is_empty('SELECT parent_table from part_config where parent_table = ''partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||'''', 'Check that partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||' was removed from part_config'); SELECT is_empty('SELECT parent_table from part_config where parent_table = ''partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||'''', 'Check that partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||' was removed from part_config'); SELECT is_empty('SELECT parent_table from part_config where parent_table = ''partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||'''', 'Check that partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' was removed from part_config'); SELECT is_empty('SELECT parent_table from part_config where parent_table = ''partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')||'''', 'Check that partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')||' was removed from part_config'); SELECT is_empty('SELECT parent_table from part_config where parent_table = ''partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')||'''', 'Check that partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')||' was removed from part_config'); SELECT is_empty('SELECT parent_table from part_config where parent_table = ''partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')||'''', 'Check that partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')||' was removed from part_config'); SELECT is_empty('SELECT parent_table from part_config where parent_table = ''partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD')||'''', 'Check that partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD')||' was removed from part_config'); SELECT is_empty('SELECT parent_table from part_config where parent_table = ''partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||'''', 'Check that partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||' was removed from part_config'); SELECT is_empty('SELECT parent_table from part_config where parent_table = ''partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||'''', 'Check that partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||' was removed from part_config'); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table', ARRAY[0], 'Check that top parent is still empty'); -- Rows are missing data for col1 values 1-19 due to retention drop above SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), ARRAY[31], 'Check data got move to partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD') ); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), ARRAY[31], 'Check data got move to partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD') ); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), ARRAY[31], 'Check data got move to partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD') ); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), ARRAY[31], 'Check data got move to partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD') ); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), ARRAY[31], 'Check data got move to partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD') ); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD'), ARRAY[31], 'Check data got move to partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD') ); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), ARRAY[31], 'Check data got move to partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD') ); --Unable to undo_partition any further because col1 is duplicated in child tables, but parent key has it as a primary key. So done testing! :D SELECT * FROM finish(); ROLLBACK; pg_partman-2.2.2/test/test-time-monthly-trunc.sql000066400000000000000000001342131262146621700221140ustar00rootroot00000000000000-- ########## TIME MONTHLY TESTS ########## -- Other tests: Long name truncation \set ON_ERROR_ROLLBACK 1 \set ON_ERROR_STOP true BEGIN; SELECT set_config('search_path','partman, public',false); SELECT plan(133); CREATE SCHEMA partman_test; CREATE ROLE partman_basic; CREATE ROLE partman_revoke; CREATE ROLE partman_owner; CREATE TABLE partman_test.time_taptest_table_123456789012345678901234567890123457890 (col1 int primary key, col2 text, col3 timestamptz NOT NULL DEFAULT now()); INSERT INTO partman_test.time_taptest_table_123456789012345678901234567890123457890 (col1, col3) VALUES (generate_series(1,10), CURRENT_TIMESTAMP); GRANT SELECT,INSERT,UPDATE ON partman_test.time_taptest_table_123456789012345678901234567890123457890 TO partman_basic; GRANT ALL ON partman_test.time_taptest_table_123456789012345678901234567890123457890 TO partman_revoke; SELECT create_parent('partman_test.time_taptest_table_123456789012345678901234567890123457890', 'col3', 'time', 'monthly'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM'), 'Check time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'1 month'::interval, 'YYYY_MM'), 'Check time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'1 month'::interval, 'YYYY_MM')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY_MM')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'4 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'4 months'::interval, 'YYYY_MM')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'5 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'5 months'::interval, 'YYYY_MM')||' does not exist'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'1 month'::interval, 'YYYY_MM'), 'Check time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'1 month'::interval, 'YYYY_MM')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'2 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'2 months'::interval, 'YYYY_MM')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY_MM')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'4 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'4 months'::interval, 'YYYY_MM')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'5 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'5 months'::interval, 'YYYY_MM')||' does not exist'); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM')); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'1 month'::interval, 'YYYY_MM'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'1 month'::interval, 'YYYY_MM')); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY_MM'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY_MM')); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM')); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'4 months'::interval, 'YYYY_MM'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'4 months'::interval, 'YYYY_MM')); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM')); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'1 month'::interval, 'YYYY_MM'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'1 month'::interval, 'YYYY_MM')); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'2 months'::interval, 'YYYY_MM'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'2 months'::interval, 'YYYY_MM')); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY_MM'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY_MM')); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'4 months'::interval, 'YYYY_MM'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'4 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'1 month'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'1 month'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'4 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'4 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'1 month'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'1 month'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'2 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'2 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'4 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'4 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'1 month'::interval, 'YYYY_MM'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'1 month'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY_MM'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'4 months'::interval, 'YYYY_MM'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'4 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'1 month'::interval, 'YYYY_MM'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'1 month'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'2 months'::interval, 'YYYY_MM'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'2 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY_MM'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'4 months'::interval, 'YYYY_MM'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'4 months'::interval, 'YYYY_MM')); SELECT results_eq('SELECT partition_data_time(''partman_test.time_taptest_table_123456789012345678901234567890123457890'')::int', ARRAY[10], 'Check that partitioning function returns correct count of rows moved'); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table_123456789012345678901234567890123457890', 'Check that parent table has had data moved to partition'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_123456789012345678901234567890123457890', ARRAY[10], 'Check count from parent table'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM'), ARRAY[10], 'Check count from time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM')); REVOKE INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER ON partman_test.time_taptest_table_123456789012345678901234567890123457890 FROM partman_revoke; INSERT INTO partman_test.time_taptest_table_123456789012345678901234567890123457890 (col1, col3) VALUES (generate_series(11,20), CURRENT_TIMESTAMP + '1 month'::interval); INSERT INTO partman_test.time_taptest_table_123456789012345678901234567890123457890 (col1, col3) VALUES (generate_series(21,25), CURRENT_TIMESTAMP + '2 months'::interval); INSERT INTO partman_test.time_taptest_table_123456789012345678901234567890123457890 (col1, col3) VALUES (generate_series(26,30), CURRENT_TIMESTAMP + '3 months'::interval); INSERT INTO partman_test.time_taptest_table_123456789012345678901234567890123457890 (col1, col3) VALUES (generate_series(31,37), CURRENT_TIMESTAMP + '4 months'::interval); INSERT INTO partman_test.time_taptest_table_123456789012345678901234567890123457890 (col1, col3) VALUES (generate_series(40,49), CURRENT_TIMESTAMP - '1 month'::interval); INSERT INTO partman_test.time_taptest_table_123456789012345678901234567890123457890 (col1, col3) VALUES (generate_series(50,70), CURRENT_TIMESTAMP - '2 months'::interval); INSERT INTO partman_test.time_taptest_table_123456789012345678901234567890123457890 (col1, col3) VALUES (generate_series(71,85), CURRENT_TIMESTAMP - '3 months'::interval); INSERT INTO partman_test.time_taptest_table_123456789012345678901234567890123457890 (col1, col3) VALUES (generate_series(86,100), CURRENT_TIMESTAMP - '4 months'::interval); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table_123456789012345678901234567890123457890', 'Check that parent table has had no data inserted to it'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'1 month'::interval, 'YYYY_MM'), ARRAY[10], 'Check count from time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'1 month'::interval, 'YYYY_MM')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY_MM'), ARRAY[5], 'Check count from time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY_MM')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM'), ARRAY[5], 'Check count from time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'4 months'::interval, 'YYYY_MM'), ARRAY[7], 'Check count from time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'4 months'::interval, 'YYYY_MM')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'1 month'::interval, 'YYYY_MM'), ARRAY[10], 'Check count from time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'1 month'::interval, 'YYYY_MM')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'2 months'::interval, 'YYYY_MM'), ARRAY[21], 'Check count from time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'2 months'::interval, 'YYYY_MM')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY_MM'), ARRAY[15], 'Check count from time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY_MM')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'4 months'::interval, 'YYYY_MM'), ARRAY[15], 'Check count from time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'4 months'::interval, 'YYYY_MM')); UPDATE part_config SET premake = 5, optimize_trigger = 5 WHERE parent_table = 'partman_test.time_taptest_table_123456789012345678901234567890123457890'; SELECT run_maintenance(); INSERT INTO partman_test.time_taptest_table_123456789012345678901234567890123457890 (col1, col3) VALUES (generate_series(101,122), CURRENT_TIMESTAMP + '5 months'::interval); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'5 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'5 months'::interval, 'YYYY_MM')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY_MM')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'7 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'7 months'::interval, 'YYYY_MM')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'8 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'8 months'::interval, 'YYYY_MM')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY_MM')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'10 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'10 months'::interval, 'YYYY_MM')||' exists'); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'5 months'::interval, 'YYYY_MM'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'5 months'::interval, 'YYYY_MM')); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY_MM'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY_MM')); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'7 months'::interval, 'YYYY_MM'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'7 months'::interval, 'YYYY_MM')); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'8 months'::interval, 'YYYY_MM'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'8 months'::interval, 'YYYY_MM')); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY_MM'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY_MM')); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table_123456789012345678901234567890123457890', 'Check that parent table has had no data inserted to it'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'5 months'::interval, 'YYYY_MM'), ARRAY[22], 'Check count from time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'5 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'5 month'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'5 month'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'6 month'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'6 month'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'7 month'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'7 month'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'8 month'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'8 month'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'9 month'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'9 month'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'5 months'::interval, 'YYYY_MM'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'5 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY_MM'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'7 months'::interval, 'YYYY_MM'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'7 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'8 months'::interval, 'YYYY_MM'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'8 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY_MM'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY_MM')); GRANT DELETE ON partman_test.time_taptest_table_123456789012345678901234567890123457890 TO partman_basic; REVOKE ALL ON partman_test.time_taptest_table_123456789012345678901234567890123457890 FROM partman_revoke; ALTER TABLE partman_test.time_taptest_table_123456789012345678901234567890123457890 OWNER TO partman_owner; UPDATE part_config SET premake = 6, optimize_trigger = 6 WHERE parent_table = 'partman_test.time_taptest_table_123456789012345678901234567890123457890'; SELECT run_maintenance(); INSERT INTO partman_test.time_taptest_table_123456789012345678901234567890123457890 (col1, col3) VALUES (generate_series(123,150), CURRENT_TIMESTAMP + '6 months'::interval); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table_123456789012345678901234567890123457890', 'Check that parent table has had no data inserted to it'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_123456789012345678901234567890123457890', ARRAY[148], 'Check count from parent table'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY_MM'), ARRAY[28], 'Check count from time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY_MM')); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'10 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'10 months'::interval, 'YYYY_MM')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'11 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'11 months'::interval, 'YYYY_MM')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'12 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'12 months'::interval, 'YYYY_MM')||' exists'); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'10 months'::interval, 'YYYY_MM'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'10 months'::interval, 'YYYY_MM')); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'11 months'::interval, 'YYYY_MM'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'11 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'10 months'::interval, 'YYYY_MM'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'10 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'10 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'10 months'::interval, 'YYYY_MM')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'10 months'::interval, 'YYYY_MM'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'10 months'::interval, 'YYYY_MM')); INSERT INTO partman_test.time_taptest_table_123456789012345678901234567890123457890 (col1, col3) VALUES (generate_series(200,210), CURRENT_TIMESTAMP + '20 months'::interval); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table_123456789012345678901234567890123457890', ARRAY[11], 'Check that data outside trigger scope goes to parent'); SELECT reapply_privileges('partman_test.time_taptest_table_123456789012345678901234567890123457890'); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'1 month'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'1 month'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'2 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'2 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'4 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'4 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'1 month'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'1 month'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'4 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'4 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'5 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'5 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'7 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'7 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'8 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'8 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'10 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'10 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'11 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'11 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'1 month'::interval, 'YYYY_MM'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'1 month'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY_MM'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'4 months'::interval, 'YYYY_MM'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'4 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'5 months'::interval, 'YYYY_MM'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'5 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY_MM'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY_MM')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'1 month'::interval, 'YYYY_MM'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'1 month'::interval, 'YYYY_MM')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY_MM'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY_MM')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'4 months'::interval, 'YYYY_MM'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'4 months'::interval, 'YYYY_MM')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'5 months'::interval, 'YYYY_MM'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'5 months'::interval, 'YYYY_MM')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY_MM'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY_MM')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'1 month'::interval, 'YYYY_MM'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'1 month'::interval, 'YYYY_MM')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'2 months'::interval, 'YYYY_MM'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'2 months'::interval, 'YYYY_MM')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY_MM'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY_MM')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'4 months'::interval, 'YYYY_MM'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'4 months'::interval, 'YYYY_MM')); -- Currently unable to do drop_partition test reliably for monthly due to differing month lengths (sometimes drops 2 partitions instead of 1) SELECT undo_partition_time('partman_test.time_taptest_table_123456789012345678901234567890123457890', 20, p_keep_table := false); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table_123456789012345678901234567890123457890', ARRAY[159], 'Check count from parent table after undo'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'1 month'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'1 month'::interval, 'YYYY_MM')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY_MM')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'4 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'4 months'::interval, 'YYYY_MM')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'5 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'5 months'::interval, 'YYYY_MM')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY_MM')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'1 month'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'1 month'::interval, 'YYYY_MM')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'2 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'2 months'::interval, 'YYYY_MM')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'4 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'4 months'::interval, 'YYYY_MM')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY_MM')||' does not exist'); SELECT * FROM finish(); ROLLBACK; pg_partman-2.2.2/test/test-time-monthly.sql000066400000000000000000001336661262146621700207760ustar00rootroot00000000000000-- ########## TIME MONTHLY TESTS ########## -- Other tests: Make sure option to not inherit foreign keys works \set ON_ERROR_ROLLBACK 1 \set ON_ERROR_STOP true BEGIN; SELECT set_config('search_path','partman, public',false); SELECT plan(166); CREATE SCHEMA partman_test; CREATE ROLE partman_basic; CREATE ROLE partman_revoke; CREATE ROLE partman_owner; CREATE TABLE partman_test.fk_test_reference (col2 text not null); CREATE UNIQUE INDEX ON partman_test.fk_test_reference(col2); INSERT INTO partman_test.fk_test_reference VALUES ('stuff'); CREATE TABLE partman_test.time_taptest_table ( col1 int primary key , col2 text not null default 'stuff' , col3 timestamptz NOT NULL DEFAULT now() , FOREIGN KEY (col2) REFERENCES partman_test.fk_test_reference(col2) MATCH SIMPLE NOT DEFERRABLE); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(1,10), CURRENT_TIMESTAMP); 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', 'time', 'monthly', p_inherit_fk := false); --SELECT create_parent('partman_test.time_taptest_table', 'col3', 'time', 'monthly'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 month'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'1 month'::interval, 'YYYY_MM')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY_MM')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'4 months'::interval, 'YYYY_MM')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'5 months'::interval, 'YYYY_MM')||' does not exist'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 month'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'1 month'::interval, 'YYYY_MM')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'2 months'::interval, 'YYYY_MM')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY_MM')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'4 months'::interval, 'YYYY_MM')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'5 months'::interval, 'YYYY_MM')||' does not exist'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 month'::interval, 'YYYY_MM'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 month'::interval, 'YYYY_MM')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY_MM'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY_MM')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 months'::interval, 'YYYY_MM'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 months'::interval, 'YYYY_MM')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 month'::interval, 'YYYY_MM'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 month'::interval, 'YYYY_MM')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 months'::interval, 'YYYY_MM'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 months'::interval, 'YYYY_MM')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY_MM'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY_MM')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 months'::interval, 'YYYY_MM'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 months'::interval, 'YYYY_MM')); SELECT col_isnt_fk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM'), 'col2', 'Check that foreign key was NOT inherited for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM')); SELECT col_isnt_fk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 month'::interval, 'YYYY_MM'), 'col2', 'Check that foreign key was NOT inherited for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 month'::interval, 'YYYY_MM')); SELECT col_isnt_fk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY_MM'), 'col2', 'Check that foreign key was NOT inherited for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY_MM')); SELECT col_isnt_fk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM'), 'col2', 'Check that foreign key was NOT inherited for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM')); SELECT col_isnt_fk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 months'::interval, 'YYYY_MM'), 'col2', 'Check that foreign key was NOT inherited for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 months'::interval, 'YYYY_MM')); SELECT col_isnt_fk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 month'::interval, 'YYYY_MM'), 'col2', 'Check that foreign key was NOT inherited for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 month'::interval, 'YYYY_MM')); SELECT col_isnt_fk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 months'::interval, 'YYYY_MM'), 'col2', 'Check that foreign key was NOT inherited for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 months'::interval, 'YYYY_MM')); SELECT col_isnt_fk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY_MM'), 'col2', 'Check that foreign key was NOT inherited for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY_MM')); SELECT col_isnt_fk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 months'::interval, 'YYYY_MM'), 'col2', 'Check that foreign key was NOT inherited for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 month'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 month'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 month'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 month'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 month'::interval, 'YYYY_MM'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 month'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY_MM'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 months'::interval, 'YYYY_MM'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 month'::interval, 'YYYY_MM'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 month'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 months'::interval, 'YYYY_MM'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY_MM'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 months'::interval, 'YYYY_MM'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 months'::interval, 'YYYY_MM')); SELECT results_eq('SELECT partition_data_time(''partman_test.time_taptest_table'')::int', ARRAY[10], 'Check that partitioning function returns correct count of rows moved'); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table', 'Check that parent table has had data moved to partition'); 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, 'YYYY_MM'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM')); REVOKE INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER ON partman_test.time_taptest_table FROM partman_revoke; INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(11,20), CURRENT_TIMESTAMP + '1 month'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(21,25), CURRENT_TIMESTAMP + '2 months'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(26,30), CURRENT_TIMESTAMP + '3 months'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(31,37), CURRENT_TIMESTAMP + '4 months'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(40,49), CURRENT_TIMESTAMP - '1 month'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(50,70), CURRENT_TIMESTAMP - '2 months'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(71,85), CURRENT_TIMESTAMP - '3 months'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(86,100), CURRENT_TIMESTAMP - '4 months'::interval); 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+'1 month'::interval, 'YYYY_MM'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 month'::interval, 'YYYY_MM')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY_MM'), ARRAY[5], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY_MM')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM'), ARRAY[5], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 months'::interval, 'YYYY_MM'), ARRAY[7], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 months'::interval, 'YYYY_MM')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 month'::interval, 'YYYY_MM'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 month'::interval, 'YYYY_MM')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 months'::interval, 'YYYY_MM'), ARRAY[21], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 months'::interval, 'YYYY_MM')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY_MM'), ARRAY[15], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY_MM')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 months'::interval, 'YYYY_MM'), ARRAY[15], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 months'::interval, 'YYYY_MM')); UPDATE part_config SET premake = 5, optimize_trigger = 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 + '5 months'::interval); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'5 months'::interval, 'YYYY_MM')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY_MM')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'7 months'::interval, 'YYYY_MM')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'8 months'::interval, 'YYYY_MM')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY_MM')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'10 months'::interval, 'YYYY_MM')||' does not exists'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 months'::interval, 'YYYY_MM'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 months'::interval, 'YYYY_MM')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY_MM'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY_MM')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 months'::interval, 'YYYY_MM'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 months'::interval, 'YYYY_MM')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 months'::interval, 'YYYY_MM'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 months'::interval, 'YYYY_MM')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY_MM'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY_MM')); SELECT col_isnt_fk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 months'::interval, 'YYYY_MM'), 'col2', 'Check that foreign key was NOT inherited for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 months'::interval, 'YYYY_MM')); SELECT col_isnt_fk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 months'::interval, 'YYYY_MM'), 'col2', 'Check that foreign key was NOT inherited for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 months'::interval, 'YYYY_MM')); SELECT col_isnt_fk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'7 months'::interval, 'YYYY_MM'), 'col2', 'Check that foreign key was NOT inherited for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'7 months'::interval, 'YYYY_MM')); SELECT col_isnt_fk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'8 months'::interval, 'YYYY_MM'), 'col2', 'Check that foreign key was NOT inherited for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'8 months'::interval, 'YYYY_MM')); SELECT col_isnt_fk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'9 months'::interval, 'YYYY_MM'), 'col2', 'Check that foreign key was NOT inherited for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'9 months'::interval, 'YYYY_MM')); 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+'5 months'::interval, 'YYYY_MM'), ARRAY[22], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 months'::interval, 'YYYY_MM'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY_MM'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 months'::interval, 'YYYY_MM'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 months'::interval, 'YYYY_MM'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY_MM'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY_MM')); GRANT DELETE ON partman_test.time_taptest_table TO partman_basic; REVOKE ALL ON partman_test.time_taptest_table FROM partman_revoke; ALTER TABLE partman_test.time_taptest_table OWNER TO partman_owner; UPDATE part_config SET premake = 6, optimize_trigger = 6 WHERE parent_table = 'partman_test.time_taptest_table'; SELECT run_maintenance(); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(123,150), CURRENT_TIMESTAMP + '6 months'::interval); 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', ARRAY[148], 'Check count from parent table'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY_MM'), ARRAY[28], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY_MM')); -- +5 month data was inserted above so 2 children get made SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'10 months'::interval, 'YYYY_MM')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'11 months'::interval, 'YYYY_MM')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'12 months'::interval, 'YYYY_MM')||' does not exists'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 months'::interval, 'YYYY_MM'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 months'::interval, 'YYYY_MM')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 months'::interval, 'YYYY_MM'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 months'::interval, 'YYYY_MM')); SELECT col_isnt_fk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 months'::interval, 'YYYY_MM'), 'col2', 'Check that foreign key was NOT inherited for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 months'::interval, 'YYYY_MM')); SELECT col_isnt_fk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 months'::interval, 'YYYY_MM'), 'col2', 'Check that foreign key was NOT inherited for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 months'::interval, 'YYYY_MM'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 months'::interval, 'YYYY_MM'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 months'::interval, 'YYYY_MM')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 months'::interval, 'YYYY_MM'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 months'::interval, 'YYYY_MM')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 months'::interval, 'YYYY_MM'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 months'::interval, 'YYYY_MM')); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(200,210), CURRENT_TIMESTAMP + '20 months'::interval); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table', ARRAY[11], 'Check that data outside trigger scope goes to parent'); SELECT reapply_privileges('partman_test.time_taptest_table'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 month'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 month'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 month'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 month'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 months'::interval, 'YYYY_MM'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 month'::interval, 'YYYY_MM'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 month'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY_MM'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 months'::interval, 'YYYY_MM'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 months'::interval, 'YYYY_MM'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY_MM'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 months'::interval, 'YYYY_MM'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 months'::interval, 'YYYY_MM'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY_MM'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 months'::interval, 'YYYY_MM'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 months'::interval, 'YYYY_MM')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 months'::interval, 'YYYY_MM'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 months'::interval, 'YYYY_MM')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 month'::interval, 'YYYY_MM'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 month'::interval, 'YYYY_MM')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 months'::interval, 'YYYY_MM'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 months'::interval, 'YYYY_MM')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY_MM'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY_MM')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 months'::interval, 'YYYY_MM'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 months'::interval, 'YYYY_MM')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 month'::interval, 'YYYY_MM'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 month'::interval, 'YYYY_MM')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY_MM'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY_MM')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 months'::interval, 'YYYY_MM'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 months'::interval, 'YYYY_MM')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 months'::interval, 'YYYY_MM'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 months'::interval, 'YYYY_MM')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY_MM'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY_MM')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 months'::interval, 'YYYY_MM'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 months'::interval, 'YYYY_MM')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 months'::interval, 'YYYY_MM'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 months'::interval, 'YYYY_MM')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY_MM'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY_MM')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 months'::interval, 'YYYY_MM'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 months'::interval, 'YYYY_MM')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 months'::interval, 'YYYY_MM'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 months'::interval, 'YYYY_MM')); -- Currently unable to do drop_partition test reliably for monthly due to differing month lengths (sometimes drops 2 partitions instead of 1) SELECT undo_partition_time('partman_test.time_taptest_table', 20, p_keep_table := false); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table', ARRAY[159], 'Check count from parent table after undo'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 month'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'1 month'::interval, 'YYYY_MM')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'2 months'::interval, 'YYYY_MM')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY_MM')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'4 months'::interval, 'YYYY_MM')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 month'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'1 month'::interval, 'YYYY_MM')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY_MM')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'4 months'::interval, 'YYYY_MM')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'5 months'::interval, 'YYYY_MM')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY_MM')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'7 months'::interval, 'YYYY_MM')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'8 months'::interval, 'YYYY_MM')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY_MM')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'10 months'::interval, 'YYYY_MM')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'11 months'::interval, 'YYYY_MM')||' does not exist'); SELECT * FROM finish(); ROLLBACK; pg_partman-2.2.2/test/test-time-quarter-hour.sql000066400000000000000000002302101262146621700217210ustar00rootroot00000000000000-- ########## TIME QUARTER-HOUR TESTS ########## \set ON_ERROR_ROLLBACK 1 \set ON_ERROR_STOP true BEGIN; SELECT set_config('search_path','partman, public',false); SELECT plan(151); CREATE SCHEMA partman_test; CREATE SCHEMA partman_retention_test; CREATE ROLE partman_basic; CREATE ROLE partman_revoke; CREATE ROLE partman_owner; CREATE TABLE partman_test.time_taptest_table (col1 int primary key, col2 text, col3 timestamptz NOT NULL DEFAULT now()); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(1,10), CURRENT_TIMESTAMP); 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', 'time', 'quarter-hour'); -- Must run_maintenance because when interval time is between 1 hour and 1 minute, the first partition name done by above is always the nearest hour rounded down SELECT run_maintenance(); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0), 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0), 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'15 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'15 mins'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'45 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'45 mins'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'75 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'75 mins'::interval, 'YYYY_MM_DD_HH24MI')||' does not exist'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'15 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'15 mins'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'30 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'30 mins'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'45 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'45 mins'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'60 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'60 mins'::interval, 'YYYY_MM_DD_HH24MI')||' 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('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'75 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'75 mins'::interval, 'YYYY_MM_DD_HH24MI')||' does not exist'); */ SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0), 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0), 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'15 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'15 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'45 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'45 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0), 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0), 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'15 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'15 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'30 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'30 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'45 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'45 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'60 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'60 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0), 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0), 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'15 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'15 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'45 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'45 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'15 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'15 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'30 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'30 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'45 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'45 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'60 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'60 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0), 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0), 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'15 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'15 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'45 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'45 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'15 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'15 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'30 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'30 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'45 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'45 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'60 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'60 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT results_eq('SELECT partition_data_time(''partman_test.time_taptest_table'')::int', ARRAY[10], 'Check that partitioning function returns correct count of rows moved'); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table', 'Check that parent table has had data moved to partition'); 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('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0), 'YYYY_MM_DD_HH24MI'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0), 'YYYY_MM_DD_HH24MI')); REVOKE INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER ON partman_test.time_taptest_table FROM partman_revoke; INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(11,20), CURRENT_TIMESTAMP + '15 mins'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(21,25), CURRENT_TIMESTAMP + '30 mins'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(26,30), CURRENT_TIMESTAMP + '45 mins'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(31,37), CURRENT_TIMESTAMP + '60 mins'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(40,49), CURRENT_TIMESTAMP - '15 mins'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(50,70), CURRENT_TIMESTAMP - '30 mins'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(71,85), CURRENT_TIMESTAMP - '45 mins'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(86,100), CURRENT_TIMESTAMP - '60 mins'::interval); 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(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'15 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'15 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY[5], 'Check count from time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'45 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY[5], 'Check count from time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'45 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY[7], 'Check count from time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'15 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'15 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'30 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY[21], 'Check count from time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'30 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'45 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY[15], 'Check count from time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'45 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'60 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY[15], 'Check count from time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'60 mins'::interval, 'YYYY_MM_DD_HH24MI')); UPDATE part_config SET premake = 5, optimize_trigger = 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 + '75 mins'::interval); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'75 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'75 mins'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'105 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'105 mins'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'135 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'135 mins'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'75 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'75 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'105 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'105 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'135 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'135 mins'::interval, 'YYYY_MM_DD_HH24MI')); 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(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'75 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY[22], 'Check count from time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'75 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'75 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'75 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'105 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'105 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'135 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'135 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'75 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'75 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'105 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'105 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'135 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'135 mins'::interval, 'YYYY_MM_DD_HH24MI')); GRANT DELETE ON partman_test.time_taptest_table TO partman_basic; REVOKE ALL ON partman_test.time_taptest_table FROM partman_revoke; ALTER TABLE partman_test.time_taptest_table OWNER TO partman_owner; UPDATE part_config SET premake = 6, optimize_trigger = 6 WHERE parent_table = 'partman_test.time_taptest_table'; SELECT run_maintenance(); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(123,150), CURRENT_TIMESTAMP + '90 mins'::interval); 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', ARRAY[148], 'Check count from parent table'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY[28], 'Check count from time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'165 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'165 mins'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'180 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'180 mins'::interval, 'YYYY_MM_DD_HH24MI')||' exists'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'165 mins'::interval, 'YYYY_MM_DD_HH24MI'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'165 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'165 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'165 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'165 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'165 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'165 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'165 mins'::interval, 'YYYY_MM_DD_HH24MI')); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(200,210), CURRENT_TIMESTAMP + '300 mins'::interval); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table', ARRAY[11], 'Check that data outside trigger scope goes to parent'); SELECT reapply_privileges('partman_test.time_taptest_table'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0), 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0), 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'15 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'15 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'30 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'30 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'45 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'45 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'60 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'60 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'15 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'15 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'45 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'45 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'75 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'75 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'105 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'105 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'135 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'135 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'165 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'165 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0), 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0), 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'15 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'15 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'45 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'45 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'75 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'75 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'105 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'105 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'135 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'135 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'165 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'165 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0), 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0), 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'15 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'15 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'30 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'30 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'45 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'45 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'60 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'60 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'15 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'15 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'45 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'45 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'75 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'75 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'105 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'105 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'135 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'135 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'165 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'165 mins'::interval, 'YYYY_MM_DD_HH24MI')); SELECT drop_partition_time('partman_test.time_taptest_table', '45 mins', p_keep_table := false); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'60 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'60 mins'::interval, 'YYYY_MM_DD_HH24MI')||' does not exist'); UPDATE part_config SET retention = '30 mins'::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('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'45 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'45 mins'::interval, 'YYYY_MM_DD_HH24MI')||' does not exist'); SELECT has_table('partman_retention_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'45 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'45 mins'::interval, 'YYYY_MM_DD_HH24MI')||' got moved to new schema'); SELECT undo_partition_time('partman_test.time_taptest_table', 20, p_keep_table := false); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table', ARRAY[129], 'Check count from parent table after undo'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0), 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0), 'YYYY_MM_DD_HH24MI')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'15 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'15 mins'::interval, 'YYYY_MM_DD_HH24MI')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'30 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)-'30 mins'::interval, 'YYYY_MM_DD_HH24MI')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'15 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'15 mins'::interval, 'YYYY_MM_DD_HH24MI')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'30 mins'::interval, 'YYYY_MM_DD_HH24MI')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'45 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'45 mins'::interval, 'YYYY_MM_DD_HH24MI')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'60 mins'::interval, 'YYYY_MM_DD_HH24MI')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'75 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'75 mins'::interval, 'YYYY_MM_DD_HH24MI')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'90 mins'::interval, 'YYYY_MM_DD_HH24MI')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'105 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'105 mins'::interval, 'YYYY_MM_DD_HH24MI')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'120 mins'::interval, 'YYYY_MM_DD_HH24MI')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'135 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'135 mins'::interval, 'YYYY_MM_DD_HH24MI')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'150 mins'::interval, 'YYYY_MM_DD_HH24MI')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'165 mins'::interval, 'YYYY_MM_DD_HH24MI'), 'Check time_taptest_table_'||to_char(date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0)+'165 mins'::interval, 'YYYY_MM_DD_HH24MI')||' does not exist'); SELECT * FROM finish(); ROLLBACK; pg_partman-2.2.2/test/test-time-quarterly-trunc.sql000066400000000000000000001261071262146621700224550ustar00rootroot00000000000000-- ########## TIME QUARTERLY TESTS ########## -- Other tests: Long name truncation, undo partitioning but keep tables \set ON_ERROR_ROLLBACK 1 \set ON_ERROR_STOP true BEGIN; SELECT set_config('search_path','partman, public',false); SELECT plan(124); CREATE SCHEMA partman_test; CREATE ROLE partman_basic; CREATE ROLE partman_revoke; CREATE ROLE partman_owner; CREATE TABLE partman_test.time_taptest_table_1234567890123456789012345678901234567890 (col1 int primary key, col2 text, col3 timestamptz NOT NULL DEFAULT now()); INSERT INTO partman_test.time_taptest_table_1234567890123456789012345678901234567890 (col1, col3) VALUES (generate_series(1,10), CURRENT_TIMESTAMP); GRANT SELECT,INSERT,UPDATE ON partman_test.time_taptest_table_1234567890123456789012345678901234567890 TO partman_basic; GRANT ALL ON partman_test.time_taptest_table_1234567890123456789012345678901234567890 TO partman_revoke; SELECT create_parent('partman_test.time_taptest_table_1234567890123456789012345678901234567890', 'col3', 'time', 'quarterly'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP, 'YYYY"q"Q'), 'Check time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP, 'YYYY"q"Q')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY"q"Q')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY"q"Q')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY"q"Q')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'12 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'12 months'::interval, 'YYYY"q"Q')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'15 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'15 months'::interval, 'YYYY"q"Q')||' does not exists'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY"q"Q')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'6 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'6 months'::interval, 'YYYY"q"Q')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'9 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'9 months'::interval, 'YYYY"q"Q')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'12 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'12 months'::interval, 'YYYY"q"Q')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'15 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'15 months'::interval, 'YYYY"q"Q')||' does not exists'); SELECT col_is_pk('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP, 'YYYY"q"Q'), ARRAY['col1'], 'Check for primary key in time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP, 'YYYY"q"Q')); SELECT col_is_pk('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY"q"Q'), ARRAY['col1'], 'Check for primary key in time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY"q"Q')); SELECT col_is_pk('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY"q"Q'), ARRAY['col1'], 'Check for primary key in time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY"q"Q')); SELECT col_is_pk('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY"q"Q'), ARRAY['col1'], 'Check for primary key in time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY"q"Q')); SELECT col_is_pk('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'12 months'::interval, 'YYYY"q"Q'), ARRAY['col1'], 'Check for primary key in time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'12 months'::interval, 'YYYY"q"Q')); SELECT col_is_pk('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY"q"Q'), ARRAY['col1'], 'Check for primary key in time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY"q"Q')); SELECT col_is_pk('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'6 months'::interval, 'YYYY"q"Q'), ARRAY['col1'], 'Check for primary key in time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'6 months'::interval, 'YYYY"q"Q')); SELECT col_is_pk('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'9 months'::interval, 'YYYY"q"Q'), ARRAY['col1'], 'Check for primary key in time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'9 months'::interval, 'YYYY"q"Q')); SELECT col_is_pk('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'12 months'::interval, 'YYYY"q"Q'), ARRAY['col1'], 'Check for primary key in time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'12 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'12 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'12 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'6 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'6 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'9 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'9 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'12 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'12 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP, 'YYYY"q"Q'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY"q"Q'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY"q"Q'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY"q"Q'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'12 months'::interval, 'YYYY"q"Q'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'12 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY"q"Q'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'6 months'::interval, 'YYYY"q"Q'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'6 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'9 months'::interval, 'YYYY"q"Q'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'9 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'12 months'::interval, 'YYYY"q"Q'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'12 months'::interval, 'YYYY"q"Q')); SELECT results_eq('SELECT partition_data_time(''partman_test.time_taptest_table_1234567890123456789012345678901234567890'')::int', ARRAY[10], 'Check that partitioning function returns correct count of rows moved'); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table_1234567890123456789012345678901234567890', 'Check that parent table has had data moved to partition'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_1234567890123456789012345678901234567890', ARRAY[10], 'Check count from parent table'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP, 'YYYY"q"Q'), ARRAY[10], 'Check count from time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP, 'YYYY"q"Q')); REVOKE INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER ON partman_test.time_taptest_table_1234567890123456789012345678901234567890 FROM partman_revoke; INSERT INTO partman_test.time_taptest_table_1234567890123456789012345678901234567890 (col1, col3) VALUES (generate_series(11,20), CURRENT_TIMESTAMP + '3 months'::interval); INSERT INTO partman_test.time_taptest_table_1234567890123456789012345678901234567890 (col1, col3) VALUES (generate_series(21,25), CURRENT_TIMESTAMP + '6 months'::interval); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table_1234567890123456789012345678901234567890', 'Check that parent table has had no data inserted to it'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_1234567890123456789012345678901234567890', ARRAY[25], 'Check count from time_taptest_table'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY"q"Q'), ARRAY[10], 'Check count from time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY"q"Q')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY"q"Q'), ARRAY[5], 'Check count from time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY"q"Q')); UPDATE part_config SET premake = 5, optimize_trigger = 5 WHERE parent_table = 'partman_test.time_taptest_table_1234567890123456789012345678901234567890'; SELECT run_maintenance(); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'15 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'15 months'::interval, 'YYYY"q"Q')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'18 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'18 months'::interval, 'YYYY"q"Q')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'21 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'21 months'::interval, 'YYYY"q"Q')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'24 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'24 months'::interval, 'YYYY"q"Q')||' does not exists'); SELECT col_is_pk('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'15 months'::interval, 'YYYY"q"Q'), ARRAY['col1'], 'Check for primary key in time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'15 months'::interval, 'YYYY"q"Q')); SELECT col_is_pk('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'18 months'::interval, 'YYYY"q"Q'), ARRAY['col1'], 'Check for primary key in time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'18 months'::interval, 'YYYY"q"Q')); SELECT col_is_pk('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'21 months'::interval, 'YYYY"q"Q'), ARRAY['col1'], 'Check for primary key in time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'21 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'15 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'15 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'18 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'18 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'21 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'21 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'15 months'::interval, 'YYYY"q"Q'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'15 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'18 months'::interval, 'YYYY"q"Q'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'18 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'21 months'::interval, 'YYYY"q"Q'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'21 months'::interval, 'YYYY"q"Q')); GRANT DELETE ON partman_test.time_taptest_table_1234567890123456789012345678901234567890 TO partman_basic; REVOKE ALL ON partman_test.time_taptest_table_1234567890123456789012345678901234567890 FROM partman_revoke; ALTER TABLE partman_test.time_taptest_table_1234567890123456789012345678901234567890 OWNER TO partman_owner; UPDATE part_config SET premake = 6, optimize_trigger = 6 WHERE parent_table = 'partman_test.time_taptest_table_1234567890123456789012345678901234567890'; SELECT run_maintenance(); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'24 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'24 months'::interval, 'YYYY"q"Q')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'27 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'27 months'::interval, 'YYYY"q"Q')||' does not exists'); SELECT col_is_pk('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'24 months'::interval, 'YYYY"q"Q'), ARRAY['col1'], 'Check for primary key in time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'24 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'24 months'::interval, 'YYYY"q"Q'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'24 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'24 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'24 months'::interval, 'YYYY"q"Q')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'24 months'::interval, 'YYYY"q"Q'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'24 months'::interval, 'YYYY"q"Q')); INSERT INTO partman_test.time_taptest_table_1234567890123456789012345678901234567890 (col1, col3) VALUES (generate_series(200,210), CURRENT_TIMESTAMP + '60 months'::interval); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table_1234567890123456789012345678901234567890', ARRAY[11], 'Check that data outside trigger scope goes to parent'); SELECT reapply_privileges('partman_test.time_taptest_table_1234567890123456789012345678901234567890'); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'12 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'12 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'18 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'18 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'21 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'21 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'24 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'24 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP, 'YYYY"q"Q'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY"q"Q'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'6 months'::interval, 'YYYY"q"Q'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'6 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'9 months'::interval, 'YYYY"q"Q'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'9 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'12 months'::interval, 'YYYY"q"Q'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'12 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY"q"Q'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY"q"Q'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY"q"Q'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'12 months'::interval, 'YYYY"q"Q'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'12 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'15 months'::interval, 'YYYY"q"Q'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'15 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'18 months'::interval, 'YYYY"q"Q'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'18 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'21 months'::interval, 'YYYY"q"Q'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'21 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'24 months'::interval, 'YYYY"q"Q'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'24 months'::interval, 'YYYY"q"Q')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP, 'YYYY"q"Q'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP, 'YYYY"q"Q')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY"q"Q'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY"q"Q')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'6 months'::interval, 'YYYY"q"Q'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'6 months'::interval, 'YYYY"q"Q')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'9 months'::interval, 'YYYY"q"Q'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'9 months'::interval, 'YYYY"q"Q')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'12 months'::interval, 'YYYY"q"Q'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'12 months'::interval, 'YYYY"q"Q')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY"q"Q'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY"q"Q')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY"q"Q'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY"q"Q')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY"q"Q'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY"q"Q')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'12 months'::interval, 'YYYY"q"Q'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'12 months'::interval, 'YYYY"q"Q')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'15 months'::interval, 'YYYY"q"Q'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'15 months'::interval, 'YYYY"q"Q')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'18 months'::interval, 'YYYY"q"Q'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'18 months'::interval, 'YYYY"q"Q')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'21 months'::interval, 'YYYY"q"Q'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'21 months'::interval, 'YYYY"q"Q')); SELECT table_owner_is ('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'24 months'::interval, 'YYYY"q"Q'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'24 months'::interval, 'YYYY"q"Q')); SELECT undo_partition_time('partman_test.time_taptest_table_1234567890123456789012345678901234567890', 20); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY"q"Q')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY"q"Q')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'6 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'6 months'::interval, 'YYYY"q"Q')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'6 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'6 months'::interval, 'YYYY"q"Q')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'9 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'9 months'::interval, 'YYYY"q"Q')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'9 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'9 months'::interval, 'YYYY"q"Q')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'12 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'12 months'::interval, 'YYYY"q"Q')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'12 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP-'12 months'::interval, 'YYYY"q"Q')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY"q"Q')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY"q"Q')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY"q"Q')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY"q"Q')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY"q"Q')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY"q"Q')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'12 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'12 months'::interval, 'YYYY"q"Q')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'12 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'12 months'::interval, 'YYYY"q"Q')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'15 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'15 months'::interval, 'YYYY"q"Q')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'15 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'15 months'::interval, 'YYYY"q"Q')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'18 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'18 months'::interval, 'YYYY"q"Q')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'18 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'18 months'::interval, 'YYYY"q"Q')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'21 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'21 months'::interval, 'YYYY"q"Q')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'21 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'21 months'::interval, 'YYYY"q"Q')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'24 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'24 months'::interval, 'YYYY"q"Q')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'24 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_123456789012345678901234567890123456_p'||to_char(CURRENT_TIMESTAMP+'24 months'::interval, 'YYYY"q"Q')||' is empty'); SELECT * FROM finish(); ROLLBACK; pg_partman-2.2.2/test/test-time-quarterly.sql000066400000000000000000001163511262146621700213240ustar00rootroot00000000000000-- ########## TIME QUARTERLY TESTS ########## \set ON_ERROR_ROLLBACK 1 \set ON_ERROR_STOP true BEGIN; SELECT set_config('search_path','partman, public',false); SELECT plan(141); CREATE SCHEMA partman_test; CREATE ROLE partman_basic; CREATE ROLE partman_revoke; CREATE ROLE partman_owner; CREATE TABLE partman_test.time_taptest_table (col1 int primary key, col2 text, col3 timestamptz NOT NULL DEFAULT now()); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(1,10), CURRENT_TIMESTAMP); 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', 'time', 'quarterly'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY"q"Q'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP, 'YYYY"q"Q')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY"q"Q')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY"q"Q')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY"q"Q')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'12 months'::interval, 'YYYY"q"Q')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'15 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'15 months'::interval, 'YYYY"q"Q')||' does not exist'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY"q"Q')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'6 months'::interval, 'YYYY"q"Q')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'9 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'9 months'::interval, 'YYYY"q"Q')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'12 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'12 months'::interval, 'YYYY"q"Q')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'15 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'15 months'::interval, 'YYYY"q"Q')||' does not exist'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY"q"Q'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY"q"Q')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY"q"Q'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY"q"Q')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY"q"Q'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY"q"Q')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY"q"Q'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY"q"Q')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 months'::interval, 'YYYY"q"Q'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 months'::interval, 'YYYY"q"Q')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY"q"Q'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY"q"Q')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY"q"Q'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY"q"Q')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 months'::interval, 'YYYY"q"Q'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 months'::interval, 'YYYY"q"Q')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'9 months'::interval, 'YYYY"q"Q'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'9 months'::interval, 'YYYY"q"Q')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'12 months'::interval, 'YYYY"q"Q'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'12 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'9 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'9 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'12 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'12 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY"q"Q'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_'||to_char(CURRENT_TIMESTAMP, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY"q"Q'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY"q"Q'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY"q"Q'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 months'::interval, 'YYYY"q"Q'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY"q"Q'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 months'::interval, 'YYYY"q"Q'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'9 months'::interval, 'YYYY"q"Q'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'9 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'12 months'::interval, 'YYYY"q"Q'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'12 months'::interval, 'YYYY"q"Q')); SELECT results_eq('SELECT partition_data_time(''partman_test.time_taptest_table'')::int', ARRAY[10], 'Check that partitioning function returns correct count of rows moved'); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table', 'Check that parent table has had data moved to partition'); 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, 'YYYY"q"Q'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY"q"Q')); REVOKE INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER ON partman_test.time_taptest_table FROM partman_revoke; INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(11,20), CURRENT_TIMESTAMP + '3 months'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(21,25), CURRENT_TIMESTAMP + '6 months'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(26,30), CURRENT_TIMESTAMP + '9 months'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(31,37), CURRENT_TIMESTAMP + '12 months'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(40,49), CURRENT_TIMESTAMP - '3 months'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(50,70), CURRENT_TIMESTAMP - '6 months'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(71,85), CURRENT_TIMESTAMP - '9 months'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(86,100), CURRENT_TIMESTAMP - '12 months'::interval); 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 months'::interval, 'YYYY"q"Q'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY"q"Q')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY"q"Q'), ARRAY[5], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY"q"Q')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY"q"Q'), ARRAY[5], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY"q"Q')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 months'::interval, 'YYYY"q"Q'), ARRAY[7], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 months'::interval, 'YYYY"q"Q')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY"q"Q'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY"q"Q')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 months'::interval, 'YYYY"q"Q'), ARRAY[21], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 months'::interval, 'YYYY"q"Q')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'9 months'::interval, 'YYYY"q"Q'), ARRAY[15], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'9 months'::interval, 'YYYY"q"Q')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'12 months'::interval, 'YYYY"q"Q'), ARRAY[15], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'12 months'::interval, 'YYYY"q"Q')); UPDATE part_config SET premake = 5, optimize_trigger = 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 + '15 months'::interval); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'15 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'15 months'::interval, 'YYYY"q"Q')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'18 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'18 months'::interval, 'YYYY"q"Q')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'21 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'21 months'::interval, 'YYYY"q"Q')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'24 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'24 months'::interval, 'YYYY"q"Q')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'27 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'27 months'::interval, 'YYYY"q"Q')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'30 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'30 months'::interval, 'YYYY"q"Q')||' does not exists'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'15 months'::interval, 'YYYY"q"Q'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'15 months'::interval, 'YYYY"q"Q')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'18 months'::interval, 'YYYY"q"Q'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'18 months'::interval, 'YYYY"q"Q')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'21 months'::interval, 'YYYY"q"Q'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'21 months'::interval, 'YYYY"q"Q')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'24 months'::interval, 'YYYY"q"Q'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'24 months'::interval, 'YYYY"q"Q')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'27 months'::interval, 'YYYY"q"Q'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'27 months'::interval, 'YYYY"q"Q')); 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+'15 months'::interval, 'YYYY"q"Q'), ARRAY[22], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'15 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'15 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'15 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'18 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'18 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'21 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'21 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'24 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'24 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'27 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'27 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'15 months'::interval, 'YYYY"q"Q'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'15 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'18 months'::interval, 'YYYY"q"Q'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'18 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'21 months'::interval, 'YYYY"q"Q'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'21 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'24 months'::interval, 'YYYY"q"Q'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'24 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'27 months'::interval, 'YYYY"q"Q'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'27 months'::interval, 'YYYY"q"Q')); GRANT DELETE ON partman_test.time_taptest_table TO partman_basic; REVOKE ALL ON partman_test.time_taptest_table FROM partman_revoke; ALTER TABLE partman_test.time_taptest_table OWNER TO partman_owner; UPDATE part_config SET premake = 6, optimize_trigger = 6 WHERE parent_table = 'partman_test.time_taptest_table'; SELECT run_maintenance(); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(123,150), CURRENT_TIMESTAMP + '18 months'::interval); 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', ARRAY[148], 'Check count from parent table'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'18 months'::interval, 'YYYY"q"Q'), ARRAY[28], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'18 months'::interval, 'YYYY"q"Q')); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'30 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'30 months'::interval, 'YYYY"q"Q')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'33 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'33 months'::interval, 'YYYY"q"Q')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'36 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'36 months'::interval, 'YYYY"q"Q')||' does not exists'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'30 months'::interval, 'YYYY"q"Q'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'30 months'::interval, 'YYYY"q"Q')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'33 months'::interval, 'YYYY"q"Q'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'33 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'30 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'30 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'33 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'33 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'30 months'::interval, 'YYYY"q"Q'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'30 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'33 months'::interval, 'YYYY"q"Q'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'33 months'::interval, 'YYYY"q"Q')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'30 months'::interval, 'YYYY"q"Q'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'30 months'::interval, 'YYYY"q"Q')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'33 months'::interval, 'YYYY"q"Q'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'33 months'::interval, 'YYYY"q"Q')); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(200,210), CURRENT_TIMESTAMP + '60 months'::interval); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table', ARRAY[11], 'Check that data outside trigger scope goes to parent'); SELECT reapply_privileges('partman_test.time_taptest_table'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'9 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'9 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'12 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'12 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'15 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'15 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'18 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'18 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'21 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'21 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'24 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'24 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'27 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'27 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'30 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'30 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'33 months'::interval, 'YYYY"q"Q'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'33 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY"q"Q'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY"q"Q'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY"q"Q'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY"q"Q'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 months'::interval, 'YYYY"q"Q'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'15 months'::interval, 'YYYY"q"Q'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'15 months'::interval, 'YYYY"q"Q')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'18 months'::interval, 'YYYY"q"Q'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'18 months'::interval, 'YYYY"q"Q')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY"q"Q'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY"q"Q')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY"q"Q'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY"q"Q')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY"q"Q'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY"q"Q')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY"q"Q'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY"q"Q')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 months'::interval, 'YYYY"q"Q'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 months'::interval, 'YYYY"q"Q')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'15 months'::interval, 'YYYY"q"Q'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'15 months'::interval, 'YYYY"q"Q')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'18 months'::interval, 'YYYY"q"Q'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'18 months'::interval, 'YYYY"q"Q')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY"q"Q'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY"q"Q')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 months'::interval, 'YYYY"q"Q'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 months'::interval, 'YYYY"q"Q')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'9 months'::interval, 'YYYY"q"Q'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'9 months'::interval, 'YYYY"q"Q')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'12 months'::interval, 'YYYY"q"Q'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'12 months'::interval, 'YYYY"q"Q')); -- Currently unable to do drop_partition test reliably for quarterly due to differing month lengths (sometimes drops 2 partitions instead of 1) SELECT undo_partition_time('partman_test.time_taptest_table', 20, p_keep_table := false); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table', ARRAY[159], 'Check count from parent table after undo'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY"q"Q'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP, 'YYYY"q"Q')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY"q"Q')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'6 months'::interval, 'YYYY"q"Q')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'9 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'9 months'::interval, 'YYYY"q"Q')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'12 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'12 months'::interval, 'YYYY"q"Q')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY"q"Q')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'6 months'::interval, 'YYYY"q"Q')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'9 months'::interval, 'YYYY"q"Q')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'12 months'::interval, 'YYYY"q"Q')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'15 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'15 months'::interval, 'YYYY"q"Q')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'18 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'18 months'::interval, 'YYYY"q"Q')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'21 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'21 months'::interval, 'YYYY"q"Q')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'24 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'24 months'::interval, 'YYYY"q"Q')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'27 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'27 months'::interval, 'YYYY"q"Q')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'30 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'30 months'::interval, 'YYYY"q"Q')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'33 months'::interval, 'YYYY"q"Q'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'33 months'::interval, 'YYYY"q"Q')||' does not exist'); SELECT * FROM finish(); ROLLBACK; pg_partman-2.2.2/test/test-time-start-partition.sql000066400000000000000000000604761262146621700224460ustar00rootroot00000000000000-- ########## TIME STATIC TESTS ########## -- Test p_start_partition parameter \set ON_ERROR_ROLLBACK 1 \set ON_ERROR_STOP true BEGIN; SELECT set_config('search_path','partman, public',false); SELECT plan(92); CREATE SCHEMA partman_test; CREATE SCHEMA partman_retention_test; CREATE TABLE partman_test.time_taptest_table (col1 int primary key, col2 text, col3 timestamptz NOT NULL DEFAULT now()); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(1,10), CURRENT_TIMESTAMP); SELECT create_parent('partman_test.time_taptest_table', 'col3', 'time', 'daily', p_start_partition := (CURRENT_TIMESTAMP - '10 days'::interval)::text); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 days'::interval, 'YYYY_MM_DD')||' does exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 days'::interval, 'YYYY_MM_DD')||' does exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'7 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'7 days'::interval, 'YYYY_MM_DD')||' does exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'8 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'8 days'::interval, 'YYYY_MM_DD')||' does exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'9 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'9 days'::interval, 'YYYY_MM_DD')||' does exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'10 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'10 days'::interval, 'YYYY_MM_DD')||' does exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'11 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'11 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'7 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'7 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'8 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'8 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'9 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'9 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'10 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'10 days'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT partition_data_time(''partman_test.time_taptest_table'')::int', ARRAY[10], 'Check that partitioning function returns correct count of rows moved'); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table', 'Check that parent table has had data moved to partition'); 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, 'YYYY_MM_DD'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(11,20), CURRENT_TIMESTAMP + '1 day'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(21,25), CURRENT_TIMESTAMP + '2 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(26,30), CURRENT_TIMESTAMP + '3 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(31,37), CURRENT_TIMESTAMP + '4 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(40,49), CURRENT_TIMESTAMP - '1 day'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(50,70), CURRENT_TIMESTAMP - '2 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(71,85), CURRENT_TIMESTAMP - '3 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(86,100), CURRENT_TIMESTAMP - '4 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(101,110), CURRENT_TIMESTAMP - '5 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(111,120), CURRENT_TIMESTAMP - '6 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(121,130), CURRENT_TIMESTAMP - '7 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(131,140), CURRENT_TIMESTAMP - '8 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(141,150), CURRENT_TIMESTAMP - '9 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(151,160), CURRENT_TIMESTAMP - '10 days'::interval); SELECT partition_data_time('partman_test.time_taptest_table', p_batch_count := 20); 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+'1 day'::interval, 'YYYY_MM_DD'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), ARRAY[5], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), ARRAY[5], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), ARRAY[7], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), ARRAY[21], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), ARRAY[15], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), ARRAY[15], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 days'::interval, 'YYYY_MM_DD'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 days'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 days'::interval, 'YYYY_MM_DD'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 days'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'7 days'::interval, 'YYYY_MM_DD'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'7 days'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'8 days'::interval, 'YYYY_MM_DD'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'8 days'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'9 days'::interval, 'YYYY_MM_DD'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'9 days'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'10 days'::interval, 'YYYY_MM_DD'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'10 days'::interval, 'YYYY_MM_DD')); UPDATE part_config SET premake = 5, optimize_trigger = 5 WHERE parent_table = 'partman_test.time_taptest_table'; SELECT run_maintenance(); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(161,170), CURRENT_TIMESTAMP + '5 days'::interval); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD')); 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+'5 days'::interval, 'YYYY_MM_DD'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')); UPDATE part_config SET premake = 6, optimize_trigger = 6 WHERE parent_table = 'partman_test.time_taptest_table'; SELECT run_maintenance(); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(171,180), CURRENT_TIMESTAMP + '6 days'::interval); 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', ARRAY[178], 'Check count from parent table'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD')||' does not exists'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD')); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(200,210), CURRENT_TIMESTAMP + '20 days'::interval); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table', ARRAY[11], 'Check that data outside trigger scope goes to parent'); SELECT drop_partition_time('partman_test.time_taptest_table', '3 days', p_keep_table := false); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||' does not exist'); UPDATE part_config SET retention = '2 days'::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 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT has_table('partman_retention_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||' got moved to new schema'); SELECT undo_partition_time('partman_test.time_taptest_table', 20, p_keep_table := false); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table', ARRAY[99], 'Check count from parent table after undo'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT * FROM finish(); ROLLBACK; pg_partman-2.2.2/test/test-time-time-time-subpart.sql000066400000000000000000000722401262146621700226420ustar00rootroot00000000000000-- ########## TIME PARENT / TIME SUBPARENT / TIME SUB-SUB-PARENT DYNAMIC ########## -- Currently tests 23, 39, 47 & 67 may fail around new years boundaries \set ON_ERROR_ROLLBACK 1 \set ON_ERROR_STOP true BEGIN; SELECT set_config('search_path','partman, public',false); SELECT plan(72); CREATE SCHEMA partman_test; CREATE TABLE partman_test.time_taptest_table (col1 int primary key, col2 text, col3 timestamptz NOT NULL DEFAULT now()); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(1,10), CURRENT_TIMESTAMP); -- yearly SELECT create_parent('partman_test.time_taptest_table', 'col3', 'time', 'yearly', p_premake := 2); -- Make sure optimize values can be different UPDATE part_config SET optimize_trigger = 5, optimize_constraint = 10 WHERE parent_table = 'partman_test.time_taptest_table'; SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')||' does not exist'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 years'::interval, 'YYYY'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 years'::interval, 'YYYY')||' does not exist'); -- Move data from parent SELECT results_eq('SELECT partition_data_time(''partman_test.time_taptest_table'')::int', ARRAY[10], 'Check that partitioning function returns correct count of rows moved'); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table', 'Check that yearly parent table has had data moved to partition'); 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, 'YYYY'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')); -- monthly SELECT create_sub_parent('partman_test.time_taptest_table', 'col3', 'time', 'monthly', p_premake := 2); -- Make sure optimize values can be different UPDATE part_config_sub SET sub_optimize_trigger = 5, sub_optimize_constraint = 10, sub_retention_keep_table = false WHERE sub_parent = 'partman_test.time_taptest_table'; SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 month'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'1 month'::interval, 'YYYY_MM'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 month'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'1 month'::interval, 'YYYY_MM')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'2 months'::interval, 'YYYY_MM')||' exists'); -- Next test starts failing in Oct since the next year's partition was made and must contain one child table (january) SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM')||' does not exist (this test may fail around year boundary. See comment in test code)'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 month'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'1 month'::interval, 'YYYY_MM'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 month'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'1 month'::interval, 'YYYY_MM')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 months'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'2 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 months'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'2 months'::interval, 'YYYY_MM')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'3 months'::interval, 'YYYY_MM')||' does not exist'); -- Check that previous and future years had the minimal partition made -- year +/- 1 tests may fail around year boundary. Tables may or may not exist depending on premake. That's fine. Should be ok for further in the future. SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY')||'_01', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY')||'_01 exists (this test may fail around year boundary. See comment in test code)'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY')||'_02', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY')||'_02 does not exists (this test may fail around year boundary. See comment in test code)'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||'_01', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||'_01 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||'_02', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||'_02 does not exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||'_01', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||'_01 exists (this test may fail around year boundary. See comment in test code)'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||'_02', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||'_02 exists (this test may fail around year boundary. See comment in test code)'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')||'_01', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'2 year'::interval, 'YYYY')||'_01 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')||'_02', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'2 year'::interval, 'YYYY')||'_02 exists'); SELECT results_eq('SELECT partition_data_time(''partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')||''')::int', ARRAY[10], 'Check that partitioning function returns correct count of rows moved (yearly subparent)'); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY'), 'Check data got moved out of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM')); -- Check subpart config SELECT results_eq('SELECT sub_parent FROM part_config_sub ORDER BY sub_parent', ARRAY['partman_test.time_taptest_table'], 'Check that part_config_sub has all tables configured as needed'); -- daily SELECT results_eq('SELECT create_sub_parent(''partman_test.time_taptest_table_p''||to_char(CURRENT_TIMESTAMP, ''YYYY''), ''col3'', ''time'', ''daily'', p_premake := 2)', ARRAY[true], 'Subpartitioning partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')||' should return true'); SELECT results_eq('SELECT create_sub_parent(''partman_test.time_taptest_table_p''||to_char(CURRENT_TIMESTAMP+''1 year''::interval, ''YYYY''), ''col3'', ''time'', ''daily'', p_premake := 2)', ARRAY[true], 'Subpartitioning partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY')||' should return true'); SELECT results_eq('SELECT create_sub_parent(''partman_test.time_taptest_table_p''||to_char(CURRENT_TIMESTAMP+''2 years''::interval, ''YYYY''), ''col3'', ''time'', ''daily'', p_premake := 2)', ARRAY[true], 'Subpartitioning partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||' should return true'); SELECT results_eq('SELECT create_sub_parent(''partman_test.time_taptest_table_p''||to_char(CURRENT_TIMESTAMP-''1 year''::interval, ''YYYY''), ''col3'', ''time'', ''daily'', p_premake := 2)', ARRAY[true], 'Subpartitioning partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||' should return true'); SELECT results_eq('SELECT create_sub_parent(''partman_test.time_taptest_table_p''||to_char(CURRENT_TIMESTAMP-''2 years''::interval, ''YYYY''), ''col3'', ''time'', ''daily'', p_premake := 2)', ARRAY[true], 'Subpartitioning partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')||' should return true'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM')||'_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM')||' exists')||'_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM')||'_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM')||'_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM')||'_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM')||'_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||' does not exists'); -- This test may fail around the end of the year or the end of some months since the minimal partition for the next year or next month was created. That's fine SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM')||'_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM')||'_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||' does not exist (this test may fail around year or month boundaries. See comment in test code)'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM')||'_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM')||'_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM')||'_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM')||'_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM')||'_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM')||'_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||' does not exist'); -- Check that previous and future years had the minimal partition made -- year +/- 1 tests may fail around year boundary. Tables may or may not exist depending on premake. That's fine. Should be ok for further in the future. SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'1 years'::interval, 'YYYY')||'_01_p'||to_char(CURRENT_TIMESTAMP+'1 years'::interval, 'YYYY')||'_01_01', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'1 years'::interval, 'YYYY')||'_01_p'||to_char(CURRENT_TIMESTAMP+'1 years'::interval, 'YYYY')||'_01_01 exists (this test may fail around year boundary. See comment in test code)'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'1 years'::interval, 'YYYY')||'_01_p'||to_char(CURRENT_TIMESTAMP+'1 years'::interval, 'YYYY')||'_01_02', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'1 years'::interval, 'YYYY')||'_01_p'||to_char(CURRENT_TIMESTAMP+'1 years'::interval, 'YYYY')||'_01_02 does not exists (this test may fail around year boundary. See comment in test code)'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||'_01_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||'_01_01', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||'_01_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||'_01_01 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||'_01_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||'_01_02', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||'_01_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||'_01_02 does not exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||'_01_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||'_01_01', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||'_01_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||'_01_01 exists (this test may fail around year boundary. See comment in test code)'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||'_01_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||'_01_02', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||'_01_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||'_01_02 exists (this test may fail around year boundary. See comment in test code)'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')||'_01_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')||'_01_01', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'2 year'::interval, 'YYYY')||'_01_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')||'_01_01 exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')||'_01_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')||'_01_02', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP-'2 year'::interval, 'YYYY')||'_01_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')||'_01_02 does not exists'); SELECT results_eq('SELECT partition_data_time(''partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM')||''')::int', ARRAY[10], 'Check that partitioning function returns correct count of rows moved (monthly subparent)'); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM'), 'Check data got moved out of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM')||'_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM')||'_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); -- Check subpart config SELECT results_eq('SELECT sub_parent FROM part_config_sub ORDER BY sub_parent', ARRAY['partman_test.time_taptest_table', 'partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY'), 'partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY'), 'partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY'), 'partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY'), 'partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')], 'Check that part_config_sub has all tables configured as needed'); INSERT INTO partman_test.time_taptest_table (col1, col2, col3) VALUES (generate_series(11,20), 'stuff', CURRENT_TIMESTAMP+'1 day'::interval); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table', 'Check new data did not go into parent time_taptest_table'); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day', 'YYYY'), 'Check new data did not go into subparent time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day', 'YYYY')); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day', 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'1 day', 'YYYY_MM'), 'Check new data did not go into subparent time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day', 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'1 day', 'YYYY_MM')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day', 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'1 day', 'YYYY_MM')||'_p'||to_char(CURRENT_TIMESTAMP+'1 day', 'YYYY_MM_DD'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day', 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'1 day', 'YYYY_MM')||'_p'||to_char(CURRENT_TIMESTAMP+'1 day', 'YYYY_MM_DD')); UPDATE part_config SET premake = 3 WHERE parent_table LIKE 'partman_test.time_taptest_table%' AND partition_type = 'time'; SELECT run_maintenance(); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'3 months'::interval, 'YYYY_MM')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM')||'_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM')||'_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||' exists'); -- Check that future year had the minimal partition made SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')||'_01', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')||'_01 exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')||'_01_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')||'_01_01', 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')||'_01_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')||'_01_01 exists'); -- Check subpart config SELECT results_eq('SELECT sub_parent FROM part_config_sub ORDER BY sub_parent', ARRAY['partman_test.time_taptest_table', 'partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY'), 'partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY'), 'partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY'), 'partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY'), 'partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY'), 'partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')], 'Check that part_config_sub has all tables configured as needed'); INSERT INTO partman_test.time_taptest_table (col1, col2, col3) VALUES (generate_series(21,30), 'stuff', CURRENT_TIMESTAMP+'3 years'::interval); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table', 'Check new data did not go into parent time_taptest_table'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years', 'YYYY'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years', 'YYYY')||'. Data should have gone here since monthly subpartition for it does not exist. This test may fail in January since that monthly partition should exist.'); -- Move data from yearly parent table and create appropriate monthly child for it SELECT results_eq('SELECT partition_data_time(''partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')||''')::int', ARRAY[10], 'Check that partitioning function returns correct count of rows moved from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')||'. This test may fail in January since that monthly partition should exist.'); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years', 'YYYY'), 'Check new data did not go into subparent time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years', 'YYYY')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years', 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'3 years', 'YYYY_MM'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years', 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'3 years', 'YYYY_MM')||'. Data should have gone here since daily subpartition for it does not exist.'); -- Move data from monthly parent table and create appropriate daily child for it SELECT results_eq('SELECT partition_data_time(''partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'3 years', 'YYYY_MM')||''')::int', ARRAY[10], 'Check that partitioning function returns correct count of rows moved from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'3 years', 'YYYY_MM')); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years', 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'3 years', 'YYYY_MM'), 'Check new data did not go into subparent time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years', 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'3 years', 'YYYY_MM')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years', 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'3 years', 'YYYY_MM')||'_p'||to_char(CURRENT_TIMESTAMP+'3 years', 'YYYY_MM_DD'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years', 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'3 years', 'YYYY_MM')||'_p'||to_char(CURRENT_TIMESTAMP+'3 years', 'YYYY_MM_DD')); /* -- Disabled test for now. New years makes testing undo functions hard. Would be calling undo on year+1_01 twice and second one would fail. SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table', ARRAY[30], 'Check count from top parent'); SELECT throws_ok('SELECT undo_partition_time(''partman_test.time_taptest_table_p''||to_char(CURRENT_TIMESTAMP+''3 years''::interval, ''YYYY''), 20, p_keep_table := false)', 'P0001', 'Child table for this parent has child table(s) itself (partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')||'_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')||'_01). Run undo partitioning on this table or remove inheritance first to ensure all data is properly moved to parent CONTEXT: SQL statement "SELECT undo_partition_time(''partman_test.time_taptest_table_p''||to_char(CURRENT_TIMESTAMP+''3 years''::interval, ''YYYY''), 20, p_keep_table := false)" PL/pgSQL function throws_ok(text,character,text,text) line 16 at EXECUTE statement DETAIL: HINT: ', 'Check that undoing partitions is prevented if subpartitions still exist'); */ SELECT * FROM finish(); ROLLBACK; pg_partman-2.2.2/test/test-time-weekly-daily-subpart.sql000066400000000000000000003255701262146621700233570ustar00rootroot00000000000000-- ########## TIME WEEKLY-DAILY SUBPARTITION TESTS ########## -- Other tests: Mixed case & special characters, extra constraints, hybrid trigger puts data outside premake into proper tables, dropping only indexes in retention, grants/revokes -- Also tests that maintenance catches up on all subpartition tables as long as data exists in them -- NOTE Some tests failing on mondays. Commented them out with notes below. Thanks Garfield. BEGIN; SELECT set_config('search_path','partman, public',false); SELECT plan(278); CREATE SCHEMA "Partman_test"; CREATE ROLE "partman-basic"; CREATE ROLE "Partman_Revoke"; CREATE ROLE partman_owner; CREATE TABLE "Partman_test"."Time-taptest-Table" ("COL1" int primary key, col2 text, "Col-3" timestamptz NOT NULL DEFAULT now()); -- Time data being inserted is truncated to the beginning of the week to avoid missing partitions INSERT INTO "Partman_test"."Time-taptest-Table" ("COL1", "Col-3") VALUES (generate_series(1,10), date_trunc('week', CURRENT_TIMESTAMP)); 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', 'Col-3', 'time', 'weekly', '{"COL1"}', p_premake := 4, p_start_partition := to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'YYYY-MM-DD HH24:MI:SS')); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW')||' exists (this week)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW'), 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW')||' exists (+1 weeks)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW'), 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')||' exists (+2 weeks)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW'), 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')||' exists (+3 weeks)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW'), 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')||' exists (+4 weeks)'); SELECT hasnt_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW'), 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW')||' does not exist (+5 weeks)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW'), 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW')||' exists (-1 weeks)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW'), 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')||' exists (-2 weeks)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW'), 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')||' exists (-3 weeks)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW'), 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')||' exists (-4 weeks)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW'), 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW')||' exists (-5 weeks)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW'), 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW')||' exists (-6 weeks)'); SELECT hasnt_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'IYYY"w"IW'), 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'IYYY"w"IW')||' does not exist (-7 weeks)'); SELECT col_is_pk('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW'), ARRAY['COL1'], 'Check for primary key in Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW')||' (this week)'); SELECT col_is_pk('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW'), ARRAY['COL1'], 'Check for primary key in Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW')||' (+1 week)'); SELECT col_is_pk('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW'), ARRAY['COL1'], 'Check for primary key in Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')||' (+2 week)'); SELECT col_is_pk('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW'), ARRAY['COL1'], 'Check for primary key in Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')||' (+3 week)'); SELECT col_is_pk('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW'), ARRAY['COL1'], 'Check for primary key in Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')||' (+4 week)'); SELECT col_is_pk('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW'), ARRAY['COL1'], 'Check for primary key in Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW')||' (-1 week)'); SELECT col_is_pk('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW'), ARRAY['COL1'], 'Check for primary key in Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')||' (-2 week)'); SELECT col_is_pk('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW'), ARRAY['COL1'], 'Check for primary key in Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')||' (-3 week)'); SELECT col_is_pk('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW'), ARRAY['COL1'], 'Check for primary key in Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')||' (-4 week)'); SELECT col_is_pk('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW'), ARRAY['COL1'], 'Check for primary key in Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW')||' (-5 week)'); SELECT col_is_pk('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW'), ARRAY['COL1'], 'Check for primary key in Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW')||' (-6 week)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW'), 'partman-basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman-basic privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW')||' (this week)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW'), 'partman-basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman-basic privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW')||' (+1 week)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW'), 'partman-basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman-basic privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')||' (+2 week)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW'), 'partman-basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman-basic privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')||' (+3 week)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW'), 'partman-basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman-basic privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')||' (+4 week)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW'), 'partman-basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman-basic privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW')||' (-1 week)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW'), 'partman-basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman-basic privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')||' (-2 week)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW'), 'partman-basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman-basic privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')||' (-3 week)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW'), 'partman-basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman-basic privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')||' (-4 week)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW'), 'partman-basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman-basic privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW')||' (-5 week)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW'), 'partman-basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman-basic privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW')||' (-6 week)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW'), 'Partman_Revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check Partman_Revoke privileges of Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW')||' (this week)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW'), 'Partman_Revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check Partman_Revoke privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW')||' (+1 week)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW'), 'Partman_Revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check Partman_Revoke privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')||' (+2 week)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW'), 'Partman_Revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check Partman_Revoke privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')||' (+3 week)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW'), 'Partman_Revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check Partman_Revoke privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')||' (+4 week)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW'), 'Partman_Revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check Partman_Revoke privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW')||' (-1 week)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW'), 'Partman_Revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check Partman_Revoke privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')||' (-2 week)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW'), 'Partman_Revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check Partman_Revoke privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')||' (-3 week)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW'), 'Partman_Revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check Partman_Revoke privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')||' (-4 week)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW'), 'Partman_Revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check Partman_Revoke privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW')||' (-5 week)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW'), 'Partman_Revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check Partman_Revoke privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW')||' (-6 week)'); SELECT results_eq('SELECT partition_data_time(''Partman_test.Time-taptest-Table'')::int', ARRAY[10], 'Check that partitioning function returns correct count of rows moved'); SELECT is_empty('SELECT * FROM ONLY "Partman_test"."Time-taptest-Table"', 'Check that parent table has had data moved to partition'); 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, 'IYYY"w"IW')||'"', ARRAY[10], 'Check count from Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW')||' (this week)'); REVOKE INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER ON "Partman_test"."Time-taptest-Table" FROM "Partman_Revoke"; INSERT INTO "Partman_test"."Time-taptest-Table" ("COL1", "Col-3") VALUES (generate_series(11,20), date_trunc('week', CURRENT_TIMESTAMP - '4 week'::interval)); INSERT INTO "Partman_test"."Time-taptest-Table" ("COL1", "Col-3") VALUES (generate_series(21,25), date_trunc('week', CURRENT_TIMESTAMP - '3 week'::interval)); INSERT INTO "Partman_test"."Time-taptest-Table" ("COL1", "Col-3") VALUES (generate_series(26,30), date_trunc('week', CURRENT_TIMESTAMP - '2 weeks'::interval)); INSERT INTO "Partman_test"."Time-taptest-Table" ("COL1", "Col-3") VALUES (generate_series(31,37), date_trunc('week', CURRENT_TIMESTAMP - '1 week'::interval)); INSERT INTO "Partman_test"."Time-taptest-Table" ("COL1", "Col-3") VALUES (generate_series(38,49), date_trunc('week', CURRENT_TIMESTAMP + '1 week'::interval)); INSERT INTO "Partman_test"."Time-taptest-Table" ("COL1", "Col-3") VALUES (generate_series(50,70), date_trunc('week', CURRENT_TIMESTAMP + '2 weeks'::interval)); INSERT INTO "Partman_test"."Time-taptest-Table" ("COL1", "Col-3") VALUES (generate_series(71,85), date_trunc('week', CURRENT_TIMESTAMP + '3 weeks'::interval)); INSERT INTO "Partman_test"."Time-taptest-Table" ("COL1", "Col-3") VALUES (generate_series(86,100), date_trunc('week', CURRENT_TIMESTAMP + '4 weeks'::interval)); -- Test hybrid trigger puts data in tables outside premake range INSERT INTO "Partman_test"."Time-taptest-Table" ("COL1", "Col-3") VALUES (generate_series(101,110), date_trunc('week', CURRENT_TIMESTAMP - '5 weeks'::interval)); INSERT INTO "Partman_test"."Time-taptest-Table" ("COL1", "Col-3") VALUES (generate_series(111,130), date_trunc('week', CURRENT_TIMESTAMP - '6 weeks'::interval)); -- Add data for checking that daily subpartition constraint management is working (8 days is before new premake value below). -- This isn't currently working, but leaving this here for future testing if I can get this working -- Also, when this test script is run on a monday, 8 days ago is 2 weeks ago, not 1 week ago, so it throws some of the rest of the tests off. -- INSERT INTO "Partman_test"."Time-taptest-Table" ("COL1", "Col-3") VALUES (generate_series(131,140), CURRENT_TIMESTAMP - '8 days'::interval); 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, 'IYYY"w"IW')||'"', ARRAY[10], 'Check count from Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW')||' (this week)'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW')||'"', ARRAY[7], 'Check count from Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW')||' (-1 week)'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW')||'"', ARRAY[20], 'Check count from Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')||' (-6 week)'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW')||'"', ARRAY[10], 'Check count from Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')||' (-5 week)'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')||'"', ARRAY[10], 'Check count from Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')||' (-4 week)'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')||'"', ARRAY[5], 'Check count from Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')||' (-3 week)'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')||'"', ARRAY[5], 'Check count from Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')||' (-2 week)'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW')||'"', ARRAY[12], 'Check count from Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW')||' (+1 week)'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'2 week'::interval, 'IYYY"w"IW')||'"', ARRAY[21], 'Check count from Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'2 week'::interval, 'IYYY"w"IW')||' (+2 week)'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'3 week'::interval, 'IYYY"w"IW')||'"', ARRAY[15], 'Check count from Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'3 week'::interval, 'IYYY"w"IW')||' (+3 week)'); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'4 week'::interval, 'IYYY"w"IW')||'"', ARRAY[15], 'Check count from Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'4 week'::interval, 'IYYY"w"IW')||' (+4 week)'); -- Ensure all the current child tables get the new privileges given above so they propagate properly when subpartitions are created SELECT reapply_privileges('Partman_test.Time-taptest-Table'); SELECT create_sub_parent('Partman_test.Time-taptest-Table', 'Col-3', 'time', 'daily', '{"COL1"}', p_premake := 4); -- Check daily partitions SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||' exists (current day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||' exists (+1 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||' exists (+2 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||' exists (+3 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||' exists (+4 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||' exists (-1 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||' exists (-2 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||' exists (-3 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||' exists (-4 day)'); SELECT col_is_pk('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), ARRAY['COL1'], 'Check for primary key in Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||' (current day)'); SELECT col_is_pk('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), ARRAY['COL1'], 'Check for primary key in Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||' (+1 day)'); SELECT col_is_pk('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'2 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'2 day'::interval, 'YYYY_MM_DD'), ARRAY['COL1'], 'Check for primary key in Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'2 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'2 day'::interval, 'YYYY_MM_DD')||' (+2 day)'); SELECT col_is_pk('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'3 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'3 day'::interval, 'YYYY_MM_DD'), ARRAY['COL1'], 'Check for primary key in Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'3 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'3 day'::interval, 'YYYY_MM_DD')||' (+3 day)'); SELECT col_is_pk('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'4 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'4 day'::interval, 'YYYY_MM_DD'), ARRAY['COL1'], 'Check for primary key in Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'4 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'4 day'::interval, 'YYYY_MM_DD')||' (+4 day)'); SELECT col_is_pk('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD'), ARRAY['COL1'], 'Check for primary key in Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||' (-1 day)'); SELECT col_is_pk('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'2 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP-'2 day'::interval, 'YYYY_MM_DD'), ARRAY['COL1'], 'Check for primary key in Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'2 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP-'2 day'::interval, 'YYYY_MM_DD')||' (-2 day)'); SELECT col_is_pk('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'3 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP-'3 day'::interval, 'YYYY_MM_DD'), ARRAY['COL1'], 'Check for primary key in Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'3 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP-'3 day'::interval, 'YYYY_MM_DD')||' (-3 day)'); SELECT col_is_pk('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'4 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP-'4 day'::interval, 'YYYY_MM_DD'), ARRAY['COL1'], 'Check for primary key in Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'4 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP-'4 day'::interval, 'YYYY_MM_DD')||' (-4 day)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD') , 'partman-basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman-basic privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||' (now)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD') , 'partman-basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman-basic privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||' (+1 day)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'2 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'2 day'::interval, 'YYYY_MM_DD') , 'partman-basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman-basic privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'2 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'2 day'::interval, 'YYYY_MM_DD')||' (+2 day)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'3 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'3 day'::interval, 'YYYY_MM_DD') , 'partman-basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman-basic privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'3 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'3 day'::interval, 'YYYY_MM_DD')||' (+3 day)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'4 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'4 day'::interval, 'YYYY_MM_DD') , 'partman-basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman-basic privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'4 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'4 day'::interval, 'YYYY_MM_DD')||' (+4 day)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD') , 'partman-basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman-basic privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||' (-1 day)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'2 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP-'2 day'::interval, 'YYYY_MM_DD') , 'partman-basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman-basic privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'2 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP-'2 day'::interval, 'YYYY_MM_DD')||' (-2 day)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'3 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP-'3 day'::interval, 'YYYY_MM_DD') , 'partman-basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman-basic privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'3 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP-'3 day'::interval, 'YYYY_MM_DD')||' (-3 day)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'4 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP-'4 day'::interval, 'YYYY_MM_DD') , 'partman-basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman-basic privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'4 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP-'4 day'::interval, 'YYYY_MM_DD')||' (-4 day)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD') , 'Partman_Revoke', ARRAY['SELECT'], 'Check Partman_Revoke privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||' (now)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD') , 'Partman_Revoke', ARRAY['SELECT'], 'Check Partman_Revoke privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||' (+1 day)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'2 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'2 day'::interval, 'YYYY_MM_DD') , 'Partman_Revoke', ARRAY['SELECT'], 'Check Partman_Revoke privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'2 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'2 day'::interval, 'YYYY_MM_DD')||' (+2 day)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'3 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'3 day'::interval, 'YYYY_MM_DD') , 'Partman_Revoke', ARRAY['SELECT'], 'Check Partman_Revoke privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'3 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'3 day'::interval, 'YYYY_MM_DD')||' (+3 day)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'4 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'4 day'::interval, 'YYYY_MM_DD') , 'Partman_Revoke', ARRAY['SELECT'], 'Check Partman_Revoke privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'4 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'4 day'::interval, 'YYYY_MM_DD')||' (+4 day)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD') , 'Partman_Revoke', ARRAY['SELECT'], 'Check Partman_Revoke privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||' (-1 day)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'2 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP-'2 day'::interval, 'YYYY_MM_DD') , 'Partman_Revoke', ARRAY['SELECT'], 'Check Partman_Revoke privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'2 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP-'2 day'::interval, 'YYYY_MM_DD')||' (-2 day)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'3 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP-'3 day'::interval, 'YYYY_MM_DD') , 'Partman_Revoke', ARRAY['SELECT'], 'Check Partman_Revoke privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'3 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP-'3 day'::interval, 'YYYY_MM_DD')||' (-3 day)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'4 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP-'4 day'::interval, 'YYYY_MM_DD') , 'Partman_Revoke', ARRAY['SELECT'], 'Check Partman_Revoke privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'4 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP-'4 day'::interval, 'YYYY_MM_DD')||' (-4 day)'); -- Check for future weekly partitions and their minimal daily. Not testing pk & privileges. If above is fine, these should be fine SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'1 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'1 weeks'::interval), 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'1 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'1 weeks'::interval), 'YYYY_MM_DD')||' exists (+1 weeks)'); -- Cannot reliably test for +1week+1day not existing since near the end of the week, the premake days go into the following week SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'2 weeks'::interval), 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'2 weeks'::interval), 'YYYY_MM_DD')||' exists (+2 weeks)'); SELECT hasnt_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'2 weeks'::interval)+'1 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'2 weeks'::interval)+'1 day'::interval, 'YYYY_MM_DD')||' does not exist (+2 weeks +1 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'3 weeks'::interval), 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'3 weeks'::interval), 'YYYY_MM_DD')||' exists (+3 weeks)'); SELECT hasnt_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'3 weeks'::interval)+'1 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'3 weeks'::interval)+'1 day'::interval, 'YYYY_MM_DD')||' does not exist (+3 weeks +1 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'4 weeks'::interval), 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'4 weeks'::interval), 'YYYY_MM_DD')||' exists (+4 weeks)'); SELECT hasnt_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'4 weeks'::interval)+'1 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'4 weeks'::interval)+'1 day'::interval, 'YYYY_MM_DD')||' does not exist (+8 weeks +1 day)'); SELECT results_eq('SELECT partition_data_time(''Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW')||''', 10)::int' , ARRAY[10] , 'Check that partitioning function returns correct count of rows moved for Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW')||' (now)'); SELECT results_eq('SELECT partition_data_time(''Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW')||''', 10)::int' , ARRAY[20], 'Check that partitioning function returns correct count of rows moved for Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW')||' (-6 weeks)'); SELECT results_eq('SELECT partition_data_time(''Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW')||''', 10)::int' , ARRAY[10], 'Check that partitioning function returns correct count of rows moved for Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW')||' (-5 weeks)'); SELECT results_eq('SELECT partition_data_time(''Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')||''', 10)::int' , ARRAY[10], 'Check that partitioning function returns correct count of rows moved for Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')||' (-4 weeks)'); SELECT results_eq('SELECT partition_data_time(''Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')||''', 10)::int' , ARRAY[5], 'Check that partitioning function returns correct count of rows moved for Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')||' (-3 weeks)'); SELECT results_eq('SELECT partition_data_time(''Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')||''', 10)::int' , ARRAY[5], 'Check that partitioning function returns correct count of rows moved for Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')||' (-2 weeks)'); SELECT results_eq('SELECT partition_data_time(''Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW')||''', 10)::int' , ARRAY[7], 'Check that partitioning function returns correct count of rows moved for Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW')||' (-1 weeks)'); SELECT results_eq('SELECT partition_data_time(''Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW')||''', 10)::int' , ARRAY[12], 'Check that partitioning function returns correct count of rows moved for Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW')||' (+1 weeks)'); SELECT results_eq('SELECT partition_data_time(''Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')||''', 10)::int' , ARRAY[21], 'Check that partitioning function returns correct count of rows moved for Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')||' (+2 weeks)'); SELECT results_eq('SELECT partition_data_time(''Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')||''', 10)::int' , ARRAY[15], 'Check that partitioning function returns correct count of rows moved for Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')||' (+3 weeks)'); SELECT results_eq('SELECT partition_data_time(''Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')||''', 10)::int' , ARRAY[15], 'Check that partitioning function returns correct count of rows moved for Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')||' (+4 weeks)'); -- This should cause all subpartition sets to catch up and premake 4 child tables SELECT run_maintenance(); -- Data at time of run_maintenance() call exists for +4 weeks, premake is 4, so should be partitions for 4 weeks after that (+8 weeks)) SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW'), 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW')||' exists (now)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'6 weeks'::interval, 'IYYY"w"IW'), 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'6 weeks'::interval, 'IYYY"w"IW')||' exists (+6 weeks)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'7 weeks'::interval, 'IYYY"w"IW'), 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'7 weeks'::interval, 'IYYY"w"IW')||' exists (+7 weeks)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'8 weeks'::interval, 'IYYY"w"IW'), 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'8 weeks'::interval, 'IYYY"w"IW')||' exists (+8 weeks)'); SELECT hasnt_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'9 weeks'::interval, 'IYYY"w"IW'), 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'9 weeks'::interval, 'IYYY"w"IW')||' does not exist (+9 weeks)'); SELECT col_is_pk('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW'), ARRAY['COL1'], 'Check for primary key in Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW')||' exists (+5 weeks)'); SELECT col_is_pk('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'6 weeks'::interval, 'IYYY"w"IW'), ARRAY['COL1'], 'Check for primary key in Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'6 weeks'::interval, 'IYYY"w"IW')||' exists (+6 weeks)'); SELECT col_is_pk('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'7 weeks'::interval, 'IYYY"w"IW'), ARRAY['COL1'], 'Check for primary key in Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'7 weeks'::interval, 'IYYY"w"IW')||' exists (+7 weeks)'); SELECT col_is_pk('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'8 weeks'::interval, 'IYYY"w"IW'), ARRAY['COL1'], 'Check for primary key in Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'8 weeks'::interval, 'IYYY"w"IW')||' exists (+8 weeks)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW'), 'partman-basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman-basic privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW')||' (+5 weeks)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'6 weeks'::interval, 'IYYY"w"IW'), 'partman-basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman-basic privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'6 weeks'::interval, 'IYYY"w"IW')||' (+6 weeks)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'7 weeks'::interval, 'IYYY"w"IW'), 'partman-basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman-basic privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'7 weeks'::interval, 'IYYY"w"IW')||' (+7 weeks)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'8 weeks'::interval, 'IYYY"w"IW'), 'partman-basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman-basic privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'8 weeks'::interval, 'IYYY"w"IW')||' (+8 weeks)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW'), 'Partman_Revoke', ARRAY['SELECT'], 'Check Partman_Revoke privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW')||' (+5 weeks)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'6 weeks'::interval, 'IYYY"w"IW'), 'Partman_Revoke', ARRAY['SELECT'], 'Check Partman_Revoke privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'6 weeks'::interval, 'IYYY"w"IW')||' (+6 weeks)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'7 weeks'::interval, 'IYYY"w"IW'), 'Partman_Revoke', ARRAY['SELECT'], 'Check Partman_Revoke privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'7 weeks'::interval, 'IYYY"w"IW')||' (+7 weeks)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'8 weeks'::interval, 'IYYY"w"IW'), 'Partman_Revoke', ARRAY['SELECT'], 'Check Partman_Revoke privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'8 weeks'::interval, 'IYYY"w"IW')||' (+8 weeks)'); SELECT is_empty('SELECT * FROM ONLY "Partman_test"."Time-taptest-Table"', 'Check that parent table has had no data inserted to it'); SELECT is_empty('SELECT * FROM ONLY "Partman_test"."Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')||'"' , 'Check that parent table Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')||' has had no data inserted to it'); SELECT is_empty('SELECT * FROM ONLY "Partman_test"."Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')||'"' , 'Check that parent table Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')||' has had no data inserted to it'); SELECT is_empty('SELECT * FROM ONLY "Partman_test"."Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')||'"' , 'Check that parent table Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')||' has had no data inserted to it'); SELECT is_empty('SELECT * FROM ONLY "Partman_test"."Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW')||'"' , 'Check that parent table Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW')||' has had no data inserted to it'); SELECT is_empty('SELECT * FROM ONLY "Partman_test"."Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW')||'"' , 'Check that parent table Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW')||' has had no data inserted to it'); SELECT is_empty('SELECT * FROM ONLY "Partman_test"."Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'2 week'::interval, 'IYYY"w"IW')||'"' , 'Check that parent table Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'2 week'::interval, 'IYYY"w"IW')||' has had no data inserted to it'); SELECT is_empty('SELECT * FROM ONLY "Partman_test"."Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'3 week'::interval, 'IYYY"w"IW')||'"' , 'Check that parent table Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'3 week'::interval, 'IYYY"w"IW')||' has had no data inserted to it'); SELECT is_empty('SELECT * FROM ONLY "Partman_test"."Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'4 week'::interval, 'IYYY"w"IW')||'"' , 'Check that parent table Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'4 week'::interval, 'IYYY"w"IW')||' has had no data inserted to it'); SELECT is_empty('SELECT * FROM ONLY "Partman_test"."Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'5 week'::interval, 'IYYY"w"IW')||'"' , 'Check that parent table Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'5 week'::interval, 'IYYY"w"IW')||' has had no data inserted to it'); -- Check that all subpartitions have their child tables +4 days after the minimum (minimal partition was already checked above) -- Since they have data, maintenance should work properly and catch up all the missing child tables SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'6 weeks'::interval)+'1 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'6 weeks'::interval)+'1 day'::interval, 'YYYY_MM_DD')||' exists (-6 weeks +1 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'6 weeks'::interval)+'2 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'6 weeks'::interval)+'2 day'::interval, 'YYYY_MM_DD')||' exists (-6 weeks +2 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'6 weeks'::interval)+'3 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'6 weeks'::interval)+'3 day'::interval, 'YYYY_MM_DD')||' exists (-6 weeks +3 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'6 weeks'::interval)+'4 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'6 weeks'::interval)+'4 day'::interval, 'YYYY_MM_DD')||' exists (-6 weeks +4 day)'); SELECT hasnt_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'6 weeks'::interval)+'5 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'6 weeks'::interval)+'5 day'::interval, 'YYYY_MM_DD')||'does not exist (-6 weeks +5 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'5 weeks'::interval)+'1 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'5 weeks'::interval)+'1 day'::interval, 'YYYY_MM_DD')||' exists (-5 weeks +1 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'5 weeks'::interval)+'2 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'5 weeks'::interval)+'2 day'::interval, 'YYYY_MM_DD')||' exists (-5 weeks +2 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'5 weeks'::interval)+'3 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'5 weeks'::interval)+'3 day'::interval, 'YYYY_MM_DD')||' exists (-5 weeks +3 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'5 weeks'::interval)+'4 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'5 weeks'::interval)+'4 day'::interval, 'YYYY_MM_DD')||' exists (-5 weeks +4 day)'); SELECT hasnt_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'5 weeks'::interval)+'5 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'5 weeks'::interval)+'5 day'::interval, 'YYYY_MM_DD')||' does not exist (-5 weeks +5 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'4 weeks'::interval)+'1 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'4 weeks'::interval)+'1 day'::interval, 'YYYY_MM_DD')||' exists (-4 weeks +1 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'4 weeks'::interval)+'2 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'4 weeks'::interval)+'2 day'::interval, 'YYYY_MM_DD')||' exists (-4 weeks +2 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'4 weeks'::interval)+'3 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'4 weeks'::interval)+'3 day'::interval, 'YYYY_MM_DD')||' exists (-4 weeks +3 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'4 weeks'::interval)+'4 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'4 weeks'::interval)+'4 day'::interval, 'YYYY_MM_DD')||' exists (-4 weeks +4 day)'); SELECT hasnt_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'4 weeks'::interval)+'5 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'4 weeks'::interval)+'5 day'::interval, 'YYYY_MM_DD')||' does not exist (-4 weeks +5 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'3 weeks'::interval)+'1 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'3 weeks'::interval)+'1 day'::interval, 'YYYY_MM_DD')||' exists (-3 weeks +1 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'3 weeks'::interval)+'2 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'3 weeks'::interval)+'2 day'::interval, 'YYYY_MM_DD')||' exists (-3 weeks +2 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'3 weeks'::interval)+'3 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'3 weeks'::interval)+'3 day'::interval, 'YYYY_MM_DD')||' exists (-3 weeks +3 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'3 weeks'::interval)+'4 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'3 weeks'::interval)+'4 day'::interval, 'YYYY_MM_DD')||' exists (-3 weeks +4 day)'); SELECT hasnt_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'3 weeks'::interval)+'5 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'3 weeks'::interval)+'5 day'::interval, 'YYYY_MM_DD')||' does not exist (-3 weeks +5 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'2 weeks'::interval)+'1 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'2 weeks'::interval)+'1 day'::interval, 'YYYY_MM_DD')||' exists (-2 weeks +1 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'2 weeks'::interval)+'2 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'2 weeks'::interval)+'2 day'::interval, 'YYYY_MM_DD')||' exists (-2 weeks +2 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'2 weeks'::interval)+'3 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'2 weeks'::interval)+'3 day'::interval, 'YYYY_MM_DD')||' exists (-2 weeks +3 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'2 weeks'::interval)+'4 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'2 weeks'::interval)+'4 day'::interval, 'YYYY_MM_DD')||' exists (-2 weeks +4 day)'); SELECT hasnt_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'2 weeks'::interval)+'5 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'2 weeks'::interval)+'5 day'::interval, 'YYYY_MM_DD')||' does not exist (-2 weeks +5 day)'); -- These tests may fail depending on the day of the week due to previous partitions being created my create_parent(). Example: Monday will cause failures because previous Friday table is created. SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'1 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'1 weeks'::interval)+'1 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'1 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'1 weeks'::interval)+'1 day'::interval, 'YYYY_MM_DD')||' exists (-1 weeks +1 day). This test may fail depending on day of the week.'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'1 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'1 weeks'::interval)+'2 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'1 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'1 weeks'::interval)+'2 day'::interval, 'YYYY_MM_DD')||' exists (-1 weeks +2 day) This test may fail depending on day of the week.'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'1 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'1 weeks'::interval)+'3 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'1 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'1 weeks'::interval)+'3 day'::interval, 'YYYY_MM_DD')||' exists (-1 weeks +3 day) This test may fail depending on day of the week.'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'1 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'1 weeks'::interval)+'4 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'1 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'1 weeks'::interval)+'4 day'::interval, 'YYYY_MM_DD')||' exists (-1 weeks +4 day) This test may fail depending on day of the week.'); SELECT hasnt_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'1 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'1 weeks'::interval)+'5 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'1 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'1 weeks'::interval)+'5 day'::interval, 'YYYY_MM_DD')||' does not exist (-1 weeks +5 day) This test may fail depending on day of the week.'); -- No need to test for this week's table. Should already have been tested elsewhere SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'1 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'1 weeks'::interval)+'1 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'1 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'1 weeks'::interval)+'1 day'::interval, 'YYYY_MM_DD')||' exists (+1 weeks +1 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'1 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'1 weeks'::interval)+'2 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'1 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'1 weeks'::interval)+'2 day'::interval, 'YYYY_MM_DD')||' exists (+1 weeks +2 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'1 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'1 weeks'::interval)+'3 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'1 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'1 weeks'::interval)+'3 day'::interval, 'YYYY_MM_DD')||' exists (+1 weeks +3 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'1 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'1 weeks'::interval)+'4 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'1 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'1 weeks'::interval)+'4 day'::interval, 'YYYY_MM_DD')||' exists (+1 weeks +4 day)'); SELECT hasnt_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'1 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'1 weeks'::interval)+'5 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'1 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'1 weeks'::interval)+'5 day'::interval, 'YYYY_MM_DD')||' does not exist (+1 weeks +5 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'2 weeks'::interval)+'1 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'2 weeks'::interval)+'1 day'::interval, 'YYYY_MM_DD')||' exists (+2 weeks +1 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'2 weeks'::interval)+'2 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'2 weeks'::interval)+'2 day'::interval, 'YYYY_MM_DD')||' exists (+2 weeks +2 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'2 weeks'::interval)+'3 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'2 weeks'::interval)+'3 day'::interval, 'YYYY_MM_DD')||' exists (+2 weeks +3 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'2 weeks'::interval)+'4 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'2 weeks'::interval)+'4 day'::interval, 'YYYY_MM_DD')||' exists (+2 weeks +4 day)'); SELECT hasnt_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'2 weeks'::interval)+'5 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'2 weeks'::interval)+'5 day'::interval, 'YYYY_MM_DD')||' does not exist (+2 weeks +5 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'3 weeks'::interval)+'1 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'3 weeks'::interval)+'1 day'::interval, 'YYYY_MM_DD')||' exists (+3 weeks +1 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'3 weeks'::interval)+'2 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'3 weeks'::interval)+'2 day'::interval, 'YYYY_MM_DD')||' exists (+3 weeks +2 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'3 weeks'::interval)+'3 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'3 weeks'::interval)+'3 day'::interval, 'YYYY_MM_DD')||' exists (+3 weeks +3 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'3 weeks'::interval)+'4 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'3 weeks'::interval)+'4 day'::interval, 'YYYY_MM_DD')||' exists (+3 weeks +4 day)'); SELECT hasnt_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'3 weeks'::interval)+'5 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'3 weeks'::interval)+'5 day'::interval, 'YYYY_MM_DD')||' does not exist (+3 weeks +5 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'4 weeks'::interval)+'1 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'4 weeks'::interval)+'1 day'::interval, 'YYYY_MM_DD')||' exists (+4 weeks +1 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'4 weeks'::interval)+'2 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'4 weeks'::interval)+'2 day'::interval, 'YYYY_MM_DD')||' exists (+4 weeks +2 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'4 weeks'::interval)+'3 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'4 weeks'::interval)+'3 day'::interval, 'YYYY_MM_DD')||' exists (+4 weeks +3 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'4 weeks'::interval)+'4 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'4 weeks'::interval)+'4 day'::interval, 'YYYY_MM_DD')||' exists (+4 weeks +4 day)'); SELECT hasnt_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'4 weeks'::interval)+'5 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'4 weeks'::interval)+'5 day'::interval, 'YYYY_MM_DD')||' does not exist (+4 weeks +5 day)'); -- Only minimum partition should be in the following sub partition sets SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'5 weeks'::interval), 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'5 weeks'::interval), 'YYYY_MM_DD')||' exists (+5 weeks)'); SELECT hasnt_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'36 days'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'5 weeks'::interval)+'1 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'36 days'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'5 weeks'::interval)+'1 day'::interval, 'YYYY_MM_DD')||' does not exist (+5 weeks +1 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'6 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'6 weeks'::interval), 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'6 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'6 weeks'::interval), 'YYYY_MM_DD')||' exists (+6 weeks)'); SELECT hasnt_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'43 days'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'6 weeks'::interval)+'1 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'43 days'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'6 weeks'::interval)+'1 day'::interval, 'YYYY_MM_DD')||' does not exist (+6 weeks +1 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'7 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'7 weeks'::interval), 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'7 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'7 weeks'::interval), 'YYYY_MM_DD')||' exists (+7 weeks)'); SELECT hasnt_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'50 days'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'7 weeks'::interval)+'1 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'50 days'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'7 weeks'::interval)+'1 day'::interval, 'YYYY_MM_DD')||' does not exist (+7 weeks +1 day)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'8 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'8 weeks'::interval), 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'8 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'8 weeks'::interval), 'YYYY_MM_DD')||' exists (+8 weeks)'); SELECT hasnt_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'57 days'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'8 weeks'::interval)+'1 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'57 days'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'8 weeks'::interval)+'1 day'::interval, 'YYYY_MM_DD')||' does not exist (+8 weeks +1 day)'); -- Default optimize_constraint is 30, so set it equal to premake for when this test was originally written -- This should then cause all the subpartition sets to create the full week of child tables and mark the sets as full UPDATE part_config SET premake = 5, optimize_constraint = 5 WHERE parent_table = 'Partman_test.Time-taptest-Table'; UPDATE part_config SET premake = 7, optimize_constraint = 7 WHERE parent_table LIKE 'Partman_test.Time-taptest-Table_p%'; SELECT run_maintenance(); -- Should automatically put additional constraint on 6 weeks prior to the current max value (+4 weeks) in the partition set. So 2 weeks ago. SELECT col_has_check('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW'), 'COL1' , 'Check for additional constraint on col1 on Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')||' (-2 weeks)'); -- Data at time of run_maintenance() call exists for +4 weeks, premake is now 5, so should be partitions for 5 weeks after that (+9 weeks)) SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'9 weeks'::interval, 'IYYY"w"IW'), 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'9 weeks'::interval, 'IYYY"w"IW')||' exists (+9 weeks)'); SELECT hasnt_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'10 weeks'::interval, 'IYYY"w"IW'), 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'10 weeks'::interval, 'IYYY"w"IW')||' does not exist (+10 weeks)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'9 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'9 weeks'::interval), 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'9 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'9 weeks'::interval), 'YYYY_MM_DD')||' exists (+9 weeks)'); SELECT hasnt_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'64 days'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'9 weeks'::interval)+'1 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'64 days'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'9 weeks'::interval)+'1 day'::interval, 'YYYY_MM_DD')||' does not exist (+9 weeks +1 day)'); SELECT col_is_pk('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'9 weeks'::interval, 'IYYY"w"IW'), ARRAY['COL1'], 'Check for primary key in Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'9 weeks'::interval, 'IYYY"w"IW')||' exists (+9 weeks)'); SELECT col_is_pk('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'9 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'9 weeks'::interval), 'YYYY_MM_DD') , ARRAY['COL1'], 'Check for primary key in Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'9 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'9 weeks'::interval), 'YYYY_MM_DD')||' exists (+9 weeks)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'9 weeks'::interval, 'IYYY"w"IW'), 'partman-basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman-basic privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'9 weeks'::interval, 'IYYY"w"IW')||' (+9 weeks)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'9 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'9 weeks'::interval), 'YYYY_MM_DD') , 'partman-basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman-basic privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'9 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'9 weeks'::interval), 'YYYY_MM_DD')||' (+9 weeks)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'9 weeks'::interval, 'IYYY"w"IW'), 'Partman_Revoke', ARRAY['SELECT'], 'Check Partman_Revoke privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'9 weeks'::interval, 'IYYY"w"IW')||' (+9 weeks)'); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'9 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'9 weeks'::interval), 'YYYY_MM_DD') , 'Partman_Revoke', ARRAY['SELECT'], 'Check Partman_Revoke privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'9 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'9 weeks'::interval), 'YYYY_MM_DD')||' (+9 weeks)'); -- This should properly go into the child table of the +5 week set INSERT INTO "Partman_test"."Time-taptest-Table" ("COL1", "Col-3") VALUES (generate_series(131,140), date_trunc('week', CURRENT_TIMESTAMP + '5 weeks'::interval)); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'5 week'::interval, 'IYYY"w"IW')||'"', ARRAY[10], 'Check count from Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'5 week'::interval, 'IYYY"w"IW')||' (+5 weeks)'); -- Run maintenance again and check for constraint at -1 week SELECT run_maintenance(); SELECT col_has_check('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'1 weeks'::interval, 'IYYY"w"IW'), 'COL1' , 'Check for additional constraint on col1 on Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'1 weeks'::interval, 'IYYY"w"IW')||' (-1 weeks)'); -- Also +10 week tables should be made since newest data is +5 weeks and premake is +5 SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'10 weeks'::interval, 'IYYY"w"IW'), 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'10 weeks'::interval, 'IYYY"w"IW')||' exists (+10 weeks)'); SELECT hasnt_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'11 weeks'::interval, 'IYYY"w"IW'), 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'11 weeks'::interval, 'IYYY"w"IW')||' does not exist (+11 weeks)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'10 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'10 weeks'::interval), 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'10 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'10 weeks'::interval), 'YYYY_MM_DD')||' exists (+10 weeks)'); SELECT hasnt_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'71 days'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'10 weeks'::interval)+'1 day'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'71 days'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'10 days'::interval)+'1 day'::interval, 'YYYY_MM_DD')||' does not exist (+10 weeks +1 day)'); -- Check for next 3 days stuff being created (changed from 4 to 7) SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' exists (+5 days)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')||' exists (+6 days)'); SELECT has_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD') , 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')||' exists (+7 days)'); SELECT col_is_pk('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'5 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'5 day'::interval, 'YYYY_MM_DD'), ARRAY['COL1'], 'Check for primary key in Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'5 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'5 day'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'6 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'6 day'::interval, 'YYYY_MM_DD'), ARRAY['COL1'], 'Check for primary key in Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'6 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'6 day'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'7 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'7 day'::interval, 'YYYY_MM_DD'), ARRAY['COL1'], 'Check for primary key in Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'7 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'7 day'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'5 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'5 day'::interval, 'YYYY_MM_DD') , 'partman-basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman-basic privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'5 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'5 day'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'6 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'6 day'::interval, 'YYYY_MM_DD') , 'partman-basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman-basic privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'6 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'6 day'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'7 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'7 day'::interval, 'YYYY_MM_DD') , 'partman-basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman-basic privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'7 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'7 day'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'5 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'5 day'::interval, 'YYYY_MM_DD') , 'Partman_Revoke', ARRAY['SELECT'], 'Check Partman_Revoke privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'5 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'5 day'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'6 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'6 day'::interval, 'YYYY_MM_DD') , 'Partman_Revoke', ARRAY['SELECT'], 'Check Partman_Revoke privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'6 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'6 day'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'7 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'7 day'::interval, 'YYYY_MM_DD') , 'Partman_Revoke', ARRAY['SELECT'], 'Check Partman_Revoke privileges of Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'7 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP+'7 day'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'4 weeks'::interval), 'YYYY_MM_DD')||'"', ARRAY[10], 'Check count from Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'4 weeks'::interval), 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'3 weeks'::interval), 'YYYY_MM_DD')||'"', ARRAY[5], 'Check count from Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'3 weeks'::interval), 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'2 weeks'::interval), 'YYYY_MM_DD')||'"', ARRAY[5], 'Check count from Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'2 weeks'::interval), 'YYYY_MM_DD')); -- Part of the 8 day constraint test mentioned above that I can't get working. See previous comment for why this causes count tests to fail on mondays --SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'8 days'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP-'8 days'::interval, 'YYYY_MM_DD')||'"', -- ARRAY[10], 'Check count from Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'8 days'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP-'8 days'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'1 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'1 weeks'::interval), 'YYYY_MM_DD')||'"', ARRAY[7], 'Check count from Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'1 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP-'1 weeks'::interval), 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'1 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'1 weeks'::interval), 'YYYY_MM_DD')||'"', ARRAY[12], 'Check count from Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'1 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'1 weeks'::interval), 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'2 weeks'::interval), 'YYYY_MM_DD')||'"', ARRAY[21], 'Check count from Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'2 weeks'::interval), 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'3 weeks'::interval), 'YYYY_MM_DD')||'"', ARRAY[15], 'Check count from Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'3 weeks'::interval), 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'4 weeks'::interval), 'YYYY_MM_DD')||'"', ARRAY[15], 'Check count from Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'4 weeks'::interval), 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM "Partman_test"."Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'5 weeks'::interval), 'YYYY_MM_DD')||'"', ARRAY[10], 'Check count from Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(date_trunc('week', CURRENT_TIMESTAMP+'5 weeks'::interval), 'YYYY_MM_DD')); -- Testing retention for daily -- All daily tables older than 2 weeks. Should only drop the daily subpartitions, not the weekly parents yet UPDATE part_config SET retention = '2 weeks', retention_keep_table = false WHERE parent_table LIKE 'Partman_test.Time-taptest-Table_p%' AND partition_interval = '1 day'; SELECT run_maintenance(); SELECT is_empty('SELECT partition_tablename FROM show_partitions(''Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW')||''')', 'Check that 6 week old parent Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW')||' table has no children'); SELECT is_empty('SELECT partition_tablename FROM show_partitions(''Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW')||''')', 'Check that 5 week old parent Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW')||' table has no children'); SELECT is_empty('SELECT partition_tablename FROM show_partitions(''Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')||''')', 'Check that 4 week old parent Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')||' table has no children'); SELECT is_empty('SELECT partition_tablename FROM show_partitions(''Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')||''')', 'Check that 3 week old parent Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')||' table has no children'); SELECT results_eq('SELECT CASE WHEN count(*) >= 1 THEN 1 ELSE 0 END FROM partman.show_partitions(''Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')||''')', ARRAY[1], 'Check that 2 week old parent Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')||' still has at least 1 child'); -- Now indexes on table olders than 3 days UPDATE part_config SET retention = '3 days', retention_keep_table = true, retention_keep_index = false WHERE parent_table LIKE 'Partman_test.Time-taptest-Table_p%' AND partition_interval = '1 day'; SELECT run_maintenance(); SELECT col_isnt_pk('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'YYYY_MM_DD'), ARRAY['COL1'], 'Check that primary key was dropped (2 weeks ago) in Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'YYYY_MM_DD')); SELECT col_isnt_pk('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'1 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP-'1 weeks'::interval, 'YYYY_MM_DD'), ARRAY['COL1'], 'Check that primary key was dropped (1 week ago) in Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'1 weeks'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP-'1 weeks'::interval, 'YYYY_MM_DD')); SELECT col_isnt_pk('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'4 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP-'4 day'::interval, 'YYYY_MM_DD'), ARRAY['COL1'], 'Check that primary key was dropped (4 days ago) in Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'4 day'::interval, 'IYYY"w"IW')||'_p'||to_char(CURRENT_TIMESTAMP-'4 day'::interval, 'YYYY_MM_DD')); --NOTE The tables with the above indexes won't get dropped below because they're no longer part of the inheritance tree. No big deal. They'll be gone in the end. -- Now do retention on top parent table to get rid of everything not needed anymore. Don't set shorter than 7 days otherwise "now()-1 week" table may get dropped. UPDATE part_config SET retention = '7 days', retention_keep_table = false WHERE parent_table = 'Partman_test.Time-taptest-Table'; -- Have to do all subpartitions before parent, otherwise it errors out if the top parent gets maintenance run first then non-existent children try to run UPDATE part_config SET retention = NULL, retention_keep_table = false WHERE parent_table LIKE 'Partman_test.Time-taptest-Table_p%'; SELECT run_maintenance(); SELECT hasnt_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW'), 'After retention Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW')||' does not exist'); SELECT hasnt_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW'), 'After retention Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW')||' does not exist'); SELECT hasnt_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW'), 'After retention Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')||' does not exist'); SELECT hasnt_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW'), 'After retention Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')||' does not exist'); SELECT hasnt_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW'), 'After retention Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')||' does not exist'); -- Undo daily for each week SELECT undo_partition_time('Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'1 weeks'::interval, 'IYYY"w"IW'), 20, p_keep_table := false); SELECT undo_partition_time('Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW'), 20, p_keep_table := false); SELECT undo_partition_time('Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'1 weeks'::interval, 'IYYY"w"IW'), 20, p_keep_table := false); SELECT undo_partition_time('Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW'), 20, p_keep_table := false); SELECT undo_partition_time('Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW'), 20, p_keep_table := false); SELECT undo_partition_time('Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW'), 20, p_keep_table := false); SELECT undo_partition_time('Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW'), 20, p_keep_table := false); SELECT undo_partition_time('Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'6 weeks'::interval, 'IYYY"w"IW'), 20, p_keep_table := false); SELECT undo_partition_time('Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'7 weeks'::interval, 'IYYY"w"IW'), 20, p_keep_table := false); SELECT undo_partition_time('Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'8 weeks'::interval, 'IYYY"w"IW'), 20, p_keep_table := false); SELECT undo_partition_time('Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'9 weeks'::interval, 'IYYY"w"IW'), 20, p_keep_table := false); SELECT undo_partition_time('Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'10 weeks'::interval, 'IYYY"w"IW'), 20, p_keep_table := false); SELECT is_empty('SELECT partition_tablename FROM show_partitions(''Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'1 weeks'::interval, 'IYYY"w"IW')||''')', 'Check that 1 week old parent Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'1 weeks'::interval, 'IYYY"w"IW')||' table has no children'); SELECT is_empty('SELECT partition_tablename FROM show_partitions(''Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW')||''')', 'Check that current parent Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW')||' table has no children'); SELECT is_empty('SELECT partition_tablename FROM show_partitions(''Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'1 weeks'::interval, 'IYYY"w"IW')||''')', 'Check that 1 week future parent Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'1 weeks'::interval, 'IYYY"w"IW')||' table has no children'); SELECT is_empty('SELECT partition_tablename FROM show_partitions(''Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')||''')', 'Check that 2 week future parent Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')||' table has no children'); SELECT is_empty('SELECT partition_tablename FROM show_partitions(''Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')||''')', 'Check that 3 week future parent Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')||' table has no children'); SELECT is_empty('SELECT partition_tablename FROM show_partitions(''Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')||''')', 'Check that 4 week future parent Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')||' table has no children'); SELECT is_empty('SELECT partition_tablename FROM show_partitions(''Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW')||''')', 'Check that 5 week future parent Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW')||' table has no children'); SELECT is_empty('SELECT partition_tablename FROM show_partitions(''Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'6 weeks'::interval, 'IYYY"w"IW')||''')', 'Check that 6 week future parent Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'6 weeks'::interval, 'IYYY"w"IW')||' table has no children'); SELECT is_empty('SELECT partition_tablename FROM show_partitions(''Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'7 weeks'::interval, 'IYYY"w"IW')||''')', 'Check that 7 week future parent Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'7 weeks'::interval, 'IYYY"w"IW')||' table has no children'); SELECT is_empty('SELECT partition_tablename FROM show_partitions(''Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'8 weeks'::interval, 'IYYY"w"IW')||''')', 'Check that 8 week future parent Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'8 weeks'::interval, 'IYYY"w"IW')||' table has no children'); SELECT is_empty('SELECT partition_tablename FROM show_partitions(''Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'9 weeks'::interval, 'IYYY"w"IW')||''')', 'Check that 9 week future parent Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'9 weeks'::interval, 'IYYY"w"IW')||' table has no children'); SELECT is_empty('SELECT partition_tablename FROM show_partitions(''Partman_test.Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'10 weeks'::interval, 'IYYY"w"IW')||''')', 'Check that 10 week future parent Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'10 weeks'::interval, 'IYYY"w"IW')||' table has no children'); -- Undo weekly SELECT undo_partition_time('Partman_test.Time-taptest-Table', 20, p_keep_table := false); SELECT is_empty('SELECT partition_tablename FROM show_partitions(''Partman_test.Time-taptest-Table'')', 'Check that parent Time-taptest-Table table has no children'); SELECT is_empty('SELECT * FROM part_config WHERE parent_table LIKE ''Partman_test.Time-taptest-Table''', 'Check that part_config table is empty'); SELECT hasnt_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP-'1 weeks'::interval, 'IYYY"w"IW'), 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP-'1 weeks'::interval, 'IYYY"w"IW')||' does not exist'); SELECT hasnt_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW'), 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW')||' does not exist'); SELECT hasnt_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'1 weeks'::interval, 'IYYY"w"IW'), 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'1 weeks'::interval, 'IYYY"w"IW')||' does not exist'); SELECT hasnt_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW'), 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')||' does not exist'); SELECT hasnt_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW'), 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')||' does not exist'); SELECT hasnt_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW'), 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')||' does not exist'); SELECT hasnt_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW'), 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW')||' does not exist'); SELECT hasnt_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'6 weeks'::interval, 'IYYY"w"IW'), 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'6 weeks'::interval, 'IYYY"w"IW')||' does not exist'); SELECT hasnt_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'7 weeks'::interval, 'IYYY"w"IW'), 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'7 weeks'::interval, 'IYYY"w"IW')||' does not exist'); SELECT hasnt_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'8 weeks'::interval, 'IYYY"w"IW'), 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'8 weeks'::interval, 'IYYY"w"IW')||' does not exist'); SELECT hasnt_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'9 weeks'::interval, 'IYYY"w"IW'), 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'9 weeks'::interval, 'IYYY"w"IW')||' does not exist'); SELECT hasnt_table('Partman_test', 'Time-taptest-Table_p'||to_char(CURRENT_TIMESTAMP+'10 weeks'::interval, 'IYYY"w"IW'), 'Check Time-taptest-Table_'||to_char(CURRENT_TIMESTAMP+'10 weeks'::interval, 'IYYY"w"IW')||' does not exist'); SELECT * FROM finish(); ROLLBACK; pg_partman-2.2.2/test/test-time-weekly-trunc.sql000066400000000000000000001300211262146621700217130ustar00rootroot00000000000000-- ########## TIME WEEKLY TESTS ########## -- Other tests: Long name truncation \set ON_ERROR_ROLLBACK 1 \set ON_ERROR_STOP true BEGIN; SELECT set_config('search_path','partman, public',false); SELECT plan(122); CREATE SCHEMA partman_test; CREATE SCHEMA partman_retention_test; CREATE ROLE partman_basic; CREATE ROLE partman_revoke; CREATE ROLE partman_owner; CREATE TABLE partman_test.time_taptest_table_123456789012345678901234567890123457890 (col1 int primary key, col2 text, col3 timestamptz NOT NULL DEFAULT now()); INSERT INTO partman_test.time_taptest_table_123456789012345678901234567890123457890 (col1, col3) VALUES (generate_series(1,10), CURRENT_TIMESTAMP); GRANT SELECT,INSERT,UPDATE ON partman_test.time_taptest_table_123456789012345678901234567890123457890 TO partman_basic; GRANT ALL ON partman_test.time_taptest_table_123456789012345678901234567890123457890 TO partman_revoke; SELECT create_parent('partman_test.time_taptest_table_123456789012345678901234567890123457890', 'col3', 'time', 'weekly'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW'), 'Check time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW')||' does not exist'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW')||' does not exist'); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW')); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW')); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW')); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW')); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')); SELECT results_eq('SELECT partition_data_time(''partman_test.time_taptest_table_123456789012345678901234567890123457890'')::int', ARRAY[10], 'Check that partitioning function returns correct count of rows moved'); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table_123456789012345678901234567890123457890', 'Check that parent table has had data moved to partition'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_123456789012345678901234567890123457890', ARRAY[10], 'Check count from parent table'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW'), ARRAY[10], 'Check count from time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW')); REVOKE INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER ON partman_test.time_taptest_table_123456789012345678901234567890123457890 FROM partman_revoke; INSERT INTO partman_test.time_taptest_table_123456789012345678901234567890123457890 (col1, col3) VALUES (generate_series(11,20), CURRENT_TIMESTAMP + '1 week'::interval); INSERT INTO partman_test.time_taptest_table_123456789012345678901234567890123457890 (col1, col3) VALUES (generate_series(21,25), CURRENT_TIMESTAMP + '2 weeks'::interval); INSERT INTO partman_test.time_taptest_table_123456789012345678901234567890123457890 (col1, col3) VALUES (generate_series(26,30), CURRENT_TIMESTAMP + '3 weeks'::interval); INSERT INTO partman_test.time_taptest_table_123456789012345678901234567890123457890 (col1, col3) VALUES (generate_series(31,37), CURRENT_TIMESTAMP + '4 weeks'::interval); INSERT INTO partman_test.time_taptest_table_123456789012345678901234567890123457890 (col1, col3) VALUES (generate_series(40,49), CURRENT_TIMESTAMP - '1 week'::interval); INSERT INTO partman_test.time_taptest_table_123456789012345678901234567890123457890 (col1, col3) VALUES (generate_series(50,70), CURRENT_TIMESTAMP - '2 weeks'::interval); INSERT INTO partman_test.time_taptest_table_123456789012345678901234567890123457890 (col1, col3) VALUES (generate_series(71,85), CURRENT_TIMESTAMP - '3 weeks'::interval); INSERT INTO partman_test.time_taptest_table_123456789012345678901234567890123457890 (col1, col3) VALUES (generate_series(86,100), CURRENT_TIMESTAMP - '4 weeks'::interval); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table_123456789012345678901234567890123457890', 'Check that parent table has had no data inserted to it'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW'), ARRAY[10], 'Check count from time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW'), ARRAY[5], 'Check count from time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW'), ARRAY[5], 'Check count from time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW'), ARRAY[7], 'Check count from time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW'), ARRAY[10], 'Check count from time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW'), ARRAY[21], 'Check count from time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW'), ARRAY[15], 'Check count from time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW'), ARRAY[15], 'Check count from time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')); UPDATE part_config SET premake = 5 WHERE parent_table = 'partman_test.time_taptest_table_123456789012345678901234567890123457890'; SELECT run_maintenance(); INSERT INTO partman_test.time_taptest_table_123456789012345678901234567890123457890 (col1, col3) VALUES (generate_series(101,122), CURRENT_TIMESTAMP + '5 weeks'::interval); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW')||' exists'); -- 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_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW')); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table_123456789012345678901234567890123457890', 'Check that parent table has had no data inserted to it'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW'), ARRAY[22], 'Check count from time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')); -- Cannot test next partitions privileges. Different lengths of months will sometimes cause an extra partition. GRANT DELETE ON partman_test.time_taptest_table_123456789012345678901234567890123457890 TO partman_basic; REVOKE ALL ON partman_test.time_taptest_table_123456789012345678901234567890123457890 FROM partman_revoke; ALTER TABLE partman_test.time_taptest_table_123456789012345678901234567890123457890 OWNER TO partman_owner; UPDATE part_config SET premake = 6 WHERE parent_table = 'partman_test.time_taptest_table_123456789012345678901234567890123457890'; SELECT run_maintenance(); INSERT INTO partman_test.time_taptest_table_123456789012345678901234567890123457890 (col1, col3) VALUES (generate_series(123,150), CURRENT_TIMESTAMP + '6 weeks'::interval); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table_123456789012345678901234567890123457890', 'Check that parent table has had no data inserted to it'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_123456789012345678901234567890123457890', ARRAY[148], 'Check count from parent table'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'6 weeks'::interval, 'IYYY"w"IW'), ARRAY[28], 'Check count from time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'6 weeks'::interval, 'IYYY"w"IW')); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'6 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'6 weeks'::interval, 'IYYY"w"IW')||' exists'); -- 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_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'6 weeks'::interval, 'IYYY"w"IW'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'6 weeks'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')); -- Cannot test next partitions privileges. Different lengths of months will sometimes cause an extra partition. INSERT INTO partman_test.time_taptest_table_123456789012345678901234567890123457890 (col1, col3) VALUES (generate_series(200,210), CURRENT_TIMESTAMP + '20 weeks'::interval); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table_123456789012345678901234567890123457890', ARRAY[11], 'Check that data outside trigger scope goes to parent'); SELECT reapply_privileges('partman_test.time_taptest_table_123456789012345678901234567890123457890'); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'6 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'6 weeks'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'6 weeks'::interval, 'IYYY"w"IW'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'6 weeks'::interval, 'IYYY"w"IW')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'6 weeks'::interval, 'IYYY"w"IW'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'6 weeks'::interval, 'IYYY"w"IW')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')); SELECT drop_partition_time('partman_test.time_taptest_table_123456789012345678901234567890123457890', '3 weeks', p_keep_table := false); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')||' does not exist'); UPDATE part_config SET retention = '2 weeks'::interval WHERE parent_table = 'partman_test.time_taptest_table_123456789012345678901234567890123457890'; SELECT drop_partition_time('partman_test.time_taptest_table_123456789012345678901234567890123457890', p_retention_schema := 'partman_retention_test'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')||' does not exist'); SELECT has_table('partman_retention_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')||' got moved to new schema'); SELECT undo_partition_time('partman_test.time_taptest_table_123456789012345678901234567890123457890', 20, p_keep_table := false); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table_123456789012345678901234567890123457890', ARRAY[129], 'Check count from parent table after undo'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP+'6 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'6 weeks'::interval, 'IYYY"w"IW')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')||' does not exist'); SELECT * FROM finish(); ROLLBACK; pg_partman-2.2.2/test/test-time-weekly.sql000066400000000000000000001315541262146621700205760ustar00rootroot00000000000000-- ########## TIME WEEKLY 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(146); CREATE SCHEMA partman_test; CREATE SCHEMA partman_retention_test; CREATE ROLE partman_basic; CREATE ROLE partman_revoke; CREATE ROLE partman_owner; CREATE TABLE partman_test.time_taptest_table (col1 int primary key, col2 text, col3 timestamptz NOT NULL DEFAULT now()); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(1,10), CURRENT_TIMESTAMP - '8 weeks'::interval); GRANT SELECT,INSERT,UPDATE ON partman_test.time_taptest_table TO partman_basic; GRANT ALL ON partman_test.time_taptest_table TO partman_revoke; -- Test that premake can be less than optimize_constraint without issues SELECT create_parent('partman_test.time_taptest_table', 'col3', 'time', 'weekly', '{"col1"}', p_premake := 2, p_start_partition := to_char(CURRENT_TIMESTAMP-'8 weeks'::interval, 'YYYY-MM-DD HH24:MI:SS')); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW')||' exists (+1 weeks)'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')||' exists (+2 weeks)'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW')||' does not exist (+3 weeks)'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW')||' exists (-1 weeks)'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')||' exists (-2 weeks)'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')||' exists (-3 weeks)'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')||' exists (-4 weeks)'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW')||' exists (-5 weeks)'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW')||' exists (-6 weeks)'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'IYYY"w"IW')||' exists (-7 weeks)'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'8 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'8 weeks'::interval, 'IYYY"w"IW')||' exists (-8 weeks)'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'9 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'9 weeks'::interval, 'IYYY"w"IW')||' does not exist (-9 weeks)'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW')||' (+1 weeks)'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')||' (+2 weeks)'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW')||' (-1 weeks)'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')||' (-2 weeks)'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')||' (-3 weeks)'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')||' (-4 weeks)'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW')||' (-5 weeks)'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW')||' (-6 weeks)'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'IYYY"w"IW'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'IYYY"w"IW')||' (-7 weeks)'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'8 weeks'::interval, 'IYYY"w"IW'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'8 weeks'::interval, 'IYYY"w"IW')||' (-8 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW')||' (+1 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')||' (+2 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW')||' (-1 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')||' (-2 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')||' (-3 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')||' (-4 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW')||' (-5 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW')||' (-6 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'IYYY"w"IW')||' (-7 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'8 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'8 weeks'::interval, 'IYYY"w"IW')||' (-8 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW')||' (+1 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')||' (+2 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW')||' (-1 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')||' (-2 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')||' (-3 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')||' (-4 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW')||' (-5 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW')||' (-6 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'IYYY"w"IW'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'IYYY"w"IW')||' (-7 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'8 weeks'::interval, 'IYYY"w"IW'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'8 weeks'::interval, 'IYYY"w"IW')||' (-8 weeks)'); SELECT results_eq('SELECT partition_data_time(''partman_test.time_taptest_table'')::int', ARRAY[10], 'Check that partitioning function returns correct count of rows moved'); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table', 'Check that parent table has had data moved to partition'); 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, 'IYYY"w"IW'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'8 weeks'::interval, 'IYYY"w"IW')||' (-8 weeks)'); REVOKE INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER ON partman_test.time_taptest_table FROM partman_revoke; INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(11,20), CURRENT_TIMESTAMP - '7 weeks'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(21,25), CURRENT_TIMESTAMP - '6 weeks'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(26,30), CURRENT_TIMESTAMP - '5 weeks'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(31,37), CURRENT_TIMESTAMP - '4 week'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(38,49), CURRENT_TIMESTAMP - '3 week'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(50,70), CURRENT_TIMESTAMP - '2 weeks'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(71,85), CURRENT_TIMESTAMP - '1 week'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(86,100), CURRENT_TIMESTAMP + '1 week'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(101,110), CURRENT_TIMESTAMP + '2 weeks'::interval); SELECT partition_data_time('partman_test.time_taptest_table', 20); 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, 'IYYY"w"IW'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'IYYY"w"IW')||' (-7 weeks)'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW'), ARRAY[5], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW')||' (-6 weeks)'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW'), ARRAY[5], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW')||' (-5 weeks)'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW'), ARRAY[7], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')||' (-4 weeks)'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW'), ARRAY[12], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')||' (-3 weeks)'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW'), ARRAY[21], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')||' (-2 weeks)'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW'), ARRAY[15], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW')||' (-1 weeks)'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW'), ARRAY[15], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW')||' (+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, 'IYYY"w"IW'), 'col1' , 'Check for additional constraint on col1 on time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')||' (-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, 'IYYY"w"IW')); SELECT apply_constraints('partman_test.time_taptest_table', 'partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'IYYY"w"IW')); SELECT apply_constraints('partman_test.time_taptest_table', 'partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW')); SELECT apply_constraints('partman_test.time_taptest_table', 'partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW')); SELECT col_has_check('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'8 weeks'::interval, 'IYYY"w"IW'), 'col1' , 'Check for additional constraint on col1 on time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'8 weeks'::interval, 'IYYY"w"IW')); SELECT col_has_check('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'IYYY"w"IW'), 'col1' , 'Check for additional constraint on col1 on time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'IYYY"w"IW')); SELECT col_has_check('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW'), 'col1' , 'Check for additional constraint on col1 on time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW')); SELECT col_has_check('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW'), 'col1' , 'Check for additional constraint on col1 on time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW')); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(111,120), CURRENT_TIMESTAMP + '3 weeks'::interval); -- +2 weeks data was inserted above, so it premakes +3 weeks after that SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')||' exists (+3 weeks'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')||' exists (+4 weeks'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW')||' exists (+5 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, 'IYYY"w"IW'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')||' (+3 weeks)'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')||' (+4 weeks)'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW')||' (+5 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, 'IYYY"w"IW'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')||' (+3 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW')||' (now)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW')||' (+1 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')||' (+2 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')||' (+3 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')||' (+4 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW')||' (+5 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW')||' (-1 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')||' (-2 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')||' (-3 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')||' (-4 weeks)'); GRANT DELETE ON partman_test.time_taptest_table TO partman_basic; REVOKE ALL ON partman_test.time_taptest_table FROM partman_revoke; ALTER TABLE partman_test.time_taptest_table OWNER TO partman_owner; 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), CURRENT_TIMESTAMP + '4 weeks'::interval); 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', 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, 'IYYY"w"IW'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')||' (+4 weeks)'); SELECT col_has_check('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW'), 'col1' , 'Check for additional constraint on col1 on time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'6 weeks'::interval, 'IYYY"w"IW')||' exists (+6 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+'6 weeks'::interval, 'IYYY"w"IW'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 weeks'::interval, 'IYYY"w"IW')||' (+6 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW')||' (now)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW')||' (+1 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')||' (+2 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')||' (+3 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')||' (+4 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW')||' (+5 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 weeks'::interval, 'IYYY"w"IW')||' (+6 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW')||' (-1 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')||' (-2 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')||' (-3 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')||' (-4 weeks)'); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(200,210), CURRENT_TIMESTAMP + '20 weeks'::interval); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table', ARRAY[11], 'Check that data outside trigger scope goes to parent'); SELECT reapply_privileges('partman_test.time_taptest_table'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW')||' (now)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW')||' (+1 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')||' (+2 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')||' (+3 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')||' (+4 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW')||' (+5 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 weeks'::interval, 'IYYY"w"IW')||' (+6 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW')||' (-1 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')||' (-2 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')||' (-3 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')||' (-4 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW')||' (now)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW')||' (+1 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')||' (+2 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')||' (+3 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')||' (+4 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW')||' (+5 weeks)'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 weeks'::interval, 'IYYY"w"IW'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 weeks'::interval, 'IYYY"w"IW')||' (+6 weeks)'); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW')||' (now)'); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW')||' (+1 weeks)'); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')||' (+2 weeks)'); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')||' (+3 weeks)'); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')||' (+4 weeks)'); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW')||' (+5 weeks)'); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 weeks'::interval, 'IYYY"w"IW'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 weeks'::interval, 'IYYY"w"IW')||' (+6 weeks)'); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW')||' (-1 weeks)'); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')||' (-2 weeks)'); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')||' (-3 weeks)'); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')||' (-4 weeks)'); 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, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'4 weeks'::interval, 'IYYY"w"IW')||' does not exist (-4 weeks)'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'5 weeks'::interval, 'IYYY"w"IW')||' does not exist (-5 weeks)'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'6 weeks'::interval, 'IYYY"w"IW')||' does not exist (-6 weeks)'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'7 weeks'::interval, 'IYYY"w"IW')||' does not exist (-7 weeks)'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'8 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'8 weeks'::interval, 'IYYY"w"IW')||' 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, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')||' does not exist (-3 weeks)'); SELECT has_table('partman_retention_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'3 weeks'::interval, 'IYYY"w"IW')||' got moved to new schema (-3 weeks)'); SELECT undo_partition_time('partman_test.time_taptest_table', 20, p_keep_table := false); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table', ARRAY[92], 'Check count from parent table after undo'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP, 'IYYY"w"IW')||' does not exist (now)'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'1 week'::interval, 'IYYY"w"IW')||' does not exist (+1 weeks)'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'2 weeks'::interval, 'IYYY"w"IW')||' does not exist (+2 weeks)'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'3 weeks'::interval, 'IYYY"w"IW')||' does not exist (+3 weeks)'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'4 weeks'::interval, 'IYYY"w"IW')||' does not exist (+4 weeks)'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'5 weeks'::interval, 'IYYY"w"IW')||' does not exist (+5 weeks)'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'6 weeks'::interval, 'IYYY"w"IW')||' does not exist (+6 weeks)'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'1 week'::interval, 'IYYY"w"IW')||' does not exist (-1 weeks)'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'2 weeks'::interval, 'IYYY"w"IW')||' does not exist (-2 weeks)'); SELECT * FROM finish(); ROLLBACK; pg_partman-2.2.2/test/test-time-yearly-trunc.sql000066400000000000000000001557151262146621700217410ustar00rootroot00000000000000-- ########## TIME YEARLY TESTS ########## -- Other tests: Ensure name truncation works, make sure dynamic trigger is working \set ON_ERROR_ROLLBACK 1 \set ON_ERROR_STOP true BEGIN; SELECT set_config('search_path','partman, public',false); SELECT plan(156); CREATE SCHEMA partman_test; CREATE SCHEMA partman_retention_test; CREATE ROLE partman_basic; CREATE ROLE partman_revoke; CREATE ROLE partman_owner; CREATE TABLE partman_test.time_taptest_table_123456789012345678901234567890123456780 (col1 int primary key, col2 text, col3 timestamptz NOT NULL DEFAULT now()); INSERT INTO partman_test.time_taptest_table_123456789012345678901234567890123456780 (col1, col3) VALUES (generate_series(1,10), CURRENT_TIMESTAMP); GRANT SELECT,INSERT,UPDATE ON partman_test.time_taptest_table_123456789012345678901234567890123456780 TO partman_basic; GRANT ALL ON partman_test.time_taptest_table_123456789012345678901234567890123456780 TO partman_revoke; SELECT create_parent('partman_test.time_taptest_table_123456789012345678901234567890123456780', 'col3', 'time', 'yearly'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP, 'YYYY'), 'Check time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY'), 'Check time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY'), 'Check time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'4 years'::interval, 'YYYY'), 'Check time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'4 years'::interval, 'YYYY')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'5 years'::interval, 'YYYY'), 'Check time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'5 years'::interval, 'YYYY')||' does not exist'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY'), 'Check time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY'), 'Check time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'3 years'::interval, 'YYYY'), 'Check time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'3 years'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'4 years'::interval, 'YYYY'), 'Check time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'4 years'::interval, 'YYYY')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'5 years'::interval, 'YYYY'), 'Check time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'5 years'::interval, 'YYYY')||' does not exist'); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'4 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'4 years'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'3 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'3 years'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'4 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'4 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'4 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'4 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'3 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'3 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'4 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'4 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP, 'YYYY'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_'||to_char(CURRENT_TIMESTAMP, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'4 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'4 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'3 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'3 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'4 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'4 years'::interval, 'YYYY')); SELECT results_eq('SELECT partition_data_time(''partman_test.time_taptest_table_123456789012345678901234567890123456780'')::int', ARRAY[10], 'Check that partitioning function returns correct count of rows moved'); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table_123456789012345678901234567890123456780', 'Check that parent table has had data moved to partition'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_123456789012345678901234567890123456780', ARRAY[10], 'Check count from parent table'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP, 'YYYY'), ARRAY[10], 'Check count from time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')); REVOKE INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER ON partman_test.time_taptest_table_123456789012345678901234567890123456780 FROM partman_revoke; INSERT INTO partman_test.time_taptest_table_123456789012345678901234567890123456780 (col1, col3) VALUES (generate_series(11,20), CURRENT_TIMESTAMP + '1 year'::interval); INSERT INTO partman_test.time_taptest_table_123456789012345678901234567890123456780 (col1, col3) VALUES (generate_series(21,25), CURRENT_TIMESTAMP + '2 years'::interval); INSERT INTO partman_test.time_taptest_table_123456789012345678901234567890123456780 (col1, col3) VALUES (generate_series(26,30), CURRENT_TIMESTAMP + '3 years'::interval); INSERT INTO partman_test.time_taptest_table_123456789012345678901234567890123456780 (col1, col3) VALUES (generate_series(31,37), CURRENT_TIMESTAMP + '4 years'::interval); INSERT INTO partman_test.time_taptest_table_123456789012345678901234567890123456780 (col1, col3) VALUES (generate_series(40,49), CURRENT_TIMESTAMP - '1 years'::interval); INSERT INTO partman_test.time_taptest_table_123456789012345678901234567890123456780 (col1, col3) VALUES (generate_series(50,70), CURRENT_TIMESTAMP - '2 years'::interval); INSERT INTO partman_test.time_taptest_table_123456789012345678901234567890123456780 (col1, col3) VALUES (generate_series(71,85), CURRENT_TIMESTAMP - '3 years'::interval); INSERT INTO partman_test.time_taptest_table_123456789012345678901234567890123456780 (col1, col3) VALUES (generate_series(86,100), CURRENT_TIMESTAMP - '4 years'::interval); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table_123456789012345678901234567890123456780', 'Check that parent table has had no data inserted to it'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY'), ARRAY[10], 'Check count from time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY'), ARRAY[5], 'Check count from time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY'), ARRAY[5], 'Check count from time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'4 years'::interval, 'YYYY'), ARRAY[7], 'Check count from time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'4 years'::interval, 'YYYY')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY'), ARRAY[10], 'Check count from time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY'), ARRAY[21], 'Check count from time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'3 years'::interval, 'YYYY'), ARRAY[15], 'Check count from time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'3 years'::interval, 'YYYY')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'4 years'::interval, 'YYYY'), ARRAY[15], 'Check count from time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'4 years'::interval, 'YYYY')); UPDATE part_config SET premake = 5, optimize_trigger = 5 WHERE parent_table = 'partman_test.time_taptest_table_123456789012345678901234567890123456780'; SELECT run_maintenance(); INSERT INTO partman_test.time_taptest_table_123456789012345678901234567890123456780 (col1, col3) VALUES (generate_series(101,122), CURRENT_TIMESTAMP + '5 years'::interval); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'5 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'5 years'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'6 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'6 years'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'7 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'7 years'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'8 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'8 years'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'9 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'9 years'::interval, 'YYYY')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'10 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'10 years'::interval, 'YYYY')||' does not exist'); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'5 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'5 years'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'6 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'6 years'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'7 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'7 years'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'8 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'8 years'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'9 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'9 years'::interval, 'YYYY')); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table_123456789012345678901234567890123456780', 'Check that parent table has had no data inserted to it'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'5 years'::interval, 'YYYY'), ARRAY[22], 'Check count from time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'5 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'5 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'5 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'6 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'6 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'7 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'7 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'8 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'8 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'9 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'9 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'5 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'5 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'6 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'6 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'7 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'7 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'8 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'8 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'9 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'9 years'::interval, 'YYYY')); GRANT DELETE ON partman_test.time_taptest_table_123456789012345678901234567890123456780 TO partman_basic; REVOKE ALL ON partman_test.time_taptest_table_123456789012345678901234567890123456780 FROM partman_revoke; ALTER TABLE partman_test.time_taptest_table_123456789012345678901234567890123456780 OWNER TO partman_owner; UPDATE part_config SET premake = 6, optimize_trigger = 6 WHERE parent_table = 'partman_test.time_taptest_table_123456789012345678901234567890123456780'; SELECT run_maintenance(); -- Data for dynamic trigger INSERT INTO partman_test.time_taptest_table_123456789012345678901234567890123456780 (col1, col3) VALUES (generate_series(123,150), CURRENT_TIMESTAMP + '11 years'::interval); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table_123456789012345678901234567890123456780', 'Check that parent table has had no data inserted to it'); -- Check dynamic trigger worked SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_123456789012345678901234567890123456780', ARRAY[148], 'Check count from parent table'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'11 years'::interval, 'YYYY'), ARRAY[28], 'Check count from time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'11 years'::interval, 'YYYY')); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'10 years'::interval, 'YYYY'), 'Check time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'10 years'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'11 years'::interval, 'YYYY'), 'Check time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'11 years'::interval, 'YYYY')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'12 years'::interval, 'YYYY'), 'Check time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'12 years'::interval, 'YYYY')||' does not exist'); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'10 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'10 years'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'11 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'11 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'10 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'10 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'11 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'11 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'10 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'10 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'11 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'11 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'10 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'10 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'11 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'11 years'::interval, 'YYYY')); INSERT INTO partman_test.time_taptest_table_123456789012345678901234567890123456780 (col1, col3) VALUES (generate_series(200,210), CURRENT_TIMESTAMP + '20 years'::interval); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table_123456789012345678901234567890123456780', ARRAY[11], 'Check that data outside trigger scope goes to parent'); SELECT reapply_privileges('partman_test.time_taptest_table_123456789012345678901234567890123456780'); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'3 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'3 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'4 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'4 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'4 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'4 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'5 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'5 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'6 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'6 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'7 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'7 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'8 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'8 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'9 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'9 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'10 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'10 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'11 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'11 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'3 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'3 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'4 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'4 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'4 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'4 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'5 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'5 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'6 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'6 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'7 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'7 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'8 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'8 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'9 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'9 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'10 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'10 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'11 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'11 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'3 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'3 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'4 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'4 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'4 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'4 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'5 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'5 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'6 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'6 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'7 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'7 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'8 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'8 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'9 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'9 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'10 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'10 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'11 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'11 years'::interval, 'YYYY')); SELECT drop_partition_time('partman_test.time_taptest_table_123456789012345678901234567890123456780', '3 years', p_keep_table := false); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'4 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'4 years'::interval, 'YYYY')||' does not exist'); UPDATE part_config SET retention = '2 years'::interval WHERE parent_table = 'partman_test.time_taptest_table_123456789012345678901234567890123456780'; SELECT drop_partition_time('partman_test.time_taptest_table_123456789012345678901234567890123456780', p_retention_schema := 'partman_retention_test'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'3 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'3 years'::interval, 'YYYY')||' does not exist'); /* This test may fail around the end of the year. If so, swap to the commented out one below */ SELECT has_table('partman_retention_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'3 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'3 years'::interval, 'YYYY')||' got moved to new schema. This test may fail around the year boundary. See comment in tests for different test to try.'); /* This test may fail around the beginning of the year. If so, swap to the commented out one above */ --SELECT has_table('partman_retention_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY'), -- 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')||' got moved to new schema. This test may fail around the year boundary. See comment in tests for different test to try.'); SELECT undo_partition_time('partman_test.time_taptest_table_123456789012345678901234567890123456780', 20, p_keep_table := false); /* This test may fail around the end of the year. If so, swap to the commented out one below */ SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table_123456789012345678901234567890123456780', ARRAY[129], 'Check count from parent table after undo. This test may fail around the year boundary. See comment in tests for different test to try.'); /* This test may fail around the beginning of the year. If so, swap to the commented out one above */ --SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table_123456789012345678901234567890123456780', ARRAY[108], 'Check count from parent table after undo. This test may fail around the year boundary. See comment in tests for different test to try.'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'4 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'4 years'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'5 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'5 years'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'6 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'6 years'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'7 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'7 years'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'8 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'8 years'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'9 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'9 years'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'10 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'10 years'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_12345678901234567890123456789012345678_p'||to_char(CURRENT_TIMESTAMP+'11 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'11 years'::interval, 'YYYY')||' does not exist'); SELECT * FROM finish(); ROLLBACK; pg_partman-2.2.2/test/test-time-yearly.sql000066400000000000000000001441701262146621700206010ustar00rootroot00000000000000-- ########## TIME YEARLY TESTS ########## -- Other tests: UNLOGGED, Multi-column foreign key \set ON_ERROR_ROLLBACK 1 \set ON_ERROR_STOP true BEGIN; SELECT set_config('search_path','partman, public',false); SELECT plan(179); CREATE SCHEMA partman_test; CREATE SCHEMA partman_retention_test; CREATE ROLE partman_basic; CREATE ROLE partman_revoke; CREATE ROLE partman_owner; CREATE TABLE partman_test.fk_test_reference (col2 text not null, col4 text not null); CREATE UNIQUE INDEX ON partman_test.fk_test_reference(col2, col4); INSERT INTO partman_test.fk_test_reference VALUES ('stuff', 'stuff'); CREATE UNLOGGED TABLE partman_test.time_taptest_table (col1 int primary key , col2 text not null default 'stuff' , col3 timestamptz NOT NULL DEFAULT now() , col4 text not null default 'stuff' , FOREIGN KEY (col2, col4) REFERENCES partman_test.fk_test_reference(col2, col4) MATCH FULL ON DELETE RESTRICT DEFERRABLE); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(1,10), CURRENT_TIMESTAMP); 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', 'time', 'yearly'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'4 years'::interval, 'YYYY')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'5 years'::interval, 'YYYY')||' does not exist'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'3 years'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'4 years'::interval, 'YYYY')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'5 years'::interval, 'YYYY')||' does not exist'); SELECT results_eq('SELECT relpersistence::text FROM pg_catalog.pg_class WHERE oid::regclass = ''partman_test.time_taptest_table''::regclass', ARRAY['u'], 'Check that parent table is unlogged'); SELECT results_eq('SELECT relpersistence::text FROM pg_catalog.pg_class WHERE oid::regclass = ''partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')||'''::regclass', ARRAY['u'], 'Check that partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')||' is unlogged'); SELECT results_eq('SELECT relpersistence::text FROM pg_catalog.pg_class WHERE oid::regclass = ''partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY')||'''::regclass', ARRAY['u'], 'Check that partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY')||' is unlogged'); SELECT results_eq('SELECT relpersistence::text FROM pg_catalog.pg_class WHERE oid::regclass = ''partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||'''::regclass', ARRAY['u'], 'Check that partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||' is unlogged'); SELECT results_eq('SELECT relpersistence::text FROM pg_catalog.pg_class WHERE oid::regclass = ''partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')||'''::regclass', ARRAY['u'], 'Check that partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')||' is unlogged'); SELECT results_eq('SELECT relpersistence::text FROM pg_catalog.pg_class WHERE oid::regclass = ''partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 years'::interval, 'YYYY')||'''::regclass', ARRAY['u'], 'Check that partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 years'::interval, 'YYYY')||' is unlogged'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 years'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 years'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 years'::interval, 'YYYY')); SELECT col_is_fk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY'), ARRAY['col2', 'col4'], 'Check for inherited foreign key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')); SELECT col_is_fk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY'), ARRAY['col2', 'col4'], 'Check for inherited foreign key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')); SELECT col_is_fk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY'), ARRAY['col2', 'col4'], 'Check for inherited foreign key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')); SELECT col_is_fk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 years'::interval, 'YYYY'), ARRAY['col2', 'col4'], 'Check for inherited foreign key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 years'::interval, 'YYYY')); SELECT col_is_fk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY'), ARRAY['col2', 'col4'], 'Check for inherited foreign key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')); SELECT col_is_fk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY'), ARRAY['col2', 'col4'], 'Check for inherited foreign key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')); SELECT col_is_fk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 years'::interval, 'YYYY'), ARRAY['col2', 'col4'], 'Check for inherited foreign key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 years'::interval, 'YYYY')); SELECT col_is_fk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 years'::interval, 'YYYY'), ARRAY['col2', 'col4'], 'Check for inherited foreign key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_'||to_char(CURRENT_TIMESTAMP, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 years'::interval, 'YYYY')); SELECT results_eq('SELECT partition_data_time(''partman_test.time_taptest_table'')::int', ARRAY[10], 'Check that partitioning function returns correct count of rows moved'); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table', 'Check that parent table has had data moved to partition'); 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, 'YYYY'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')); REVOKE INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER ON partman_test.time_taptest_table FROM partman_revoke; INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(11,20), CURRENT_TIMESTAMP + '1 year'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(21,25), CURRENT_TIMESTAMP + '2 years'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(26,30), CURRENT_TIMESTAMP + '3 years'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(31,37), CURRENT_TIMESTAMP + '4 years'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(40,49), CURRENT_TIMESTAMP - '1 years'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(50,70), CURRENT_TIMESTAMP - '2 years'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(71,85), CURRENT_TIMESTAMP - '3 years'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(86,100), CURRENT_TIMESTAMP - '4 years'::interval); 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+'1 year'::interval, 'YYYY'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY'), ARRAY[5], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY'), ARRAY[5], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 years'::interval, 'YYYY'), ARRAY[7], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 years'::interval, 'YYYY')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY'), ARRAY[21], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 years'::interval, 'YYYY'), ARRAY[15], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 years'::interval, 'YYYY')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 years'::interval, 'YYYY'), ARRAY[15], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 years'::interval, 'YYYY')); UPDATE part_config SET premake = 5, optimize_trigger = 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 + '5 years'::interval); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'5 years'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'6 years'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'7 years'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'8 years'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'9 years'::interval, 'YYYY')||' exists'); SELECT results_eq('SELECT relpersistence::text FROM pg_catalog.pg_class WHERE oid::regclass = ''partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 years'::interval, 'YYYY')||'''::regclass', ARRAY['u'], 'Check that partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 years'::interval, 'YYYY')||' is unlogged'); SELECT results_eq('SELECT relpersistence::text FROM pg_catalog.pg_class WHERE oid::regclass = ''partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 years'::interval, 'YYYY')||'''::regclass', ARRAY['u'], 'Check that partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 years'::interval, 'YYYY')||' is unlogged'); SELECT results_eq('SELECT relpersistence::text FROM pg_catalog.pg_class WHERE oid::regclass = ''partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 years'::interval, 'YYYY')||'''::regclass', ARRAY['u'], 'Check that partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 years'::interval, 'YYYY')||' is unlogged'); SELECT results_eq('SELECT relpersistence::text FROM pg_catalog.pg_class WHERE oid::regclass = ''partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 years'::interval, 'YYYY')||'''::regclass', ARRAY['u'], 'Check that partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 years'::interval, 'YYYY')||' is unlogged'); SELECT results_eq('SELECT relpersistence::text FROM pg_catalog.pg_class WHERE oid::regclass = ''partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 years'::interval, 'YYYY')||'''::regclass', ARRAY['u'], 'Check that partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 years'::interval, 'YYYY')||' is unlogged'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'10 years'::interval, 'YYYY')||' does not exist'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 years'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 years'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 years'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 years'::interval, 'YYYY')); SELECT col_is_fk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 years'::interval, 'YYYY'), ARRAY['col2', 'col4'], 'Check for inherited foreign key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 years'::interval, 'YYYY')); SELECT col_is_fk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 years'::interval, 'YYYY'), ARRAY['col2', 'col4'], 'Check for inherited foreign key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 years'::interval, 'YYYY')); SELECT col_is_fk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 years'::interval, 'YYYY'), ARRAY['col2', 'col4'], 'Check for inherited foreign key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 years'::interval, 'YYYY')); SELECT col_is_fk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 years'::interval, 'YYYY'), ARRAY['col2', 'col4'], 'Check for inherited foreign key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 years'::interval, 'YYYY')); SELECT col_is_fk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 years'::interval, 'YYYY'), ARRAY['col2', 'col4'], 'Check for inherited foreign key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 years'::interval, 'YYYY')); 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+'5 years'::interval, 'YYYY'), ARRAY[22], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 years'::interval, 'YYYY')); GRANT DELETE ON partman_test.time_taptest_table TO partman_basic; REVOKE ALL ON partman_test.time_taptest_table FROM partman_revoke; ALTER TABLE partman_test.time_taptest_table OWNER TO partman_owner; UPDATE part_config SET premake = 6, optimize_trigger = 6 WHERE parent_table = 'partman_test.time_taptest_table'; SELECT run_maintenance(); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(123,150), CURRENT_TIMESTAMP + '6 years'::interval); 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', ARRAY[148], 'Check count from parent table'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 years'::interval, 'YYYY'), ARRAY[28], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 years'::interval, 'YYYY')); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 years'::interval, 'YYYY'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 years'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 years'::interval, 'YYYY'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 years'::interval, 'YYYY')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 years'::interval, 'YYYY'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 years'::interval, 'YYYY')||' does not exist'); SELECT results_eq('SELECT relpersistence::text FROM pg_catalog.pg_class WHERE oid::regclass = ''partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 years'::interval, 'YYYY')||'''::regclass', ARRAY['u'], 'Check that partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 years'::interval, 'YYYY')||' is unlogged'); SELECT results_eq('SELECT relpersistence::text FROM pg_catalog.pg_class WHERE oid::regclass = ''partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 years'::interval, 'YYYY')||'''::regclass', ARRAY['u'], 'Check that partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 years'::interval, 'YYYY')||' is unlogged'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 years'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 years'::interval, 'YYYY')); SELECT col_is_fk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 years'::interval, 'YYYY'), ARRAY['col2', 'col4'], 'Check for inherited foreign key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 years'::interval, 'YYYY')); SELECT col_is_fk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 years'::interval, 'YYYY'), ARRAY['col2', 'col4'], 'Check for inherited foreign key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 years'::interval, 'YYYY')); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(200,210), CURRENT_TIMESTAMP + '20 years'::interval); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table', ARRAY[11], 'Check that data outside trigger scope goes to parent'); SELECT reapply_privileges('partman_test.time_taptest_table'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 years'::interval, 'YYYY')); SELECT drop_partition_time('partman_test.time_taptest_table', '3 years', p_keep_table := false); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'4 years'::interval, 'YYYY')||' does not exist'); UPDATE part_config SET retention = '2 years'::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 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'3 years'::interval, 'YYYY')||' does not exist'); -- **** This test may fail around the end of the year. If so, swap to the commented out one below SELECT has_table('partman_retention_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'3 years'::interval, 'YYYY')||' got moved to new schema. This test may fail around the year boundary. See comment in tests for different test to try.'); -- **** This test may fail around the beginning of the year. If so, swap to the commented out one above --SELECT has_table('partman_retention_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY'), -- 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')||' got moved to new schema. This test may fail around the year boundary. See comment in tests for different test to try.'); SELECT undo_partition_time('partman_test.time_taptest_table', 20, p_keep_table := false); -- **** This test may fail around the end of the year. If so, swap to the commented out one below SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table', ARRAY[129], 'Check count from parent table after undo. This test may fail around the year boundary. See comment in tests for different test to try.'); -- **** This test may fail around the beginning of the year. If so, swap to the commented out one above --SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table', ARRAY[108], 'Check count from parent table after undo. This test may fail around the year boundary. See comment in tests for different test to try.'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'1 year'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP-'2 years'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'1 year'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'2 years'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'3 years'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'4 years'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'5 years'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'6 years'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'7 years'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'8 years'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'9 years'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'10 years'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(CURRENT_TIMESTAMP+'11 years'::interval, 'YYYY')||' does not exist'); SELECT * FROM finish(); ROLLBACK; pg_partman-2.2.2/test/test_bgw/000077500000000000000000000000001262146621700164515ustar00rootroot00000000000000pg_partman-2.2.2/test/test_bgw/test-id-bgw.sql000066400000000000000000000511311262146621700213210ustar00rootroot00000000000000-- ########## ID TESTS WITH BACKGROUND WORKER RUNNING ########## -- Additional tests: turn off pg_jobmon logging, UNLOGGED, Make sure option to not inherit foreign keys works, larger than necessary p_batch_count to partition_data_id(), retention -- Set the pg_partman_bgw.interval setting in postgresql.conf to 10 seconds (or less) in order for this test suite to pass successfully. -- Cannot run this test inside a transaction since then the BGW would not see this partition set exists \set ON_ERROR_ROLLBACK 1 \set ON_ERROR_STOP true --BEGIN; SELECT set_config('search_path','partman, public',false); SELECT plan(124); CREATE SCHEMA partman_test; CREATE SCHEMA partman_retention_test; CREATE ROLE partman_basic; CREATE ROLE partman_revoke; CREATE ROLE partman_owner; CREATE TABLE partman_test.fk_test_reference (col2 text unique not null); INSERT INTO partman_test.fk_test_reference VALUES ('stuff'); CREATE UNLOGGED TABLE partman_test.id_taptest_table ( col1 int primary key , col2 text not null default 'stuff' references partman_test.fk_test_reference (col2) , col3 timestamptz DEFAULT now()); INSERT INTO partman_test.id_taptest_table (col1) VALUES (generate_series(1,9)); GRANT SELECT,INSERT,UPDATE ON partman_test.id_taptest_table TO partman_basic; GRANT ALL ON partman_test.id_taptest_table TO partman_revoke; SELECT results_eq('SELECT create_parent(''partman_test.id_taptest_table'', ''col1'', ''id'', ''10'', p_use_run_maintenance := true, p_inherit_fk := false, p_jobmon := false)::text', ARRAY['true'], 'Check that create_parent() returns true'); SELECT has_table('partman_test', 'id_taptest_table_p0', 'Check id_taptest_table_p0 exists'); SELECT has_table('partman_test', 'id_taptest_table_p10', 'Check id_taptest_table_p10 exists'); SELECT has_table('partman_test', 'id_taptest_table_p20', 'Check id_taptest_table_p20 exists'); SELECT has_table('partman_test', 'id_taptest_table_p30', 'Check id_taptest_table_p30 exists'); SELECT has_table('partman_test', 'id_taptest_table_p40', 'Check id_taptest_table_p40 exists'); SELECT hasnt_table('partman_test', 'id_taptest_table_p50', 'Check id_taptest_table_p50 doesn''t exists yet'); SELECT col_is_pk('partman_test', 'id_taptest_table_p0', ARRAY['col1'], 'Check for primary key in id_taptest_table_p0'); SELECT col_is_pk('partman_test', 'id_taptest_table_p10', ARRAY['col1'], 'Check for primary key in id_taptest_table_p10'); SELECT col_is_pk('partman_test', 'id_taptest_table_p20', ARRAY['col1'], 'Check for primary key in id_taptest_table_p20'); SELECT col_is_pk('partman_test', 'id_taptest_table_p30', ARRAY['col1'], 'Check for primary key in id_taptest_table_p30'); SELECT col_is_pk('partman_test', 'id_taptest_table_p40', ARRAY['col1'], 'Check for primary key in id_taptest_table_p40'); SELECT col_isnt_fk('partman_test', 'id_taptest_table_p0', 'col2', 'Check that foreign key was NOT inherited to id_taptest_table_p0'); SELECT col_isnt_fk('partman_test', 'id_taptest_table_p10', 'col2', 'Check that foreign key was NOT inherited to id_taptest_table_p10'); SELECT col_isnt_fk('partman_test', 'id_taptest_table_p20', 'col2', 'Check that foreign key was NOT inherited to id_taptest_table_p20'); SELECT col_isnt_fk('partman_test', 'id_taptest_table_p30', 'col2', 'Check that foreign key was NOT inherited to id_taptest_table_p30'); SELECT col_isnt_fk('partman_test', 'id_taptest_table_p40', 'col2', 'Check that foreign key was NOT inherited to id_taptest_table_p40'); SELECT table_privs_are('partman_test', 'id_taptest_table_p0', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p0'); SELECT table_privs_are('partman_test', 'id_taptest_table_p10', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p10'); SELECT table_privs_are('partman_test', 'id_taptest_table_p20', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p20'); SELECT table_privs_are('partman_test', 'id_taptest_table_p30', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p30'); SELECT table_privs_are('partman_test', 'id_taptest_table_p40', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p40'); SELECT table_privs_are('partman_test', 'id_taptest_table_p0', 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of id_taptest_table_p0'); SELECT table_privs_are('partman_test', 'id_taptest_table_p10', 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of id_taptest_table_p10'); SELECT table_privs_are('partman_test', 'id_taptest_table_p20', 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of id_taptest_table_p20'); SELECT table_privs_are('partman_test', 'id_taptest_table_p30', 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of id_taptest_table_p30'); SELECT table_privs_are('partman_test', 'id_taptest_table_p40', 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of id_taptest_table_p40'); SELECT results_eq('SELECT relpersistence::text FROM pg_catalog.pg_class WHERE oid::regclass = ''partman_test.id_taptest_table''::regclass', ARRAY['u'], 'Check that parent table is unlogged'); SELECT results_eq('SELECT relpersistence::text FROM pg_catalog.pg_class WHERE oid::regclass = ''partman_test.id_taptest_table_p0''::regclass', ARRAY['u'], 'Check that id_taptest_table_p0 is unlogged'); SELECT results_eq('SELECT relpersistence::text FROM pg_catalog.pg_class WHERE oid::regclass = ''partman_test.id_taptest_table_p10''::regclass', ARRAY['u'], 'Check that id_taptest_table_p10 is unlogged'); SELECT results_eq('SELECT relpersistence::text FROM pg_catalog.pg_class WHERE oid::regclass = ''partman_test.id_taptest_table_p20''::regclass', ARRAY['u'], 'Check that id_taptest_table_p20 is unlogged'); SELECT results_eq('SELECT relpersistence::text FROM pg_catalog.pg_class WHERE oid::regclass = ''partman_test.id_taptest_table_p30''::regclass', ARRAY['u'], 'Check that id_taptest_table_p30 is unlogged'); SELECT results_eq('SELECT relpersistence::text FROM pg_catalog.pg_class WHERE oid::regclass = ''partman_test.id_taptest_table_p40''::regclass', ARRAY['u'], 'Check that id_taptest_table_p40 is unlogged'); SELECT results_eq('SELECT partition_data_id(''partman_test.id_taptest_table'', p_batch_count := 5)::int', ARRAY[9], 'Check that partitioning function returns correct count of rows moved'); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table', 'Check that parent table has had data moved to partition'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table', ARRAY[9], 'Check count from parent table'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p0', ARRAY[9], 'Check count from id_taptest_table_p0'); REVOKE INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER ON partman_test.id_taptest_table FROM partman_revoke; INSERT INTO partman_test.id_taptest_table (col1) VALUES (generate_series(10,25)); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table', 'Check that parent table has had no data inserted to it'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p10', ARRAY[10], 'Check count from id_taptest_table_p10'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20', ARRAY[6], 'Check count from id_taptest_table_p20'); -- Make sure automatic 50% serial partition creation is not working SELECT hasnt_table('partman_test', 'id_taptest_table_p50', 'Check id_taptest_table_p50 does not exist yet (before bgw run)'); SELECT hasnt_table('partman_test', 'id_taptest_table_p60', 'Check id_taptest_table_p60 does not exist yet (before bgw run)'); SELECT pass('Waiting 20 seconds for background worker to run...'); SELECT pg_sleep(20); SELECT has_table('partman_test', 'id_taptest_table_p50', 'Check id_taptest_table_p50 exists'); SELECT results_eq('SELECT relpersistence::text FROM pg_catalog.pg_class WHERE oid::regclass = ''partman_test.id_taptest_table_p50''::regclass', ARRAY['u'], 'Check that id_taptest_table_p50 is unlogged'); SELECT has_table('partman_test', 'id_taptest_table_p60', 'Check id_taptest_table_p60 exists'); SELECT results_eq('SELECT relpersistence::text FROM pg_catalog.pg_class WHERE oid::regclass = ''partman_test.id_taptest_table_p60''::regclass', ARRAY['u'], 'Check that id_taptest_table_p60 is unlogged'); SELECT hasnt_table('partman_test', 'id_taptest_table_p70', 'Check id_taptest_table_p70 doesn''t exists yet'); SELECT col_is_pk('partman_test', 'id_taptest_table_p50', ARRAY['col1'], 'Check for primary key in id_taptest_table_p50'); SELECT col_isnt_fk('partman_test', 'id_taptest_table_p50', 'col2', 'Check that foreign key was NOT inherited to id_taptest_table_p50'); SELECT col_is_pk('partman_test', 'id_taptest_table_p60', ARRAY['col1'], 'Check for primary key in id_taptest_table_p60'); SELECT col_isnt_fk('partman_test', 'id_taptest_table_p60', 'col2', 'Check that foreign key was NOT inherited to id_taptest_table_p60'); SELECT table_privs_are('partman_test', 'id_taptest_table_p0', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p0'); SELECT table_privs_are('partman_test', 'id_taptest_table_p10', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p10'); SELECT table_privs_are('partman_test', 'id_taptest_table_p20', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p20'); SELECT table_privs_are('partman_test', 'id_taptest_table_p30', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p30'); SELECT table_privs_are('partman_test', 'id_taptest_table_p40', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p40'); SELECT table_privs_are('partman_test', 'id_taptest_table_p50', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p50'); SELECT table_privs_are('partman_test', 'id_taptest_table_p50', 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of id_taptest_table_p50'); SELECT table_privs_are('partman_test', 'id_taptest_table_p60', 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of id_taptest_table_p60'); GRANT DELETE ON partman_test.id_taptest_table TO partman_basic; REVOKE ALL ON partman_test.id_taptest_table FROM partman_revoke; ALTER TABLE partman_test.id_taptest_table OWNER TO partman_owner; INSERT INTO partman_test.id_taptest_table (col1) VALUES (generate_series(26,38)); SELECT is_empty('SELECT * FROM ONLY partman_test.id_taptest_table', 'Check that parent table has had no data inserted to it'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table', ARRAY[38], 'Check count from id_taptest_table'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p20', ARRAY[10], 'Check count from id_taptest_table_p20'); SELECT results_eq('SELECT count(*)::int FROM partman_test.id_taptest_table_p30', ARRAY[9], 'Check count from id_taptest_table_p30'); SELECT pass('Waiting 20 seconds for background worker to run...'); SELECT pg_sleep(20); SELECT has_table('partman_test', 'id_taptest_table_p70', 'Check id_taptest_table_p70 exists'); SELECT results_eq('SELECT relpersistence::text FROM pg_catalog.pg_class WHERE oid::regclass = ''partman_test.id_taptest_table_p70''::regclass', ARRAY['u'], 'Check that id_taptest_table_p70 is unlogged'); SELECT hasnt_table('partman_test', 'id_taptest_table_p80', 'Check id_taptest_table_p90 doesn''t exists yet'); SELECT col_is_pk('partman_test', 'id_taptest_table_p70', ARRAY['col1'], 'Check for primary key in id_taptest_table_p70'); SELECT col_isnt_fk('partman_test', 'id_taptest_table_p70', 'col2', 'Check that foreign key was NOT inherited to id_taptest_table_p70'); SELECT table_privs_are('partman_test', 'id_taptest_table_p0', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p0'); SELECT table_privs_are('partman_test', 'id_taptest_table_p10', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p10'); SELECT table_privs_are('partman_test', 'id_taptest_table_p20', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p20'); SELECT table_privs_are('partman_test', 'id_taptest_table_p30', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p30'); SELECT table_privs_are('partman_test', 'id_taptest_table_p40', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p40'); SELECT table_privs_are('partman_test', 'id_taptest_table_p50', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of id_taptest_table_p50'); SELECT table_privs_are('partman_test', 'id_taptest_table_p60', 'partman_basic', ARRAY['SELECT', 'INSERT', 'UPDATE'], 'Check partman_basic privileges of id_taptest_table_p60'); SELECT table_privs_are('partman_test', 'id_taptest_table_p50', 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of id_taptest_table_p50'); SELECT table_privs_are('partman_test', 'id_taptest_table_p60', 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of id_taptest_table_p60'); SELECT table_privs_are('partman_test', 'id_taptest_table_p70', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE','DELETE'], 'Check partman_basic privileges of id_taptest_table_p70'); SELECT table_owner_is('partman_test', 'id_taptest_table_p70', 'partman_owner', 'Check that ownership change worked for id_taptest_table_p70'); SELECT table_privs_are('partman_test', 'id_taptest_table_p70', 'partman_revoke', '{}'::text[], 'Check partman_revoke has no privileges on id_taptest_table_p70'); INSERT INTO partman_test.id_taptest_table (col1) VALUES (generate_series(200,210)); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table', ARRAY[11], 'Check that data outside trigger scope goes to parent'); SELECT pass('Waiting 20 seconds for background worker to run...'); SELECT pg_sleep(20); SELECT reapply_privileges('partman_test.id_taptest_table'); SELECT table_privs_are('partman_test', 'id_taptest_table_p0', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE','DELETE'], 'Check partman_basic privileges of id_taptest_table_p0'); SELECT table_privs_are('partman_test', 'id_taptest_table_p10', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE','DELETE'], 'Check partman_basic privileges of id_taptest_table_p10'); SELECT table_privs_are('partman_test', 'id_taptest_table_p20', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE','DELETE'], 'Check partman_basic privileges of id_taptest_table_p20'); SELECT table_privs_are('partman_test', 'id_taptest_table_p30', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE','DELETE'], 'Check partman_basic privileges of id_taptest_table_p30'); SELECT table_privs_are('partman_test', 'id_taptest_table_p40', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE','DELETE'], 'Check partman_basic privileges of id_taptest_table_p40'); SELECT table_privs_are('partman_test', 'id_taptest_table_p50', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE','DELETE'], 'Check partman_basic privileges of id_taptest_table_p50'); SELECT table_privs_are('partman_test', 'id_taptest_table_p60', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE','DELETE'], 'Check partman_basic privileges of id_taptest_table_p60'); SELECT table_privs_are('partman_test', 'id_taptest_table_p70', 'partman_basic', ARRAY['SELECT','INSERT','UPDATE','DELETE'], 'Check partman_basic privileges of id_taptest_table_p70'); SELECT table_privs_are('partman_test', 'id_taptest_table_p0', 'partman_revoke', '{}'::text[], 'Check partman_revoke has no privileges on id_taptest_table_p0'); SELECT table_privs_are('partman_test', 'id_taptest_table_p10', 'partman_revoke', '{}'::text[], 'Check partman_revoke has no privileges on id_taptest_table_p10'); SELECT table_privs_are('partman_test', 'id_taptest_table_p20', 'partman_revoke', '{}'::text[], 'Check partman_revoke has no privileges on id_taptest_table_p20'); SELECT table_privs_are('partman_test', 'id_taptest_table_p30', 'partman_revoke', '{}'::text[], 'Check partman_revoke has no privileges on id_taptest_table_p30'); SELECT table_privs_are('partman_test', 'id_taptest_table_p40', 'partman_revoke', '{}'::text[], 'Check partman_revoke has no privileges on id_taptest_table_p40'); SELECT table_privs_are('partman_test', 'id_taptest_table_p50', 'partman_revoke', '{}'::text[], 'Check partman_revoke has no privileges on id_taptest_table_p50'); SELECT table_privs_are('partman_test', 'id_taptest_table_p60', 'partman_revoke', '{}'::text[], 'Check partman_revoke has no privileges on id_taptest_table_p60'); SELECT table_privs_are('partman_test', 'id_taptest_table_p70', 'partman_revoke', '{}'::text[], 'Check partman_revoke has no privileges on id_taptest_table_p70'); SELECT table_owner_is ('partman_test', 'id_taptest_table_p0', 'partman_owner', 'Check that ownership change worked for id_taptest_table_p0'); SELECT table_owner_is ('partman_test', 'id_taptest_table_p10', 'partman_owner', 'Check that ownership change worked for id_taptest_table_p10'); SELECT table_owner_is ('partman_test', 'id_taptest_table_p20', 'partman_owner', 'Check that ownership change worked for id_taptest_table_p20'); SELECT table_owner_is ('partman_test', 'id_taptest_table_p30', 'partman_owner', 'Check that ownership change worked for id_taptest_table_p30'); SELECT table_owner_is ('partman_test', 'id_taptest_table_p40', 'partman_owner', 'Check that ownership change worked for id_taptest_table_p40'); SELECT table_owner_is ('partman_test', 'id_taptest_table_p50', 'partman_owner', 'Check that ownership change worked for id_taptest_table_p50'); SELECT table_owner_is ('partman_test', 'id_taptest_table_p60', 'partman_owner', 'Check that ownership change worked for id_taptest_table_p60'); SELECT table_owner_is ('partman_test', 'id_taptest_table_p70', 'partman_owner', 'Check that ownership change worked for id_taptest_table_p70'); -- Max value is 38 above SELECT drop_partition_id('partman_test.id_taptest_table', '20', p_keep_table := false); SELECT hasnt_table('partman_test', 'id_taptest_table_p0', 'Check id_taptest_table_p0 doesn''t exists anymore'); UPDATE part_config SET retention = '10', retention_schema = 'partman_retention_test' WHERE parent_table = 'partman_test.id_taptest_table'; SELECT pass('Waiting 20 seconds for background worker to run...'); SELECT pg_sleep(20); SELECT hasnt_table('partman_test', 'id_taptest_table_p10', 'Check id_taptest_table_p10 doesn''t exists anymore'); SELECT has_table('partman_retention_test', 'id_taptest_table_p10', 'Check id_taptest_table_p10 got moved to new schema'); -- Has to run twice because second time around is when it sees the partition is empty & drops it SELECT undo_partition_id('partman_test.id_taptest_table', 2, p_keep_table := false); SELECT hasnt_table('partman_test', 'id_taptest_table_p20', 'Check id_taptest_table_p20 does not exist'); SELECT undo_partition_id('partman_test.id_taptest_table', 10, p_keep_table := false); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.id_taptest_table', ARRAY[30], 'Check count from parent table after undo'); SELECT hasnt_table('partman_test', 'id_taptest_table_p30', 'Check id_taptest_table_p30 does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p40', 'Check id_taptest_table_p40 does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p50', 'Check id_taptest_table_p50 does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p60', 'Check id_taptest_table_p60 does not exist'); SELECT hasnt_table('partman_test', 'id_taptest_table_p70', 'Check id_taptest_table_p70 does not exist'); DROP SCHEMA IF EXISTS partman_test CASCADE; DROP SCHEMA IF EXISTS partman_retention_test CASCADE; DROP ROLE IF EXISTS partman_basic; DROP ROLE IF EXISTS partman_revoke; DROP ROLE IF EXISTS partman_owner; SELECT hasnt_schema('partman_test', 'Ensure partman_test schema has been dropped'); SELECT hasnt_schema('partman_retention_test', 'Ensure partman_retention_test schema has been dropped'); SELECT hasnt_role('partman_basic', 'Ensure partman_basic role has been dropped'); SELECT hasnt_role('partman_revoke', 'Ensure partman_revoke role has been dropped'); SELECT hasnt_role('partman_owner', 'Ensure partman_owner role has been dropped'); SELECT * FROM finish(); --ROLLBACK; pg_partman-2.2.2/test/test_bgw/test-time-daily-bgw.sql000066400000000000000000000707401262146621700227720ustar00rootroot00000000000000-- ########## TIME TESTS WITH BACKGROUND WORKER RUNNING ########## -- Other tests: larger than necessary p_batch_count to partition_data_time(), create_parent() returns true, with OIDs, retention to new schema, retention keep indexes -- Set the pg_partman_bgw.interval setting in postgresql.conf to 10 seconds (or less) in order for this test suite to pass successfully. -- Cannot run this test inside a transaction since then the BGW would not see this partition set exists \set ON_ERROR_ROLLBACK 1 \set ON_ERROR_STOP true --BEGIN; SELECT set_config('search_path','partman, public',false); SELECT plan(108); CREATE SCHEMA partman_test; CREATE SCHEMA partman_retention_test; CREATE ROLE partman_basic; CREATE ROLE partman_revoke; CREATE ROLE partman_owner; CREATE TABLE partman_test.time_taptest_table (col1 int primary key, col2 text, col3 timestamptz NOT NULL DEFAULT now()) WITH OIDS; INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(1,10), CURRENT_TIMESTAMP); 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', 'time', 'daily'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT partition_data_time(''partman_test.time_taptest_table'', p_batch_count := 5)::int', ARRAY[10], 'Check that partitioning function returns correct count of rows moved'); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table', 'Check that parent table has had data moved to partition'); 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, 'YYYY_MM_DD'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); REVOKE INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER ON partman_test.time_taptest_table FROM partman_revoke; INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(11,20), CURRENT_TIMESTAMP + '1 day'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(21,25), CURRENT_TIMESTAMP + '2 days'::interval); 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', ARRAY[25], 'Check count from time_taptest_table'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), ARRAY[5], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')); UPDATE part_config SET premake = 5, optimize_trigger = 5 WHERE parent_table = 'partman_test.time_taptest_table'; SELECT pass('Waiting 20 seconds for background worker to run...'); SELECT pg_sleep(20); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')); GRANT DELETE ON partman_test.time_taptest_table TO partman_basic; REVOKE ALL ON partman_test.time_taptest_table FROM partman_revoke; ALTER TABLE partman_test.time_taptest_table OWNER TO partman_owner; UPDATE part_config SET premake = 6, optimize_trigger = 6 WHERE parent_table = 'partman_test.time_taptest_table'; SELECT pass('Waiting 20 seconds for background worker to run...'); SELECT pg_sleep(20); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(200,210), CURRENT_TIMESTAMP + '20 days'::interval); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table', ARRAY[11], 'Check that data outside trigger scope goes to parent'); SELECT reapply_privileges('partman_test.time_taptest_table'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')); SELECT drop_partition_time('partman_test.time_taptest_table', '3 days', p_keep_table := false); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||' does not exist'); UPDATE part_config SET retention = '2 days'::interval, retention_schema = 'partman_retention_test' WHERE parent_table = 'partman_test.time_taptest_table'; SELECT pass('Waiting 20 seconds for background worker to run...'); SELECT pg_sleep(20); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT has_table('partman_retention_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||' got moved to new schema'); SELECT undo_partition_time('partman_test.time_taptest_table', 20); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||' is empty'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||' is empty'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')||' is empty'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')||' still exists'); SELECT is_empty('SELECT * FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')||' is empty'); DROP SCHEMA IF EXISTS partman_test CASCADE; DROP SCHEMA IF EXISTS partman_retention_test CASCADE; DROP ROLE IF EXISTS partman_basic; DROP ROLE IF EXISTS partman_revoke; DROP ROLE IF EXISTS partman_owner; SELECT hasnt_schema('partman_test', 'Ensure partman_test schema has been dropped'); SELECT hasnt_schema('partman_retention_test', 'Ensure partman_retention_test schema has been dropped'); SELECT hasnt_role('partman_basic', 'Ensure partman_basic role has been dropped'); SELECT hasnt_role('partman_revoke', 'Ensure partman_revoke role has been dropped'); SELECT hasnt_role('partman_owner', 'Ensure partman_owner role has been dropped'); SELECT * FROM finish(); --ROLLBACK; pg_partman-2.2.2/test/test_custom_time/000077500000000000000000000000001262146621700202225ustar00rootroot00000000000000pg_partman-2.2.2/test/test_custom_time/test-time-custom-100years.sql000066400000000000000000001376561262146621700255520ustar00rootroot00000000000000-- ########## TIME CUSTOM TESTS ########## \set ON_ERROR_ROLLBACK 1 \set ON_ERROR_STOP true BEGIN; SELECT set_config('search_path','partman, public',false); SELECT plan(152); CREATE SCHEMA partman_test; CREATE SCHEMA partman_retention_test; CREATE ROLE partman_basic; CREATE ROLE partman_revoke; CREATE ROLE partman_owner; CREATE TABLE partman_test.time_taptest_table (col1 int primary key, col2 text, col3 timestamptz NOT NULL DEFAULT now()); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(1,10), CURRENT_TIMESTAMP); 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', 'time-custom', '100 years'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP), 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP), 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'100 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'100 years'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'200 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'200 years'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'300 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'300 years'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'400 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'400 years'::interval, 'YYYY')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'500 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'500 years'::interval, 'YYYY')||' does not exist'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'100 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'100 years'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'200 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'200 years'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'300 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'300 years'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'400 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'400 years'::interval, 'YYYY')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'500 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'500 years'::interval, 'YYYY')||' does not exist'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP), 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP), 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'100 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'100 years'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'200 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'200 years'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'300 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'300 years'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'400 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'400 years'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP), 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP), 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'100 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'100 years'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'200 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'200 years'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'300 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'300 years'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'400 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'400 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP), 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP), 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'100 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'100 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'200 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'200 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'300 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'300 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'400 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'400 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'100 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'100 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'200 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'200 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'300 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'300 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'400 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'400 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP), 'YYYY'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP), 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'100 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'100 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'200 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'200 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'300 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'300 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'400 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'400 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'100 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'100 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'200 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'200 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'300 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'300 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'400 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'400 years'::interval, 'YYYY')); SELECT partition_data_time('partman_test.time_taptest_table'); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table', 'Check that parent table has had data moved to partition'); 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('century', CURRENT_TIMESTAMP), 'YYYY'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP), 'YYYY')); REVOKE INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER ON partman_test.time_taptest_table FROM partman_revoke; INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(11,20), CURRENT_TIMESTAMP + '100 years'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(21,25), CURRENT_TIMESTAMP + '200 years'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(26,30), CURRENT_TIMESTAMP + '300 years'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(31,37), CURRENT_TIMESTAMP + '400 years'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(40,49), CURRENT_TIMESTAMP - '100 years'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(50,70), CURRENT_TIMESTAMP - '200 years'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(71,85), CURRENT_TIMESTAMP - '300 years'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(86,100), CURRENT_TIMESTAMP - '400 years'::interval); 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(date_trunc('century', CURRENT_TIMESTAMP)+'100 years'::interval, 'YYYY'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'100 years'::interval, 'YYYY')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'200 years'::interval, 'YYYY'), ARRAY[5], 'Check count from time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'200 years'::interval, 'YYYY')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'300 years'::interval, 'YYYY'), ARRAY[5], 'Check count from time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'300 years'::interval, 'YYYY')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'400 years'::interval, 'YYYY'), ARRAY[7], 'Check count from time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'400 years'::interval, 'YYYY')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'100 years'::interval, 'YYYY'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'100 years'::interval, 'YYYY')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'200 years'::interval, 'YYYY'), ARRAY[21], 'Check count from time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'200 years'::interval, 'YYYY')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'300 years'::interval, 'YYYY'), ARRAY[15], 'Check count from time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'300 years'::interval, 'YYYY')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'400 years'::interval, 'YYYY'), ARRAY[15], 'Check count from time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'400 years'::interval, 'YYYY')); UPDATE part_config SET premake = 5, optimize_trigger = 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 + '500 years'::interval); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'500 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'500 years'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'600 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'600 years'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'700 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'700 years'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'800 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'800 years'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'900 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'900 years'::interval, 'YYYY')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1000 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1000 years'::interval, 'YYYY')||' does not exist'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'500 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'500 years'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'600 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'600 years'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'700 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'700 years'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'800 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'800 years'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'900 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'900 years'::interval, 'YYYY')); 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(date_trunc('century', CURRENT_TIMESTAMP)+'500 years'::interval, 'YYYY'), ARRAY[22], 'Check count from time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'500 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'500 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'500 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'600 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'600 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'700 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'700 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'800 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'800 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'900 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'900 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'500 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'500 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'600 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'600 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'700 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'700 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'800 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'800 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'900 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'900 years'::interval, 'YYYY')); GRANT DELETE ON partman_test.time_taptest_table TO partman_basic; REVOKE ALL ON partman_test.time_taptest_table FROM partman_revoke; ALTER TABLE partman_test.time_taptest_table OWNER TO partman_owner; UPDATE part_config SET premake = 6, optimize_trigger = 6 WHERE parent_table = 'partman_test.time_taptest_table'; SELECT run_maintenance(); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(123,150), CURRENT_TIMESTAMP + '600 years'::interval); 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', ARRAY[148], 'Check count from parent table'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'600 years'::interval, 'YYYY'), ARRAY[28], 'Check count from time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'600 years'::interval, 'YYYY')); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1000 years'::interval, 'YYYY'), 'Check time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1000 years'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1100 years'::interval, 'YYYY'), 'Check time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1100 years'::interval, 'YYYY')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1200 years'::interval, 'YYYY'), 'Check time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1200 years'::interval, 'YYYY')||' does not exist'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1000 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1000 years'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1100 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1100 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1000 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE','DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1000 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1100 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE','DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1100 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1000 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1000 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1100 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1100 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1000 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1000 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1100 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1000 years'::interval, 'YYYY')); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(200,210), CURRENT_TIMESTAMP + '2000 years'::interval); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table', ARRAY[11], 'Check that data outside trigger scope goes to parent'); SELECT reapply_privileges('partman_test.time_taptest_table'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP), 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP), 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'100 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'100 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'200 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'200 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'300 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'300 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'400 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'400 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'100 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'100 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'200 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'200 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'300 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'300 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'400 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'400 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'500 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'500 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'600 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'600 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'700 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'700 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'800 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'800 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'900 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'900 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1000 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1000 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1100 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1100 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP), 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP), 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'100 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'100 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'200 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'200 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'300 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'300 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'400 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'400 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'500 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'500 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'600 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'600 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'700 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'700 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'800 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'800 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'900 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'900 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1000 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1000 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1100 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1100 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP), 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP), 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'100 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'100 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'200 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'200 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'300 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'300 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'400 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'400 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'100 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'100 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'200 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'200 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'300 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'300 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'400 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'400 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'500 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'500 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'600 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'600 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'700 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'700 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'800 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'800 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'900 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'900 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1000 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1000 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1100 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1100 years'::interval, 'YYYY')); SELECT drop_partition_time('partman_test.time_taptest_table', '300 years', p_keep_table := false); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'400 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'400 years'::interval, 'YYYY')||' does not exist'); UPDATE part_config SET retention = '200 years'::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('century', CURRENT_TIMESTAMP)-'300 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'300 years'::interval, 'YYYY')||' does not exist'); SELECT has_table('partman_retention_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'300 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'300 years'::interval, 'YYYY')||' got moved to new schema'); SELECT undo_partition_time('partman_test.time_taptest_table', 20, p_keep_table := false); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table', ARRAY[129], 'Check count from parent table after undo'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP), 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP), 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'100 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'100 years'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'200 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'200 years'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'100 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'100 years'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'200 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'200 years'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'300 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'300 years'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'400 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'400 years'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'500 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'500 years'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'600 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'600 years'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'700 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'700 years'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'800 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'800 years'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'900 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'900 years'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1000 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1000 years'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1100 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1100 years'::interval, 'YYYY')||' does not exist'); SELECT is_empty('SELECT * FROM custom_time_partitions WHERE parent_table = ''partman_test.time_taptest_table''', 'Check that custom_time_partitions table is empty'); SELECT * FROM finish(); ROLLBACK; pg_partman-2.2.2/test/test_custom_time/test-time-custom-30sec.sql000066400000000000000000002327631262146621700251160ustar00rootroot00000000000000-- ########## TIME CUSTOM TESTS ########## -- 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" \set ON_ERROR_ROLLBACK 1 \set ON_ERROR_STOP true BEGIN; SELECT set_config('search_path','partman, public',false); SELECT plan(150); CREATE SCHEMA partman_test; CREATE SCHEMA partman_retention_test; CREATE ROLE partman_basic; CREATE ROLE partman_revoke; CREATE ROLE partman_owner; CREATE TABLE partman_test.time_taptest_table (col1 int primary key, col2 text, col3 timestamptz NOT NULL DEFAULT now()); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(1,10), CURRENT_TIMESTAMP); 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', 'time-custom', '30 seconds'); -- Must run_maintenance because when interval time is between 1 hour and 1 minute, the first partition name done by above is always the nearest hour rounded down SELECT run_maintenance(); 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), 'YYYY_MM_DD_HH24MISS'), 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP), 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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), 'YYYY_MM_DD_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), 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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), 'YYYY_MM_DD_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), 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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), 'YYYY_MM_DD_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), 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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), 'YYYY_MM_DD_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), 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_HH24MISS')); SELECT partition_data_time('partman_test.time_taptest_table'); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table', 'Check that parent table has had data moved to partition'); 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), 'YYYY_MM_DD_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), 'YYYY_MM_DD_HH24MISS')); REVOKE INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER ON partman_test.time_taptest_table FROM partman_revoke; INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(11,20), CURRENT_TIMESTAMP + '30 secs'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(21,25), CURRENT_TIMESTAMP + '60 secs'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(26,30), CURRENT_TIMESTAMP + '90 secs'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(31,37), CURRENT_TIMESTAMP + '120 secs'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(40,49), CURRENT_TIMESTAMP - '30 secs'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(50,70), CURRENT_TIMESTAMP - '60 secs'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(71,85), CURRENT_TIMESTAMP - '90 secs'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(86,100), CURRENT_TIMESTAMP - '120 secs'::interval); 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(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'30 secs'::interval, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_HH24MISS')); UPDATE part_config SET premake = 5, optimize_trigger = 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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_HH24MISS')); 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(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0)+'150 secs'::interval, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_HH24MISS')); GRANT DELETE ON partman_test.time_taptest_table TO partman_basic; REVOKE ALL ON partman_test.time_taptest_table FROM partman_revoke; ALTER TABLE partman_test.time_taptest_table OWNER TO partman_owner; UPDATE part_config SET premake = 6, optimize_trigger = 6 WHERE parent_table = 'partman_test.time_taptest_table'; SELECT run_maintenance(); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(123,150), CURRENT_TIMESTAMP + '180 secs'::interval); 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', 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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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', ARRAY[11], 'Check that data outside trigger scope goes to parent'); 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), 'YYYY_MM_DD_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), 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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), 'YYYY_MM_DD_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), 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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), 'YYYY_MM_DD_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), 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_HH24MISS')||' got moved to new schema'); SELECT undo_partition_time('partman_test.time_taptest_table', 20, p_keep_table := false); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table', ARRAY[129], 'Check count from parent 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), 'YYYY_MM_DD_HH24MISS'), 'Check time_taptest_table_'||to_char(date_trunc('minute', CURRENT_TIMESTAMP) + '30sec'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0), 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_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, 'YYYY_MM_DD_HH24MISS')||' does not exist'); SELECT * FROM finish(); ROLLBACK; pg_partman-2.2.2/test/test_custom_time/test-time-custom-epoch-100years.sql000066400000000000000000001426701262146621700266360ustar00rootroot00000000000000-- ########## TIME CUSTOM TESTS ########## \set ON_ERROR_ROLLBACK 1 \set ON_ERROR_STOP true BEGIN; SELECT set_config('search_path','partman, public',false); SELECT plan(156); CREATE SCHEMA partman_test; CREATE SCHEMA partman_retention_test; CREATE ROLE partman_basic; CREATE ROLE partman_revoke; CREATE ROLE partman_owner; CREATE TABLE partman_test.time_taptest_table (col1 int primary key, col2 text, col3 bigint NOT NULL DEFAULT extract('epoch' from now())); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(1,10), extract('epoch' from CURRENT_TIMESTAMP)); 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', 'time-custom', '100 years', p_epoch := true); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP), 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP), 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'100 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'100 years'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'200 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'200 years'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'300 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'300 years'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'400 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'400 years'::interval, 'YYYY')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'500 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'500 years'::interval, 'YYYY')||' does not exist'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'100 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'100 years'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'200 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'200 years'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'300 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'300 years'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'400 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'400 years'::interval, 'YYYY')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'500 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'500 years'::interval, 'YYYY')||' does not exist'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP), 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP), 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'100 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'100 years'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'200 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'200 years'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'300 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'300 years'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'400 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'400 years'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP), 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP), 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'100 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'100 years'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'200 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'200 years'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'300 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'300 years'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'400 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'400 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP), 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP), 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'100 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'100 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'200 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'200 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'300 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'300 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'400 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'400 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'100 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'100 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'200 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'200 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'300 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'300 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'400 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'400 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP), 'YYYY'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP), 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'100 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'100 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'200 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'200 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'300 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'300 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'400 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'400 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'100 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'100 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'200 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'200 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'300 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'300 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'400 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'400 years'::interval, 'YYYY')); SELECT partition_data_time('partman_test.time_taptest_table'); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table', 'Check that parent table has had data moved to partition'); 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('century', CURRENT_TIMESTAMP), 'YYYY'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP), 'YYYY')); REVOKE INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER ON partman_test.time_taptest_table FROM partman_revoke; INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(11,20), extract('epoch' from CURRENT_TIMESTAMP + '100 years'::interval)); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(21,25), extract('epoch' from CURRENT_TIMESTAMP + '200 years'::interval)); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(26,30), extract('epoch' from CURRENT_TIMESTAMP + '300 years'::interval)); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(31,37), extract('epoch' from CURRENT_TIMESTAMP + '400 years'::interval)); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(40,49), extract('epoch' from CURRENT_TIMESTAMP - '100 years'::interval)); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(50,70), extract('epoch' from CURRENT_TIMESTAMP - '200 years'::interval)); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(71,85), extract('epoch' from CURRENT_TIMESTAMP - '300 years'::interval)); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(86,100), extract('epoch' from CURRENT_TIMESTAMP - '400 years'::interval)); 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(date_trunc('century', CURRENT_TIMESTAMP)+'100 years'::interval, 'YYYY'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'100 years'::interval, 'YYYY')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'200 years'::interval, 'YYYY'), ARRAY[5], 'Check count from time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'200 years'::interval, 'YYYY')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'300 years'::interval, 'YYYY'), ARRAY[5], 'Check count from time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'300 years'::interval, 'YYYY')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'400 years'::interval, 'YYYY'), ARRAY[7], 'Check count from time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'400 years'::interval, 'YYYY')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'100 years'::interval, 'YYYY'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'100 years'::interval, 'YYYY')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'200 years'::interval, 'YYYY'), ARRAY[21], 'Check count from time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'200 years'::interval, 'YYYY')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'300 years'::interval, 'YYYY'), ARRAY[15], 'Check count from time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'300 years'::interval, 'YYYY')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'400 years'::interval, 'YYYY'), ARRAY[15], 'Check count from time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'400 years'::interval, 'YYYY')); 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), extract('epoch' from CURRENT_TIMESTAMP + '500 years'::interval)); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'500 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'500 years'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'600 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'600 years'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'700 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'700 years'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'800 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'800 years'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'900 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'900 years'::interval, 'YYYY')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1000 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1000 years'::interval, 'YYYY')||' exists'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'500 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'500 years'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'600 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'600 years'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'700 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'700 years'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'800 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'800 years'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'900 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'900 years'::interval, 'YYYY')); 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(date_trunc('century', CURRENT_TIMESTAMP)+'500 years'::interval, 'YYYY'), ARRAY[22], 'Check count from time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'500 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'500 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'500 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'600 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'600 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'700 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'700 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'800 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'800 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'900 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'900 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'500 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'500 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'600 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'600 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'700 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'700 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'800 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'800 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'900 years'::interval, 'YYYY'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'900 years'::interval, 'YYYY')); GRANT DELETE ON partman_test.time_taptest_table TO partman_basic; REVOKE ALL ON partman_test.time_taptest_table FROM partman_revoke; ALTER TABLE partman_test.time_taptest_table OWNER TO partman_owner; UPDATE part_config SET premake = 6 WHERE parent_table = 'partman_test.time_taptest_table'; SELECT run_maintenance(); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(123,150), extract('epoch' from CURRENT_TIMESTAMP + '600 years'::interval)); 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', ARRAY[148], 'Check count from parent table'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'600 years'::interval, 'YYYY'), ARRAY[28], 'Check count from time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'600 years'::interval, 'YYYY')); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1000 years'::interval, 'YYYY'), 'Check time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1000 years'::interval, 'YYYY')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1100 years'::interval, 'YYYY'), 'Check time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1100 years'::interval, 'YYYY')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1200 years'::interval, 'YYYY'), 'Check time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1200 years'::interval, 'YYYY')||' exists'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1000 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1000 years'::interval, 'YYYY')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1100 years'::interval, 'YYYY'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1100 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1000 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1000 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1100 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1100 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1000 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1000 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1100 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1100 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1000 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1000 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1100 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1100 years'::interval, 'YYYY')); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(200,210), extract('epoch' from CURRENT_TIMESTAMP + '2000 years'::interval)); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table', ARRAY[11], 'Check that data outside trigger scope goes to parent'); SELECT reapply_privileges('partman_test.time_taptest_table'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP), 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP), 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'100 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'100 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'200 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'200 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'300 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'300 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'400 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'400 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'100 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'100 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'200 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'200 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'300 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'300 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'400 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'400 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'500 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'500 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'600 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'600 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'700 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'700 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'800 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'800 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'900 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'900 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1000 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1000 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1100 years'::interval, 'YYYY'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1100 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP), 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP), 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'100 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'100 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'200 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'200 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'300 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'300 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'400 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'400 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'100 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'100 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'200 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'200 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'300 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'300 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'400 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'400 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'500 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'500 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'600 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'600 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'700 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'700 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'800 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'800 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'900 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'900 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1000 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1000 years'::interval, 'YYYY')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1100 years'::interval, 'YYYY'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1100 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP), 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP), 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'100 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'100 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'200 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'200 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'300 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'300 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'400 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'400 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'100 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'100 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'200 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'200 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'300 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'300 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'400 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'400 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'500 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'500 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'600 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'600 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'700 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'700 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'800 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'800 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'900 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'900 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1000 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1000 years'::interval, 'YYYY')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1100 years'::interval, 'YYYY'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1100 years'::interval, 'YYYY')); SELECT drop_partition_time('partman_test.time_taptest_table', '300 years', p_keep_table := false); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'400 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'400 years'::interval, 'YYYY')||' does not exist'); UPDATE part_config SET retention = '200 years'::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('century', CURRENT_TIMESTAMP)-'300 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'300 years'::interval, 'YYYY')||' does not exist'); SELECT has_table('partman_retention_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'300 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'300 years'::interval, 'YYYY')||' got moved to new schema'); SELECT undo_partition_time('partman_test.time_taptest_table', 20, p_keep_table := false); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table', ARRAY[129], 'Check count from parent table after undo'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP), 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP), 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'100 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'100 years'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'200 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)-'200 years'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'100 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'100 years'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'200 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'200 years'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'300 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'300 years'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'400 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'400 years'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'500 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'500 years'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'600 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'600 years'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'700 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'700 years'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'800 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'800 years'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'900 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'900 years'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1000 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1000 years'::interval, 'YYYY')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1100 years'::interval, 'YYYY'), 'Check time_taptest_table_'||to_char(date_trunc('century', CURRENT_TIMESTAMP)+'1100 years'::interval, 'YYYY')||' does not exist'); SELECT is_empty('SELECT * FROM custom_time_partitions WHERE parent_table = ''partman_test.time_taptest_table''', 'Check that custom_time_partitions table is empty'); SELECT * FROM finish(); ROLLBACK; pg_partman-2.2.2/test/test_no_search_path/000077500000000000000000000000001262146621700206475ustar00rootroot00000000000000pg_partman-2.2.2/test/test_no_search_path/test-time-daily_nosearchpath.sql000066400000000000000000001707761262146621700271640ustar00rootroot00000000000000-- ########## TIME STATIC TESTS ########## -- Other tests: With OIDS, run_maintenance(p_analyze := false), check that maintenance catches up if tables are missing -- Do not set the search path before running tests. Requires that pg_partman is installed to partman schema. -- This test it to ensure that all internal functions properly use the @extschema@ macro \set ON_ERROR_ROLLBACK 1 \set ON_ERROR_STOP true BEGIN; SELECT plan(210); CREATE SCHEMA partman_test; CREATE SCHEMA partman_retention_test; CREATE ROLE partman_basic; CREATE ROLE partman_revoke; CREATE ROLE partman_owner; CREATE TABLE partman_test.time_taptest_table (col1 int primary key, col2 text, col3 timestamptz NOT NULL DEFAULT now()) WITH (OIDS); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(1,10), CURRENT_TIMESTAMP); 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 partman.create_parent('partman_test.time_taptest_table', 'col3', 'time', 'daily'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'5 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT partman.partition_data_time(''partman_test.time_taptest_table'')::int', ARRAY[10], 'Check that partitioning function returns correct count of rows moved'); SELECT is_empty('SELECT * FROM ONLY partman_test.time_taptest_table', 'Check that parent table has had data moved to partition'); 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, 'YYYY_MM_DD'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); REVOKE INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER ON partman_test.time_taptest_table FROM partman_revoke; INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(11,20), CURRENT_TIMESTAMP + '1 day'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(21,25), CURRENT_TIMESTAMP + '2 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(26,30), CURRENT_TIMESTAMP + '3 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(31,37), CURRENT_TIMESTAMP + '4 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(40,49), CURRENT_TIMESTAMP - '1 day'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(50,70), CURRENT_TIMESTAMP - '2 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(71,85), CURRENT_TIMESTAMP - '3 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(86,100), CURRENT_TIMESTAMP - '4 days'::interval); 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+'1 day'::interval, 'YYYY_MM_DD'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), ARRAY[5], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), ARRAY[5], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), ARRAY[7], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD'), ARRAY[10], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), ARRAY[21], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), ARRAY[15], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), ARRAY[15], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')); UPDATE partman.part_config SET premake = 5, optimize_trigger = 5 WHERE parent_table = 'partman_test.time_taptest_table'; -- Run to get +5 trigger in plae SELECT partman.run_maintenance(p_analyze := false); -- Insert after maintenance so new +5 day trigger is in place INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(101,122), CURRENT_TIMESTAMP + '5 days'::interval); -- Run again to create proper future partitions SELECT partman.run_maintenance(p_analyze := false); -- Data exists for +5 days, with 5 premake so +10 day table should exist SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD')); 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+'5 days'::interval, 'YYYY_MM_DD'), ARRAY[22], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')); GRANT DELETE ON partman_test.time_taptest_table TO partman_basic; REVOKE ALL ON partman_test.time_taptest_table FROM partman_revoke; ALTER TABLE partman_test.time_taptest_table OWNER TO partman_owner; UPDATE partman.part_config SET premake = 6, optimize_trigger = 6 WHERE parent_table = 'partman_test.time_taptest_table'; SELECT partman.run_maintenance(p_analyze := false); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(123,150), CURRENT_TIMESTAMP + '6 days'::interval); SELECT partman.run_maintenance(p_analyze := false); 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', ARRAY[148], 'Check count from parent table'); SELECT results_eq('SELECT count(*)::int FROM partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), ARRAY[28], 'Check count from time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD')||' exists'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'13 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'13 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD')); SELECT col_is_pk('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD'), ARRAY['col1'], 'Check for primary key in time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', ARRAY['SELECT'], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD')); SELECT partman.reapply_privileges('partman_test.time_taptest_table'); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), 'partman_basic', ARRAY['SELECT','INSERT','UPDATE', 'DELETE'], 'Check partman_basic privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')); SELECT table_privs_are('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), 'partman_revoke', '{}'::text[], 'Check partman_revoke privileges of time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')); SELECT table_owner_is ('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), 'partman_owner', 'Check that ownership change worked for time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')); -- Test that maintenance will catch up DO $$ BEGIN EXECUTE 'DROP TABLE partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'); EXECUTE 'DROP TABLE partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 days'::interval, 'YYYY_MM_DD'); EXECUTE 'DROP TABLE partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'); EXECUTE 'DROP TABLE partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 days'::interval, 'YYYY_MM_DD'); EXECUTE 'DROP TABLE partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'); EXECUTE 'DROP TABLE partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'); EXECUTE 'DROP TABLE partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'); EXECUTE 'DROP TABLE partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'); EXECUTE 'DROP TABLE partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'); EXECUTE 'DROP TABLE partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD'); EXECUTE 'DROP TABLE partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'); EXECUTE 'DROP TABLE partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD'); EXECUTE 'DROP TABLE partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD'); EXECUTE 'DROP TABLE partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD'); EXECUTE 'DROP TABLE partman_test.time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD'); END $$; SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT partman.run_maintenance(); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||' does exist'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 days'::interval, 'YYYY_MM_DD')||' does exist'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||' does exist'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 days'::interval, 'YYYY_MM_DD')||' does exist'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||' does exist'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||' does exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||' does not exist'); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(11,20), CURRENT_TIMESTAMP + '1 day'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(21,25), CURRENT_TIMESTAMP + '2 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(26,30), CURRENT_TIMESTAMP + '3 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(1,10), CURRENT_TIMESTAMP); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(40,49), CURRENT_TIMESTAMP - '1 day'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(50,70), CURRENT_TIMESTAMP - '2 days'::interval); SELECT partman.run_maintenance(); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||' does exist'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' does exist'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')||' does exist'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')||' does exist'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')||' does exist'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD')||' does exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD')||' does not exist'); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(31,37), CURRENT_TIMESTAMP + '4 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(101,122), CURRENT_TIMESTAMP + '5 days'::interval); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(123,150), CURRENT_TIMESTAMP + '6 days'::interval); SELECT partman.run_maintenance(); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD')||' does exist'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD')||' does exist'); SELECT has_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD')||' does exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'13 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'13 days'::interval, 'YYYY_MM_DD')||' does not exist'); INSERT INTO partman_test.time_taptest_table (col1, col3) VALUES (generate_series(200,210), CURRENT_TIMESTAMP + '20 days'::interval); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table', ARRAY[11], 'Check that data outside trigger scope goes to parent'); SELECT partman.drop_partition_time('partman_test.time_taptest_table', '3 days', p_keep_table := false); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'4 days'::interval, 'YYYY_MM_DD')||' does not exist'); UPDATE partman.part_config SET retention = '2 days'::interval WHERE parent_table = 'partman_test.time_taptest_table'; SELECT partman.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 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT has_table('partman_retention_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'3 days'::interval, 'YYYY_MM_DD')||' got moved to new schema'); SELECT partman.undo_partition_time('partman_test.time_taptest_table', 20, p_keep_table := false); SELECT results_eq('SELECT count(*)::int FROM ONLY partman_test.time_taptest_table', ARRAY[129], 'Check count from parent table after undo'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'1 day'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'2 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'3 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'4 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'5 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'6 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'7 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'8 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'9 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'10 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'11 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP+'12 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'1 day'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT hasnt_table('partman_test', 'time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD'), 'Check time_taptest_table_p'||to_char(CURRENT_TIMESTAMP-'2 days'::interval, 'YYYY_MM_DD')||' does not exist'); SELECT * FROM finish(); ROLLBACK; pg_partman-2.2.2/test/test_reindex/000077500000000000000000000000001262146621700173305ustar00rootroot00000000000000pg_partman-2.2.2/test/test_reindex/01-setup-tables.sql000066400000000000000000000143471262146621700227100ustar00rootroot00000000000000\set ON_ERROR_ROLLBACK 1 \set ON_ERROR_STOP true CREATE SCHEMA partman_reindex_test; SELECT set_config('search_path','partman_reindex_test, partman, public',false); DELETE FROM part_config WHERE parent_table = 'partman_reindex_test.test_reindex'; CREATE TABLE test_reindex (id bigint, stuff text, morestuff timestamptz default now()); ALTER TABLE test_reindex ADD CONSTRAINT test_reindex_id_pkey PRIMARY KEY (id); INSERT INTO test_reindex VALUES (generate_series(1,1000), 'stuff'||generate_series(1, 1000)); CREATE INDEX test_reindex_stuff_idx ON test_reindex (stuff); CREATE INDEX test_reindex_upper_stuff_idx ON test_reindex(upper(stuff)); SELECT create_parent('partman_reindex_test.test_reindex', 'id', 'id', '100'); SELECT plan(40); SELECT has_table('partman_reindex_test', 'test_reindex', 'Check test_reindex exists'); SELECT col_is_pk('partman_reindex_test', 'test_reindex', ARRAY['id'], 'Check for primary key in test_reindex'); SELECT has_index('partman_reindex_test', 'test_reindex', 'test_reindex_stuff_idx', ARRAY['stuff'], 'Check for stuff index in test_reindex'); SELECT has_index('partman_reindex_test', 'test_reindex', 'test_reindex_upper_stuff_idx', ARRAY['upper(stuff)'], 'Check for upper(stuff) index in test_reindex'); SELECT has_table('partman_reindex_test', 'test_reindex_p600', 'Check test_reindex_p600 exists'); SELECT col_is_pk('partman_reindex_test', 'test_reindex_p600', ARRAY['id'], 'Check for primary key in test_reindex_p600'); SELECT has_index('partman_reindex_test', 'test_reindex_p600', 'test_reindex_p600_stuff_idx', ARRAY['stuff'], 'Check for stuff index in test_reindex_p600'); SELECT has_index('partman_reindex_test', 'test_reindex_p600', 'test_reindex_p600_upper_idx', ARRAY['upper(stuff)'], 'Check for upper(stuff) index in test_reindex_p600'); SELECT has_table('partman_reindex_test', 'test_reindex_p700', 'Check test_reindex_p700 exists'); SELECT col_is_pk('partman_reindex_test', 'test_reindex_p700', ARRAY['id'], 'Check for primary key in test_reindex_p700'); SELECT has_index('partman_reindex_test', 'test_reindex_p700', 'test_reindex_p700_stuff_idx', ARRAY['stuff'], 'Check for stuff index in test_reindex_p700'); SELECT has_index('partman_reindex_test', 'test_reindex_p700', 'test_reindex_p700_upper_idx', ARRAY['upper(stuff)'], 'Check for upper(stuff) index in test_reindex_p700'); SELECT has_table('partman_reindex_test', 'test_reindex_p800', 'Check test_reindex_p800 exists'); SELECT col_is_pk('partman_reindex_test', 'test_reindex_p800', ARRAY['id'], 'Check for primary key in test_reindex_p800'); SELECT has_index('partman_reindex_test', 'test_reindex_p800', 'test_reindex_p800_stuff_idx', ARRAY['stuff'], 'Check for stuff index in test_reindex_p800'); SELECT has_index('partman_reindex_test', 'test_reindex_p800', 'test_reindex_p800_upper_idx', ARRAY['upper(stuff)'], 'Check for upper(stuff) index in test_reindex_p800'); SELECT has_table('partman_reindex_test', 'test_reindex_p900', 'Check test_reindex_p900 exists'); SELECT col_is_pk('partman_reindex_test', 'test_reindex_p900', ARRAY['id'], 'Check for primary key in test_reindex_p900'); SELECT has_index('partman_reindex_test', 'test_reindex_p900', 'test_reindex_p900_stuff_idx', ARRAY['stuff'], 'Check for stuff index in test_reindex_p900'); SELECT has_index('partman_reindex_test', 'test_reindex_p900', 'test_reindex_p900_upper_idx', ARRAY['upper(stuff)'], 'Check for upper(stuff) index in test_reindex_p900'); SELECT has_table('partman_reindex_test', 'test_reindex_p1000', 'Check test_reindex_p1000 exists'); SELECT col_is_pk('partman_reindex_test', 'test_reindex_p1000', ARRAY['id'], 'Check for primary key in test_reindex_p1000'); SELECT has_index('partman_reindex_test', 'test_reindex_p1000', 'test_reindex_p1000_stuff_idx', ARRAY['stuff'], 'Check for stuff index in test_reindex_p1000'); SELECT has_index('partman_reindex_test', 'test_reindex_p1000', 'test_reindex_p1000_upper_idx', ARRAY['upper(stuff)'], 'Check for upper(stuff) index in test_reindex_p1000'); SELECT has_table('partman_reindex_test', 'test_reindex_p1100', 'Check test_reindex_p1100 exists'); SELECT col_is_pk('partman_reindex_test', 'test_reindex_p1100', ARRAY['id'], 'Check for primary key in test_reindex_p1100'); SELECT has_index('partman_reindex_test', 'test_reindex_p1100', 'test_reindex_p1100_stuff_idx', ARRAY['stuff'], 'Check for stuff index in test_reindex_p1100'); SELECT has_index('partman_reindex_test', 'test_reindex_p1100', 'test_reindex_p1100_upper_idx', ARRAY['upper(stuff)'], 'Check for upper(stuff) index in test_reindex_p1100'); SELECT has_table('partman_reindex_test', 'test_reindex_p1200', 'Check test_reindex_p1200 exists'); SELECT col_is_pk('partman_reindex_test', 'test_reindex_p1200', ARRAY['id'], 'Check for primary key in test_reindex_p1200'); SELECT has_index('partman_reindex_test', 'test_reindex_p1200', 'test_reindex_p1200_stuff_idx', ARRAY['stuff'], 'Check for stuff index in test_reindex_p1200'); SELECT has_index('partman_reindex_test', 'test_reindex_p1200', 'test_reindex_p1200_upper_idx', ARRAY['upper(stuff)'], 'Check for upper(stuff) index in test_reindex_p1200'); SELECT has_table('partman_reindex_test', 'test_reindex_p1300', 'Check test_reindex_p1300 exists'); SELECT col_is_pk('partman_reindex_test', 'test_reindex_p1300', ARRAY['id'], 'Check for primary key in test_reindex_p1300'); SELECT has_index('partman_reindex_test', 'test_reindex_p1300', 'test_reindex_p1300_stuff_idx', ARRAY['stuff'], 'Check for stuff index in test_reindex_p1300'); SELECT has_index('partman_reindex_test', 'test_reindex_p1300', 'test_reindex_p1300_upper_idx', ARRAY['upper(stuff)'], 'Check for upper(stuff) index in test_reindex_p1300'); SELECT has_table('partman_reindex_test', 'test_reindex_p1400', 'Check test_reindex_p1400 exists'); SELECT col_is_pk('partman_reindex_test', 'test_reindex_p1400', ARRAY['id'], 'Check for primary key in test_reindex_p1400'); SELECT has_index('partman_reindex_test', 'test_reindex_p1400', 'test_reindex_p1400_stuff_idx', ARRAY['stuff'], 'Check for stuff index in test_reindex_p1400'); SELECT has_index('partman_reindex_test', 'test_reindex_p1400', 'test_reindex_p1400_upper_idx', ARRAY['upper(stuff)'], 'Check for upper(stuff) index in test_reindex_p1400'); SELECT partition_data_id('partman_reindex_test.test_reindex', p_batch_count := 20); SELECT diag('!!! Next run 02-change-index.sql !!!'); SELECT * FROM finish(); pg_partman-2.2.2/test/test_reindex/02-change-index.sql000066400000000000000000000026241262146621700226260ustar00rootroot00000000000000\set ON_ERROR_ROLLBACK 1 \set ON_ERROR_STOP true SELECT set_config('search_path','partman_reindex_test, partman, public',false); DROP INDEX test_reindex_stuff_idx; CREATE INDEX test_reindex_morestuff_idx ON test_reindex (morestuff); CREATE INDEX test_reindex_stuff_morestuff_idx ON test_reindex (stuff, morestuff); CREATE INDEX test_reindex_lower_stuff_idx ON test_reindex(lower(stuff)); ALTER TABLE test_reindex ADD new_id bigint; UPDATE test_reindex SET new_id = id; ALTER TABLE test_reindex DROP CONSTRAINT test_reindex_id_pkey; ALTER TABLE test_reindex ADD CONSTRAINT test_reindex_new_id primary key (new_id); SELECT plan(3); SELECT hasnt_index('partman_reindex_test', 'test_reindex', 'test_reindex_stuff_idx', 'Check for stuff index in test_reindex'); SELECT has_index('partman_reindex_test', 'test_reindex', 'test_reindex_morestuff_idx', ARRAY['morestuff'], 'Check for stuff index in test_reindex'); SELECT col_is_pk('partman_reindex_test', 'test_reindex', ARRAY['new_id'], 'Check for new primary key in test_reindex'); SELECT diag('!!! Now run reapply_index.py on "partman_reindex_test.test_reindex" with the --primary option to apply the new indexes to all the children !!!'); SELECT diag('!!! After that completes, run 03-check-indexes.sql !!!'); SELECT diag('!!! You can set any options you''d like on the python script to test them. Re-run from test 01 to test different options !!! '); SELECT * FROM finish(); pg_partman-2.2.2/test/test_reindex/03-check-indexes.sql000066400000000000000000000354551262146621700230170ustar00rootroot00000000000000\set ON_ERROR_ROLLBACK 1 \set ON_ERROR_STOP true SELECT set_config('search_path','partman_reindex_test, partman, public',false); SELECT plan(90); SELECT hasnt_index('partman_reindex_test', 'test_reindex_p0', 'test_reindex_p0_stuff_idx', 'Check stuff index was removed in test_reindex_p0'); SELECT hasnt_index('partman_reindex_test', 'test_reindex_p100', 'test_reindex_p100_stuff_idx', 'Check stuff index was removed in test_reindex_p100'); SELECT hasnt_index('partman_reindex_test', 'test_reindex_p200', 'test_reindex_p200_stuff_idx', 'Check stuff index was removed in test_reindex_p200'); SELECT hasnt_index('partman_reindex_test', 'test_reindex_p300', 'test_reindex_p300_stuff_idx', 'Check stuff index was removed in test_reindex_p300'); SELECT hasnt_index('partman_reindex_test', 'test_reindex_p400', 'test_reindex_p400_stuff_idx', 'Check stuff index was removed in test_reindex_p400'); SELECT hasnt_index('partman_reindex_test', 'test_reindex_p500', 'test_reindex_p500_stuff_idx', 'Check stuff index was removed in test_reindex_p500'); SELECT hasnt_index('partman_reindex_test', 'test_reindex_p600', 'test_reindex_p600_stuff_idx', 'Check stuff index was removed in test_reindex_p600'); SELECT hasnt_index('partman_reindex_test', 'test_reindex_p700', 'test_reindex_p700_stuff_idx', 'Check stuff index was removed in test_reindex_p700'); SELECT hasnt_index('partman_reindex_test', 'test_reindex_p800', 'test_reindex_p800_stuff_idx', 'Check stuff index was removed in test_reindex_p800'); SELECT hasnt_index('partman_reindex_test', 'test_reindex_p900', 'test_reindex_p900_stuff_idx', 'Check stuff index was removed in test_reindex_p900'); SELECT hasnt_index('partman_reindex_test', 'test_reindex_p1000', 'test_reindex_p1000_stuff_idx', 'Check stuff index was removed in test_reindex_p1000'); SELECT hasnt_index('partman_reindex_test', 'test_reindex_p1100', 'test_reindex_p1100_stuff_idx', 'Check stuff index was removed in test_reindex_p1100'); SELECT hasnt_index('partman_reindex_test', 'test_reindex_p1200', 'test_reindex_p1200_stuff_idx', 'Check stuff index was removed in test_reindex_p1200'); SELECT hasnt_index('partman_reindex_test', 'test_reindex_p1300', 'test_reindex_p1300_stuff_idx', 'Check stuff index was removed in test_reindex_p1300'); SELECT hasnt_index('partman_reindex_test', 'test_reindex_p1400', 'test_reindex_p1400_stuff_idx', 'Check stuff index was removed in test_reindex_p1400'); SELECT has_index('partman_reindex_test', 'test_reindex_p0', 'test_reindex_p0_morestuff_idx', ARRAY['morestuff'], 'Check for morestuff index in test_reindex_p0'); SELECT has_index('partman_reindex_test', 'test_reindex_p100', 'test_reindex_p100_morestuff_idx', ARRAY['morestuff'], 'Check for morestuff index in test_reindex_p100'); SELECT has_index('partman_reindex_test', 'test_reindex_p200', 'test_reindex_p200_morestuff_idx', ARRAY['morestuff'], 'Check for morestuff index in test_reindex_p200'); SELECT has_index('partman_reindex_test', 'test_reindex_p300', 'test_reindex_p300_morestuff_idx', ARRAY['morestuff'], 'Check for morestuff index in test_reindex_p300'); SELECT has_index('partman_reindex_test', 'test_reindex_p400', 'test_reindex_p400_morestuff_idx', ARRAY['morestuff'], 'Check for morestuff index in test_reindex_p400'); SELECT has_index('partman_reindex_test', 'test_reindex_p500', 'test_reindex_p500_morestuff_idx', ARRAY['morestuff'], 'Check for morestuff index in test_reindex_p500'); SELECT has_index('partman_reindex_test', 'test_reindex_p600', 'test_reindex_p600_morestuff_idx', ARRAY['morestuff'], 'Check for morestuff index in test_reindex_p600'); SELECT has_index('partman_reindex_test', 'test_reindex_p700', 'test_reindex_p700_morestuff_idx', ARRAY['morestuff'], 'Check for morestuff index in test_reindex_p700'); SELECT has_index('partman_reindex_test', 'test_reindex_p800', 'test_reindex_p800_morestuff_idx', ARRAY['morestuff'], 'Check for morestuff index in test_reindex_p800'); SELECT has_index('partman_reindex_test', 'test_reindex_p900', 'test_reindex_p900_morestuff_idx', ARRAY['morestuff'], 'Check for morestuff index in test_reindex_p900'); SELECT has_index('partman_reindex_test', 'test_reindex_p1000', 'test_reindex_p1000_morestuff_idx', ARRAY['morestuff'], 'Check for morestuff index in test_reindex_p1000'); SELECT has_index('partman_reindex_test', 'test_reindex_p1100', 'test_reindex_p1100_morestuff_idx', ARRAY['morestuff'], 'Check for morestuff index in test_reindex_p1100'); SELECT has_index('partman_reindex_test', 'test_reindex_p1200', 'test_reindex_p1200_morestuff_idx', ARRAY['morestuff'], 'Check for morestuff index in test_reindex_p1200'); SELECT has_index('partman_reindex_test', 'test_reindex_p1300', 'test_reindex_p1300_morestuff_idx', ARRAY['morestuff'], 'Check for morestuff index in test_reindex_p1300'); SELECT has_index('partman_reindex_test', 'test_reindex_p1400', 'test_reindex_p1400_morestuff_idx', ARRAY['morestuff'], 'Check for morestuff index in test_reindex_p1400'); SELECT has_index('partman_reindex_test', 'test_reindex_p0', 'test_reindex_p0_stuff_morestuff_idx', ARRAY['stuff', 'morestuff'], 'Check for morestuff index in test_reindex_p0'); SELECT has_index('partman_reindex_test', 'test_reindex_p100', 'test_reindex_p100_stuff_morestuff_idx', ARRAY['stuff', 'morestuff'], 'Check for stuff,morestuff index in test_reindex_p100'); SELECT has_index('partman_reindex_test', 'test_reindex_p200', 'test_reindex_p200_stuff_morestuff_idx', ARRAY['stuff', 'morestuff'], 'Check for stuff,morestuff index in test_reindex_p200'); SELECT has_index('partman_reindex_test', 'test_reindex_p300', 'test_reindex_p300_stuff_morestuff_idx', ARRAY['stuff', 'morestuff'], 'Check for stuff,morestuff index in test_reindex_p300'); SELECT has_index('partman_reindex_test', 'test_reindex_p400', 'test_reindex_p400_stuff_morestuff_idx', ARRAY['stuff', 'morestuff'], 'Check for stuff,morestuff index in test_reindex_p400'); SELECT has_index('partman_reindex_test', 'test_reindex_p500', 'test_reindex_p500_stuff_morestuff_idx', ARRAY['stuff', 'morestuff'], 'Check for stuff,morestuff index in test_reindex_p500'); SELECT has_index('partman_reindex_test', 'test_reindex_p600', 'test_reindex_p600_stuff_morestuff_idx', ARRAY['stuff', 'morestuff'], 'Check for stuff,morestuff index in test_reindex_p600'); SELECT has_index('partman_reindex_test', 'test_reindex_p700', 'test_reindex_p700_stuff_morestuff_idx', ARRAY['stuff', 'morestuff'], 'Check for stuff,morestuff index in test_reindex_p700'); SELECT has_index('partman_reindex_test', 'test_reindex_p800', 'test_reindex_p800_stuff_morestuff_idx', ARRAY['stuff', 'morestuff'], 'Check for stuff,morestuff index in test_reindex_p800'); SELECT has_index('partman_reindex_test', 'test_reindex_p900', 'test_reindex_p900_stuff_morestuff_idx', ARRAY['stuff', 'morestuff'], 'Check for stuff,morestuff index in test_reindex_p900'); SELECT has_index('partman_reindex_test', 'test_reindex_p1000', 'test_reindex_p1000_stuff_morestuff_idx', ARRAY['stuff', 'morestuff'], 'Check for stuff,morestuff index in test_reindex_p1000'); SELECT has_index('partman_reindex_test', 'test_reindex_p1100', 'test_reindex_p1100_stuff_morestuff_idx', ARRAY['stuff', 'morestuff'], 'Check for stuff,morestuff index in test_reindex_p1100'); SELECT has_index('partman_reindex_test', 'test_reindex_p1200', 'test_reindex_p1200_stuff_morestuff_idx', ARRAY['stuff', 'morestuff'], 'Check for stuff,morestuff index in test_reindex_p1200'); SELECT has_index('partman_reindex_test', 'test_reindex_p1300', 'test_reindex_p1300_stuff_morestuff_idx', ARRAY['stuff', 'morestuff'], 'Check for stuff,morestuff index in test_reindex_p1300'); SELECT has_index('partman_reindex_test', 'test_reindex_p1400', 'test_reindex_p1400_stuff_morestuff_idx', ARRAY['stuff', 'morestuff'], 'Check for stuff,morestuff index in test_reindex_p1400'); SELECT has_index('partman_reindex_test', 'test_reindex_p0', 'test_reindex_p0_idx', ARRAY['lower(stuff)'], 'Check for lower(stuff) index in test_reindex_p0'); SELECT has_index('partman_reindex_test', 'test_reindex_p100', 'test_reindex_p100_idx', ARRAY['lower(stuff)'], 'Check for lower(stuff) stuff index in test_reindex_p100'); SELECT has_index('partman_reindex_test', 'test_reindex_p200', 'test_reindex_p200_idx', ARRAY['lower(stuff)'], 'Check for lower(stuff) stuff index in test_reindex_p200'); SELECT has_index('partman_reindex_test', 'test_reindex_p300', 'test_reindex_p300_idx', ARRAY['lower(stuff)'], 'Check for lower(stuff) stuff index in test_reindex_p300'); SELECT has_index('partman_reindex_test', 'test_reindex_p400', 'test_reindex_p400_idx', ARRAY['lower(stuff)'], 'Check for lower(stuff) stuff index in test_reindex_p400'); SELECT has_index('partman_reindex_test', 'test_reindex_p500', 'test_reindex_p500_idx', ARRAY['lower(stuff)'], 'Check for lower(stuff) stuff index in test_reindex_p500'); SELECT has_index('partman_reindex_test', 'test_reindex_p600', 'test_reindex_p600_idx', ARRAY['lower(stuff)'], 'Check for lower(stuff) stuff index in test_reindex_p600'); SELECT has_index('partman_reindex_test', 'test_reindex_p700', 'test_reindex_p700_idx', ARRAY['lower(stuff)'], 'Check for lower(stuff) stuff index in test_reindex_p700'); SELECT has_index('partman_reindex_test', 'test_reindex_p800', 'test_reindex_p800_idx', ARRAY['lower(stuff)'], 'Check for lower(stuff) stuff index in test_reindex_p800'); SELECT has_index('partman_reindex_test', 'test_reindex_p900', 'test_reindex_p900_idx', ARRAY['lower(stuff)'], 'Check for lower(stuff) stuff index in test_reindex_p900'); SELECT has_index('partman_reindex_test', 'test_reindex_p1000', 'test_reindex_p1000_idx', ARRAY['lower(stuff)'], 'Check for lower(stuff) stuff index in test_reindex_p1000'); SELECT has_index('partman_reindex_test', 'test_reindex_p1100', 'test_reindex_p1100_idx', ARRAY['lower(stuff)'], 'Check for lower(stuff) stuff index in test_reindex_p1100'); SELECT has_index('partman_reindex_test', 'test_reindex_p1200', 'test_reindex_p1200_idx', ARRAY['lower(stuff)'], 'Check for lower(stuff) stuff index in test_reindex_p1200'); SELECT has_index('partman_reindex_test', 'test_reindex_p1300', 'test_reindex_p1300_idx', ARRAY['lower(stuff)'], 'Check for lower(stuff) stuff index in test_reindex_p1300'); SELECT has_index('partman_reindex_test', 'test_reindex_p1400', 'test_reindex_p1400_idx', ARRAY['lower(stuff)'], 'Check for lower(stuff) stuff index in test_reindex_p1400'); SELECT has_index('partman_reindex_test', 'test_reindex_p0', 'test_reindex_p0_upper_idx', ARRAY['upper(stuff)'], 'Check for upper(stuff) index in test_reindex_p0'); SELECT has_index('partman_reindex_test', 'test_reindex_p100', 'test_reindex_p100_upper_idx', ARRAY['upper(stuff)'], 'Check for upper(stuff) stuff index in test_reindex_p100'); SELECT has_index('partman_reindex_test', 'test_reindex_p200', 'test_reindex_p200_upper_idx', ARRAY['upper(stuff)'], 'Check for upper(stuff) stuff index in test_reindex_p200'); SELECT has_index('partman_reindex_test', 'test_reindex_p300', 'test_reindex_p300_upper_idx', ARRAY['upper(stuff)'], 'Check for upper(stuff) stuff index in test_reindex_p300'); SELECT has_index('partman_reindex_test', 'test_reindex_p400', 'test_reindex_p400_upper_idx', ARRAY['upper(stuff)'], 'Check for upper(stuff) stuff index in test_reindex_p400'); SELECT has_index('partman_reindex_test', 'test_reindex_p500', 'test_reindex_p500_upper_idx', ARRAY['upper(stuff)'], 'Check for upper(stuff) stuff index in test_reindex_p500'); SELECT has_index('partman_reindex_test', 'test_reindex_p600', 'test_reindex_p600_upper_idx', ARRAY['upper(stuff)'], 'Check for upper(stuff) stuff index in test_reindex_p600'); SELECT has_index('partman_reindex_test', 'test_reindex_p700', 'test_reindex_p700_upper_idx', ARRAY['upper(stuff)'], 'Check for upper(stuff) stuff index in test_reindex_p700'); SELECT has_index('partman_reindex_test', 'test_reindex_p800', 'test_reindex_p800_upper_idx', ARRAY['upper(stuff)'], 'Check for upper(stuff) stuff index in test_reindex_p800'); SELECT has_index('partman_reindex_test', 'test_reindex_p900', 'test_reindex_p900_upper_idx', ARRAY['upper(stuff)'], 'Check for upper(stuff) stuff index in test_reindex_p900'); SELECT has_index('partman_reindex_test', 'test_reindex_p1000', 'test_reindex_p1000_upper_idx', ARRAY['upper(stuff)'], 'Check for upper(stuff) stuff index in test_reindex_p1000'); SELECT has_index('partman_reindex_test', 'test_reindex_p1100', 'test_reindex_p1100_upper_idx', ARRAY['upper(stuff)'], 'Check for upper(stuff) stuff index in test_reindex_p1100'); SELECT has_index('partman_reindex_test', 'test_reindex_p1200', 'test_reindex_p1200_upper_idx', ARRAY['upper(stuff)'], 'Check for upper(stuff) stuff index in test_reindex_p1200'); SELECT has_index('partman_reindex_test', 'test_reindex_p1300', 'test_reindex_p1300_upper_idx', ARRAY['upper(stuff)'], 'Check for upper(stuff) stuff index in test_reindex_p1300'); SELECT has_index('partman_reindex_test', 'test_reindex_p1400', 'test_reindex_p1400_upper_idx', ARRAY['upper(stuff)'], 'Check for upper(stuff) stuff index in test_reindex_p1400'); SELECT col_is_pk('partman_reindex_test', 'test_reindex_p0', ARRAY['new_id'], 'Check for new primary key in test_reindex_p0'); SELECT col_is_pk('partman_reindex_test', 'test_reindex_p100', ARRAY['new_id'], 'Check for new primary key in test_reindex_p100'); SELECT col_is_pk('partman_reindex_test', 'test_reindex_p200', ARRAY['new_id'], 'Check for new primary key in test_reindex_p200'); SELECT col_is_pk('partman_reindex_test', 'test_reindex_p300', ARRAY['new_id'], 'Check for new primary key in test_reindex_p300'); SELECT col_is_pk('partman_reindex_test', 'test_reindex_p400', ARRAY['new_id'], 'Check for new primary key in test_reindex_p400'); SELECT col_is_pk('partman_reindex_test', 'test_reindex_p500', ARRAY['new_id'], 'Check for new primary key in test_reindex_p500'); SELECT col_is_pk('partman_reindex_test', 'test_reindex_p600', ARRAY['new_id'], 'Check for new primary key in test_reindex_p600'); SELECT col_is_pk('partman_reindex_test', 'test_reindex_p700', ARRAY['new_id'], 'Check for new primary key in test_reindex_p700'); SELECT col_is_pk('partman_reindex_test', 'test_reindex_p800', ARRAY['new_id'], 'Check for new primary key in test_reindex_p800'); SELECT col_is_pk('partman_reindex_test', 'test_reindex_p900', ARRAY['new_id'], 'Check for new primary key in test_reindex_p900'); SELECT col_is_pk('partman_reindex_test', 'test_reindex_p1000', ARRAY['new_id'], 'Check for new primary key in test_reindex_p1000'); SELECT col_is_pk('partman_reindex_test', 'test_reindex_p1100', ARRAY['new_id'], 'Check for new primary key in test_reindex_p1100'); SELECT col_is_pk('partman_reindex_test', 'test_reindex_p1200', ARRAY['new_id'], 'Check for new primary key in test_reindex_p1200'); SELECT col_is_pk('partman_reindex_test', 'test_reindex_p1300', ARRAY['new_id'], 'Check for new primary key in test_reindex_p1300'); SELECT col_is_pk('partman_reindex_test', 'test_reindex_p1400', ARRAY['new_id'], 'Check for new primary key in test_reindex_p1400'); SELECT diag('!!! Now run reapply_index.py again with all the same arguments as last time but add --recreate_all !!!'); SELECT diag('!!! After that completes, run 04-check-indexes.sql !!!'); SELECT * FROM finish(); pg_partman-2.2.2/test/test_reindex/04-recreate_all_indexes.sql000066400000000000000000000352461262146621700244450ustar00rootroot00000000000000\set ON_ERROR_ROLLBACK 1 \set ON_ERROR_STOP true SELECT set_config('search_path','partman_reindex_test, partman, public',false); SELECT plan(90); SELECT hasnt_index('partman_reindex_test', 'test_reindex_p0', 'test_reindex_p0_stuff_idx', 'Check stuff index was removed in test_reindex_p0'); SELECT hasnt_index('partman_reindex_test', 'test_reindex_p100', 'test_reindex_p100_stuff_idx', 'Check stuff index was removed in test_reindex_p100'); SELECT hasnt_index('partman_reindex_test', 'test_reindex_p200', 'test_reindex_p200_stuff_idx', 'Check stuff index was removed in test_reindex_p200'); SELECT hasnt_index('partman_reindex_test', 'test_reindex_p300', 'test_reindex_p300_stuff_idx', 'Check stuff index was removed in test_reindex_p300'); SELECT hasnt_index('partman_reindex_test', 'test_reindex_p400', 'test_reindex_p400_stuff_idx', 'Check stuff index was removed in test_reindex_p400'); SELECT hasnt_index('partman_reindex_test', 'test_reindex_p500', 'test_reindex_p500_stuff_idx', 'Check stuff index was removed in test_reindex_p500'); SELECT hasnt_index('partman_reindex_test', 'test_reindex_p600', 'test_reindex_p600_stuff_idx', 'Check stuff index was removed in test_reindex_p600'); SELECT hasnt_index('partman_reindex_test', 'test_reindex_p700', 'test_reindex_p700_stuff_idx', 'Check stuff index was removed in test_reindex_p700'); SELECT hasnt_index('partman_reindex_test', 'test_reindex_p800', 'test_reindex_p800_stuff_idx', 'Check stuff index was removed in test_reindex_p800'); SELECT hasnt_index('partman_reindex_test', 'test_reindex_p900', 'test_reindex_p900_stuff_idx', 'Check stuff index was removed in test_reindex_p900'); SELECT hasnt_index('partman_reindex_test', 'test_reindex_p1000', 'test_reindex_p1000_stuff_idx', 'Check stuff index was removed in test_reindex_p1000'); SELECT hasnt_index('partman_reindex_test', 'test_reindex_p1100', 'test_reindex_p1100_stuff_idx', 'Check stuff index was removed in test_reindex_p1100'); SELECT hasnt_index('partman_reindex_test', 'test_reindex_p1200', 'test_reindex_p1200_stuff_idx', 'Check stuff index was removed in test_reindex_p1200'); SELECT hasnt_index('partman_reindex_test', 'test_reindex_p1300', 'test_reindex_p1300_stuff_idx', 'Check stuff index was removed in test_reindex_p1300'); SELECT hasnt_index('partman_reindex_test', 'test_reindex_p1400', 'test_reindex_p1400_stuff_idx', 'Check stuff index was removed in test_reindex_p1400'); SELECT has_index('partman_reindex_test', 'test_reindex_p0', 'test_reindex_p0_morestuff_idx', ARRAY['morestuff'], 'Check for morestuff index in test_reindex_p0'); SELECT has_index('partman_reindex_test', 'test_reindex_p100', 'test_reindex_p100_morestuff_idx', ARRAY['morestuff'], 'Check for morestuff index in test_reindex_p100'); SELECT has_index('partman_reindex_test', 'test_reindex_p200', 'test_reindex_p200_morestuff_idx', ARRAY['morestuff'], 'Check for morestuff index in test_reindex_p200'); SELECT has_index('partman_reindex_test', 'test_reindex_p300', 'test_reindex_p300_morestuff_idx', ARRAY['morestuff'], 'Check for morestuff index in test_reindex_p300'); SELECT has_index('partman_reindex_test', 'test_reindex_p400', 'test_reindex_p400_morestuff_idx', ARRAY['morestuff'], 'Check for morestuff index in test_reindex_p400'); SELECT has_index('partman_reindex_test', 'test_reindex_p500', 'test_reindex_p500_morestuff_idx', ARRAY['morestuff'], 'Check for morestuff index in test_reindex_p500'); SELECT has_index('partman_reindex_test', 'test_reindex_p600', 'test_reindex_p600_morestuff_idx', ARRAY['morestuff'], 'Check for morestuff index in test_reindex_p600'); SELECT has_index('partman_reindex_test', 'test_reindex_p700', 'test_reindex_p700_morestuff_idx', ARRAY['morestuff'], 'Check for morestuff index in test_reindex_p700'); SELECT has_index('partman_reindex_test', 'test_reindex_p800', 'test_reindex_p800_morestuff_idx', ARRAY['morestuff'], 'Check for morestuff index in test_reindex_p800'); SELECT has_index('partman_reindex_test', 'test_reindex_p900', 'test_reindex_p900_morestuff_idx', ARRAY['morestuff'], 'Check for morestuff index in test_reindex_p900'); SELECT has_index('partman_reindex_test', 'test_reindex_p1000', 'test_reindex_p1000_morestuff_idx', ARRAY['morestuff'], 'Check for morestuff index in test_reindex_p1000'); SELECT has_index('partman_reindex_test', 'test_reindex_p1100', 'test_reindex_p1100_morestuff_idx', ARRAY['morestuff'], 'Check for morestuff index in test_reindex_p1100'); SELECT has_index('partman_reindex_test', 'test_reindex_p1200', 'test_reindex_p1200_morestuff_idx', ARRAY['morestuff'], 'Check for morestuff index in test_reindex_p1200'); SELECT has_index('partman_reindex_test', 'test_reindex_p1300', 'test_reindex_p1300_morestuff_idx', ARRAY['morestuff'], 'Check for morestuff index in test_reindex_p1300'); SELECT has_index('partman_reindex_test', 'test_reindex_p1400', 'test_reindex_p1400_morestuff_idx', ARRAY['morestuff'], 'Check for morestuff index in test_reindex_p1400'); SELECT has_index('partman_reindex_test', 'test_reindex_p0', 'test_reindex_p0_stuff_morestuff_idx', ARRAY['stuff', 'morestuff'], 'Check for morestuff index in test_reindex_p0'); SELECT has_index('partman_reindex_test', 'test_reindex_p100', 'test_reindex_p100_stuff_morestuff_idx', ARRAY['stuff', 'morestuff'], 'Check for stuff,morestuff index in test_reindex_p100'); SELECT has_index('partman_reindex_test', 'test_reindex_p200', 'test_reindex_p200_stuff_morestuff_idx', ARRAY['stuff', 'morestuff'], 'Check for stuff,morestuff index in test_reindex_p200'); SELECT has_index('partman_reindex_test', 'test_reindex_p300', 'test_reindex_p300_stuff_morestuff_idx', ARRAY['stuff', 'morestuff'], 'Check for stuff,morestuff index in test_reindex_p300'); SELECT has_index('partman_reindex_test', 'test_reindex_p400', 'test_reindex_p400_stuff_morestuff_idx', ARRAY['stuff', 'morestuff'], 'Check for stuff,morestuff index in test_reindex_p400'); SELECT has_index('partman_reindex_test', 'test_reindex_p500', 'test_reindex_p500_stuff_morestuff_idx', ARRAY['stuff', 'morestuff'], 'Check for stuff,morestuff index in test_reindex_p500'); SELECT has_index('partman_reindex_test', 'test_reindex_p600', 'test_reindex_p600_stuff_morestuff_idx', ARRAY['stuff', 'morestuff'], 'Check for stuff,morestuff index in test_reindex_p600'); SELECT has_index('partman_reindex_test', 'test_reindex_p700', 'test_reindex_p700_stuff_morestuff_idx', ARRAY['stuff', 'morestuff'], 'Check for stuff,morestuff index in test_reindex_p700'); SELECT has_index('partman_reindex_test', 'test_reindex_p800', 'test_reindex_p800_stuff_morestuff_idx', ARRAY['stuff', 'morestuff'], 'Check for stuff,morestuff index in test_reindex_p800'); SELECT has_index('partman_reindex_test', 'test_reindex_p900', 'test_reindex_p900_stuff_morestuff_idx', ARRAY['stuff', 'morestuff'], 'Check for stuff,morestuff index in test_reindex_p900'); SELECT has_index('partman_reindex_test', 'test_reindex_p1000', 'test_reindex_p1000_stuff_morestuff_idx', ARRAY['stuff', 'morestuff'], 'Check for stuff,morestuff index in test_reindex_p1000'); SELECT has_index('partman_reindex_test', 'test_reindex_p1100', 'test_reindex_p1100_stuff_morestuff_idx', ARRAY['stuff', 'morestuff'], 'Check for stuff,morestuff index in test_reindex_p1100'); SELECT has_index('partman_reindex_test', 'test_reindex_p1200', 'test_reindex_p1200_stuff_morestuff_idx', ARRAY['stuff', 'morestuff'], 'Check for stuff,morestuff index in test_reindex_p1200'); SELECT has_index('partman_reindex_test', 'test_reindex_p1300', 'test_reindex_p1300_stuff_morestuff_idx', ARRAY['stuff', 'morestuff'], 'Check for stuff,morestuff index in test_reindex_p1300'); SELECT has_index('partman_reindex_test', 'test_reindex_p1400', 'test_reindex_p1400_stuff_morestuff_idx', ARRAY['stuff', 'morestuff'], 'Check for stuff,morestuff index in test_reindex_p1400'); SELECT has_index('partman_reindex_test', 'test_reindex_p0', 'test_reindex_p0_idx', ARRAY['lower(stuff)'], 'Check for lower(stuff) index in test_reindex_p0'); SELECT has_index('partman_reindex_test', 'test_reindex_p100', 'test_reindex_p100_idx', ARRAY['lower(stuff)'], 'Check for lower(stuff) stuff index in test_reindex_p100'); SELECT has_index('partman_reindex_test', 'test_reindex_p200', 'test_reindex_p200_idx', ARRAY['lower(stuff)'], 'Check for lower(stuff) stuff index in test_reindex_p200'); SELECT has_index('partman_reindex_test', 'test_reindex_p300', 'test_reindex_p300_idx', ARRAY['lower(stuff)'], 'Check for lower(stuff) stuff index in test_reindex_p300'); SELECT has_index('partman_reindex_test', 'test_reindex_p400', 'test_reindex_p400_idx', ARRAY['lower(stuff)'], 'Check for lower(stuff) stuff index in test_reindex_p400'); SELECT has_index('partman_reindex_test', 'test_reindex_p500', 'test_reindex_p500_idx', ARRAY['lower(stuff)'], 'Check for lower(stuff) stuff index in test_reindex_p500'); SELECT has_index('partman_reindex_test', 'test_reindex_p600', 'test_reindex_p600_idx', ARRAY['lower(stuff)'], 'Check for lower(stuff) stuff index in test_reindex_p600'); SELECT has_index('partman_reindex_test', 'test_reindex_p700', 'test_reindex_p700_idx', ARRAY['lower(stuff)'], 'Check for lower(stuff) stuff index in test_reindex_p700'); SELECT has_index('partman_reindex_test', 'test_reindex_p800', 'test_reindex_p800_idx', ARRAY['lower(stuff)'], 'Check for lower(stuff) stuff index in test_reindex_p800'); SELECT has_index('partman_reindex_test', 'test_reindex_p900', 'test_reindex_p900_idx', ARRAY['lower(stuff)'], 'Check for lower(stuff) stuff index in test_reindex_p900'); SELECT has_index('partman_reindex_test', 'test_reindex_p1000', 'test_reindex_p1000_idx', ARRAY['lower(stuff)'], 'Check for lower(stuff) stuff index in test_reindex_p1000'); SELECT has_index('partman_reindex_test', 'test_reindex_p1100', 'test_reindex_p1100_idx', ARRAY['lower(stuff)'], 'Check for lower(stuff) stuff index in test_reindex_p1100'); SELECT has_index('partman_reindex_test', 'test_reindex_p1200', 'test_reindex_p1200_idx', ARRAY['lower(stuff)'], 'Check for lower(stuff) stuff index in test_reindex_p1200'); SELECT has_index('partman_reindex_test', 'test_reindex_p1300', 'test_reindex_p1300_idx', ARRAY['lower(stuff)'], 'Check for lower(stuff) stuff index in test_reindex_p1300'); SELECT has_index('partman_reindex_test', 'test_reindex_p1400', 'test_reindex_p1400_idx', ARRAY['lower(stuff)'], 'Check for lower(stuff) stuff index in test_reindex_p1400'); SELECT has_index('partman_reindex_test', 'test_reindex_p0', 'test_reindex_p0_idx1', ARRAY['upper(stuff)'], 'Check for upper(stuff) index in test_reindex_p0'); SELECT has_index('partman_reindex_test', 'test_reindex_p100', 'test_reindex_p100_idx1', ARRAY['upper(stuff)'], 'Check for upper(stuff) stuff index in test_reindex_p100'); SELECT has_index('partman_reindex_test', 'test_reindex_p200', 'test_reindex_p200_idx1', ARRAY['upper(stuff)'], 'Check for upper(stuff) stuff index in test_reindex_p200'); SELECT has_index('partman_reindex_test', 'test_reindex_p300', 'test_reindex_p300_idx1', ARRAY['upper(stuff)'], 'Check for upper(stuff) stuff index in test_reindex_p300'); SELECT has_index('partman_reindex_test', 'test_reindex_p400', 'test_reindex_p400_idx1', ARRAY['upper(stuff)'], 'Check for upper(stuff) stuff index in test_reindex_p400'); SELECT has_index('partman_reindex_test', 'test_reindex_p500', 'test_reindex_p500_idx1', ARRAY['upper(stuff)'], 'Check for upper(stuff) stuff index in test_reindex_p500'); SELECT has_index('partman_reindex_test', 'test_reindex_p600', 'test_reindex_p600_idx1', ARRAY['upper(stuff)'], 'Check for upper(stuff) stuff index in test_reindex_p600'); SELECT has_index('partman_reindex_test', 'test_reindex_p700', 'test_reindex_p700_idx1', ARRAY['upper(stuff)'], 'Check for upper(stuff) stuff index in test_reindex_p700'); SELECT has_index('partman_reindex_test', 'test_reindex_p800', 'test_reindex_p800_idx1', ARRAY['upper(stuff)'], 'Check for upper(stuff) stuff index in test_reindex_p800'); SELECT has_index('partman_reindex_test', 'test_reindex_p900', 'test_reindex_p900_idx1', ARRAY['upper(stuff)'], 'Check for upper(stuff) stuff index in test_reindex_p900'); SELECT has_index('partman_reindex_test', 'test_reindex_p1000', 'test_reindex_p1000_idx1', ARRAY['upper(stuff)'], 'Check for upper(stuff) stuff index in test_reindex_p1000'); SELECT has_index('partman_reindex_test', 'test_reindex_p1100', 'test_reindex_p1100_idx1', ARRAY['upper(stuff)'], 'Check for upper(stuff) stuff index in test_reindex_p1100'); SELECT has_index('partman_reindex_test', 'test_reindex_p1200', 'test_reindex_p1200_idx1', ARRAY['upper(stuff)'], 'Check for upper(stuff) stuff index in test_reindex_p1200'); SELECT has_index('partman_reindex_test', 'test_reindex_p1300', 'test_reindex_p1300_idx1', ARRAY['upper(stuff)'], 'Check for upper(stuff) stuff index in test_reindex_p1300'); SELECT has_index('partman_reindex_test', 'test_reindex_p1400', 'test_reindex_p1400_idx1', ARRAY['upper(stuff)'], 'Check for upper(stuff) stuff index in test_reindex_p1400'); SELECT col_is_pk('partman_reindex_test', 'test_reindex_p0', ARRAY['new_id'], 'Check for new primary key in test_reindex_p0'); SELECT col_is_pk('partman_reindex_test', 'test_reindex_p100', ARRAY['new_id'], 'Check for new primary key in test_reindex_p100'); SELECT col_is_pk('partman_reindex_test', 'test_reindex_p200', ARRAY['new_id'], 'Check for new primary key in test_reindex_p200'); SELECT col_is_pk('partman_reindex_test', 'test_reindex_p300', ARRAY['new_id'], 'Check for new primary key in test_reindex_p300'); SELECT col_is_pk('partman_reindex_test', 'test_reindex_p400', ARRAY['new_id'], 'Check for new primary key in test_reindex_p400'); SELECT col_is_pk('partman_reindex_test', 'test_reindex_p500', ARRAY['new_id'], 'Check for new primary key in test_reindex_p500'); SELECT col_is_pk('partman_reindex_test', 'test_reindex_p600', ARRAY['new_id'], 'Check for new primary key in test_reindex_p600'); SELECT col_is_pk('partman_reindex_test', 'test_reindex_p700', ARRAY['new_id'], 'Check for new primary key in test_reindex_p700'); SELECT col_is_pk('partman_reindex_test', 'test_reindex_p800', ARRAY['new_id'], 'Check for new primary key in test_reindex_p800'); SELECT col_is_pk('partman_reindex_test', 'test_reindex_p900', ARRAY['new_id'], 'Check for new primary key in test_reindex_p900'); SELECT col_is_pk('partman_reindex_test', 'test_reindex_p1000', ARRAY['new_id'], 'Check for new primary key in test_reindex_p1000'); SELECT col_is_pk('partman_reindex_test', 'test_reindex_p1100', ARRAY['new_id'], 'Check for new primary key in test_reindex_p1100'); SELECT col_is_pk('partman_reindex_test', 'test_reindex_p1200', ARRAY['new_id'], 'Check for new primary key in test_reindex_p1200'); SELECT col_is_pk('partman_reindex_test', 'test_reindex_p1300', ARRAY['new_id'], 'Check for new primary key in test_reindex_p1300'); SELECT col_is_pk('partman_reindex_test', 'test_reindex_p1400', ARRAY['new_id'], 'Check for new primary key in test_reindex_p1400'); SELECT diag('!!! If all tests have passed, you can now run 99-cleanup-reindex-test.sql to remove all reindexing test objects !!!'); SELECT * FROM finish(); pg_partman-2.2.2/test/test_reindex/99-cleanup-reindex-test.sql000066400000000000000000000005311262146621700243470ustar00rootroot00000000000000\set ON_ERROR_ROLLBACK 1 \set ON_ERROR_STOP true SELECT set_config('search_path','partman_reindex_test, partman, public',false); SELECT plan(1); SELECT undo_partition_id('partman_reindex_test.test_reindex', 20, p_keep_table := false); DROP SCHEMA IF EXISTS partman_reindex_test CASCADE; SELECT pass('Cleanup Done'); SELECT * FROM finish(); pg_partman-2.2.2/updates/000077500000000000000000000000001262146621700153215ustar00rootroot00000000000000pg_partman-2.2.2/updates/pg_partman--0.1.0--0.1.1.sql000066400000000000000000000054471262146621700214230ustar00rootroot00000000000000-- Only re-create partition functions if a new partition is made. CREATE OR REPLACE FUNCTION run_maintenance() RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_datetime_string text; v_current_partition_timestamp timestamp; v_last_partition_timestamp timestamp; v_premade_count int; v_row record; v_sql text; BEGIN v_sql := 'SELECT parent_table , type , part_interval::interval , control , last_partition FROM @extschema@.part_config where type = ''time-static'' or type = ''time-dynamic'''; FOR v_row IN SELECT parent_table , type , part_interval::interval , control , premake , datetime_string , last_partition FROM @extschema@.part_config WHERE type = 'time-static' OR type = 'time-dynamic' LOOP CASE WHEN v_row.part_interval = '15 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0); WHEN v_row.part_interval = '30 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0); WHEN v_row.part_interval = '1 hour' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP); WHEN v_row.part_interval = '1 day' THEN v_current_partition_timestamp := date_trunc('day', CURRENT_TIMESTAMP); WHEN v_row.part_interval = '1 week' THEN v_current_partition_timestamp := date_trunc('week', CURRENT_TIMESTAMP); WHEN v_row.part_interval = '1 month' THEN v_current_partition_timestamp := date_trunc('month', CURRENT_TIMESTAMP); WHEN v_row.part_interval = '1 year' THEN v_current_partition_timestamp := date_trunc('year', CURRENT_TIMESTAMP); END CASE; v_last_partition_timestamp := to_timestamp(substring(v_row.last_partition from char_length(v_row.parent_table||'_p')+1), v_row.datetime_string); -- Check and see how many premade partitions there are. If it's less than premake in config table, make another v_premade_count = EXTRACT('epoch' FROM (v_last_partition_timestamp - v_current_partition_timestamp)::interval) / EXTRACT('epoch' FROM v_row.part_interval::interval); IF v_premade_count < v_row.premake THEN RAISE NOTICE 'Creating next partition'; EXECUTE 'SELECT @extschema@.create_next_time_partition('||quote_literal(v_row.parent_table)||')'; IF v_row.type = 'time-static' THEN EXECUTE 'SELECT @extschema@.create_time_function('||quote_literal(v_row.parent_table)||')'; END IF; END IF; END LOOP; -- end of main loop END $$; pg_partman-2.2.2/updates/pg_partman--0.1.1--0.1.2.sql000066400000000000000000001066651262146621700214310ustar00rootroot00000000000000-- 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. ALTER TABLE @extschema@.part_config ALTER premake SET DEFAULT 4; CREATE OR REPLACE FUNCTION create_parent(p_parent_table text, p_control text, p_type @extschema@.partition_type, p_interval text, p_premake int DEFAULT 4, p_debug boolean DEFAULT false) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_current_id bigint; v_datetime_string text; v_id_interval bigint; v_job_id bigint; v_jobmon_schema text; v_last_partition_name text; v_old_search_path text; v_partition_time timestamp[]; v_partition_id bigint[]; v_max bigint; v_starting_partition_id bigint; v_step_id bigint; v_tablename text; v_time_interval interval; BEGIN SELECT tablename INTO v_tablename FROM pg_tables WHERE schemaname || '.' || tablename = p_parent_table; IF v_tablename IS NULL THEN RAISE EXCEPTION 'Please create given parent table first: %', p_parent_table; END IF; EXECUTE 'LOCK TABLE '||p_parent_table||' IN ACCESS EXCLUSIVE MODE'; SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN SETUP PARENT: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating initial partitions on new parent table: '||p_parent_table); END IF; CASE WHEN p_interval = 'yearly' THEN v_time_interval = '1 year'; v_datetime_string := 'YYYY'; WHEN p_interval = 'quarterly' THEN v_time_interval = '3 months'; v_datetime_string = 'YYYY"q"Q'; WHEN p_interval = 'monthly' THEN v_time_interval = '1 month'; v_datetime_string := 'YYYY_MM'; WHEN p_interval = 'weekly' THEN v_time_interval = '1 week'; v_datetime_string := 'IYYY"w"IW'; WHEN p_interval = 'daily' THEN v_time_interval = '1 day'; v_datetime_string := 'YYYY_MM_DD'; WHEN p_interval = 'hourly' THEN v_time_interval = '1 hour'; v_datetime_string := 'YYYY_MM_DD_HH24MI'; WHEN p_interval = 'half-hour' THEN v_time_interval = '30 mins'; v_datetime_string := 'YYYY_MM_DD_HH24MI'; WHEN p_interval = 'quarter-hour' THEN v_time_interval = '15 mins'; v_datetime_string := 'YYYY_MM_DD_HH24MI'; ELSE IF p_type = 'id-static' OR p_type = 'id-dynamic' THEN v_id_interval := p_interval::bigint; ELSE RAISE EXCEPTION 'Invalid interval for time based partitioning: %', p_interval; END IF; END CASE; IF p_type = 'time-static' OR p_type = 'time-dynamic' THEN FOR i IN 0..p_premake LOOP v_partition_time := array_append(v_partition_time, quote_literal(CURRENT_TIMESTAMP + (v_time_interval*i))::timestamp); END LOOP; EXECUTE 'SELECT @extschema@.create_time_partition('||quote_literal(p_parent_table)||','||quote_literal(p_control)||',' ||quote_literal(v_time_interval)||','||quote_literal(v_datetime_string)||','||quote_literal(v_partition_time)||')' INTO v_last_partition_name; INSERT INTO @extschema@.part_config (parent_table, type, part_interval, control, premake, datetime_string, last_partition) VALUES (p_parent_table, p_type, v_time_interval, p_control, p_premake, v_datetime_string, v_last_partition_name); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Time partitions premade: '||p_premake); END IF; END IF; IF p_type = 'id-static' OR p_type = 'id-dynamic' THEN -- If there is already data, start partitioning with the highest current value EXECUTE 'SELECT COALESCE(max('||p_control||')::bigint, 0) FROM '||p_parent_table||' LIMIT 1' INTO v_max; v_starting_partition_id := v_max - (v_max % v_id_interval); FOR i IN 0..p_premake LOOP v_partition_id = array_append(v_partition_id, (v_id_interval*i)+v_starting_partition_id); END LOOP; EXECUTE 'SELECT @extschema@.create_id_partition('||quote_literal(p_parent_table)||','||quote_literal(p_control)||',' ||v_id_interval||','||quote_literal(v_partition_id)||')' INTO v_last_partition_name; INSERT INTO @extschema@.part_config (parent_table, type, part_interval, control, premake, last_partition) VALUES (p_parent_table, p_type, v_id_interval, p_control, p_premake, v_last_partition_name); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'ID partitions premade: '||p_premake); END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Creating partition function'); END IF; IF p_type = 'time-static' OR p_type = 'time-dynamic' THEN EXECUTE 'SELECT @extschema@.create_time_function('||quote_literal(p_parent_table)||')'; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Time function created'); END IF; ELSIF p_type = 'id-static' OR p_type = 'id-dynamic' THEN v_current_id := COALESCE(v_max, 0); EXECUTE 'SELECT @extschema@.create_id_function('||quote_literal(p_parent_table)||','||v_current_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; EXECUTE 'SELECT @extschema@.create_trigger('||quote_literal(p_parent_table)||')'; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN CREATE PARENT: '||p_parent_table); v_step_id := add_step(v_job_id, 'Partition creation for table '||p_parent_table||' failed'); ELSIF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'BAD', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; CREATE OR REPLACE FUNCTION create_time_partition (p_parent_table text, p_control text, p_interval interval, p_datetime_string text, p_partition_times timestamp[]) RETURNS text LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_job_id bigint; v_jobmon_schema text; v_old_search_path text; v_partition_name text; v_partition_timestamp_end timestamp; v_partition_timestamp_start timestamp; v_quarter text; v_step_id bigint; v_tablename text; v_time timestamp; v_year text; BEGIN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; FOREACH v_time IN ARRAY p_partition_times LOOP v_partition_name := p_parent_table || '_p'; IF p_interval = '1 year' OR p_interval = '1 month' OR p_interval = '1 day' OR p_interval = '1 hour' OR p_interval = '30 mins' OR p_interval = '15 mins' THEN v_partition_name := v_partition_name || to_char(v_time, 'YYYY'); IF p_interval = '1 month' OR p_interval = '1 day' OR p_interval = '1 hour' OR p_interval = '30 mins' OR p_interval = '15 mins' THEN v_partition_name := v_partition_name || '_' || to_char(v_time, 'MM'); IF p_interval = '1 day' OR p_interval = '1 hour' OR p_interval = '30 mins' OR p_interval = '15 mins' THEN v_partition_name := v_partition_name || '_' || to_char(v_time, 'DD'); IF p_interval = '1 hour' OR p_interval = '30 mins' OR p_interval = '15 mins' THEN v_partition_name := v_partition_name || '_' || to_char(v_time, 'HH24'); IF p_interval <> '30 mins' AND p_interval <> '15 mins' THEN v_partition_name := v_partition_name || '00'; ELSIF p_interval = '15 mins' THEN IF date_part('minute', v_time) < 15 THEN v_partition_name := v_partition_name || '00'; ELSIF date_part('minute', v_time) >= 15 AND date_part('minute', v_time) < 30 THEN v_partition_name := v_partition_name || '15'; ELSIF date_part('minute', v_time) >= 30 AND date_part('minute', v_time) < 45 THEN v_partition_name := v_partition_name || '30'; ELSE v_partition_name := v_partition_name || '45'; END IF; ELSIF p_interval = '30 mins' THEN IF date_part('minute', v_time) < 30 THEN v_partition_name := v_partition_name || '00'; ELSE v_partition_name := v_partition_name || '30'; END IF; END IF; END IF; -- end hour IF END IF; -- end day IF END IF; -- end month IF ELSIF p_interval = '1 week' THEN v_partition_name := v_partition_name || to_char(v_time, 'IYYY') || 'w' || to_char(v_time, 'IW'); END IF; -- end year/week IF -- pull out datetime portion of last partition's tablename v_partition_timestamp_start := to_timestamp(substring(v_partition_name from char_length(p_parent_table||'_p')+1), p_datetime_string); v_partition_timestamp_end := to_timestamp(substring(v_partition_name from char_length(p_parent_table||'_p')+1), p_datetime_string) + p_interval; -- "Q" is ignored in to_timestamp, so handle special case IF p_interval = '3 months' THEN v_year := to_char(v_time, 'YYYY'); v_quarter := to_char(v_time, 'Q'); v_partition_name := v_partition_name || v_year || 'q' || v_quarter; CASE WHEN v_quarter = '1' THEN v_partition_timestamp_start := to_timestamp(v_year || '-01-01', 'YYYY-MM-DD'); WHEN v_quarter = '2' THEN v_partition_timestamp_start := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); WHEN v_quarter = '3' THEN v_partition_timestamp_start := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); WHEN v_quarter = '4' THEN v_partition_timestamp_start := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); END CASE; v_partition_timestamp_end := v_partition_timestamp_start + p_interval; END IF; SELECT schemaname ||'.'|| tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_tablename IS NOT NULL THEN CONTINUE; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating new partition '||v_partition_name||' with interval from '||v_partition_timestamp_start||' to '||(v_partition_timestamp_end-'1sec'::interval)); END IF; IF position('.' in p_parent_table) > 0 THEN v_tablename := substring(v_partition_name from position('.' in v_partition_name)+1); END IF; EXECUTE 'CREATE TABLE '||v_partition_name||' (LIKE '||p_parent_table||' INCLUDING DEFAULTS INCLUDING INDEXES)'; EXECUTE 'ALTER TABLE '||v_partition_name||' ADD CONSTRAINT '||v_tablename||'_partition_check CHECK ('||p_control||'>='||quote_literal(v_partition_timestamp_start)||' AND '||p_control||'<'||quote_literal(v_partition_timestamp_end)||')'; EXECUTE 'ALTER TABLE '||v_partition_name||' INHERIT '||p_parent_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); PERFORM close_job(v_job_id); END IF; END LOOP; IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_partition_name; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); v_step_id := add_step(v_job_id, 'Partition maintenance for table '||p_parent_table||' failed'); ELSIF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'BAD', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; CREATE OR REPLACE FUNCTION create_time_function(p_parent_table text) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_1st_partition_name text; v_1st_partition_timestamp timestamp; v_2nd_partition_name text; v_2nd_partition_timestamp timestamp; v_control text; v_current_partition_name text; v_current_partition_timestamp timestamp; v_datetime_string text; v_final_partition_timestamp timestamp; v_job_id bigint; v_jobmon_schema text; v_old_search_path text; v_part_interval interval; v_prev_partition_name text; v_prev_partition_timestamp timestamp; v_step_id bigint; v_trig_func text; v_type text; BEGIN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE FUNCTION: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating partition function for table '||p_parent_table); END IF; SELECT type , part_interval::interval , control , datetime_string FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic') INTO v_type, v_part_interval, v_control, v_datetime_string; IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF v_type = 'time-static' THEN CASE WHEN v_part_interval = '15 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0); WHEN v_part_interval = '30 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0); WHEN v_part_interval = '1 hour' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP); WHEN v_part_interval = '1 day' THEN v_current_partition_timestamp := date_trunc('day', CURRENT_TIMESTAMP); WHEN v_part_interval = '1 week' THEN v_current_partition_timestamp := date_trunc('week', CURRENT_TIMESTAMP); WHEN v_part_interval = '1 month' THEN v_current_partition_timestamp := date_trunc('month', CURRENT_TIMESTAMP); WHEN v_part_interval = '3 months' THEN v_current_partition_timestamp := date_trunc('quarter', CURRENT_TIMESTAMP); WHEN v_part_interval = '1 year' THEN v_current_partition_timestamp := date_trunc('year', CURRENT_TIMESTAMP); END CASE; v_prev_partition_timestamp := v_current_partition_timestamp - v_part_interval::interval; v_1st_partition_timestamp := v_current_partition_timestamp + v_part_interval::interval; v_2nd_partition_timestamp := v_1st_partition_timestamp + v_part_interval::interval; v_final_partition_timestamp := v_2nd_partition_timestamp + v_part_interval::interval; v_prev_partition_name := p_parent_table || '_p' || to_char(v_prev_partition_timestamp, v_datetime_string); v_current_partition_name := p_parent_table || '_p' || to_char(v_current_partition_timestamp, v_datetime_string); v_1st_partition_name := p_parent_table || '_p' || to_char(v_1st_partition_timestamp, v_datetime_string); v_2nd_partition_name := p_parent_table || '_p' || to_char(v_2nd_partition_timestamp, v_datetime_string); v_trig_func := 'CREATE OR REPLACE FUNCTION '||p_parent_table||'_part_trig_func() RETURNS trigger LANGUAGE plpgsql AS $t$ BEGIN IF TG_OP = ''INSERT'' THEN IF NEW.'||v_control||' >= '||quote_literal(v_current_partition_timestamp)||' AND NEW.'||v_control||' < '||quote_literal(v_1st_partition_timestamp)|| ' THEN INSERT INTO '||v_current_partition_name||' VALUES (NEW.*); ELSIF NEW.'||v_control||' >= '||quote_literal(v_1st_partition_timestamp)||' AND NEW.'||v_control||' < '||quote_literal(v_2nd_partition_timestamp)|| ' THEN INSERT INTO '||v_1st_partition_name||' VALUES (NEW.*); ELSIF NEW.'||v_control||' >= '||quote_literal(v_2nd_partition_timestamp)||' AND NEW.'||v_control||' < '||quote_literal(v_final_partition_timestamp)|| ' THEN INSERT INTO '||v_2nd_partition_name||' VALUES (NEW.*); ELSIF NEW.'||v_control||' >= '||quote_literal(v_prev_partition_timestamp)||' AND NEW.'||v_control||' < '||quote_literal(v_current_partition_timestamp)|| ' THEN INSERT INTO '||v_prev_partition_name||' VALUES (NEW.*); ELSE RETURN NEW; END IF; END IF; RETURN NULL; END $t$;'; -- RAISE NOTICE 'v_trig_func: %',v_trig_func; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Added function for current time interval: '||v_current_partition_timestamp||' to '||(v_1st_partition_timestamp-'1sec'::interval)); END IF; ELSIF v_type = 'time-dynamic' THEN v_trig_func := 'CREATE OR REPLACE FUNCTION '||p_parent_table||'_part_trig_func() RETURNS trigger LANGUAGE plpgsql AS $t$ DECLARE v_partition_name text; v_partition_timestamp timestamp; BEGIN IF TG_OP = ''INSERT'' THEN '; CASE WHEN v_part_interval = '15 mins' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''hour'', NEW.'||v_control||') + ''15min''::interval * floor(date_part(''minute'', NEW.'||v_control||') / 15.0);'; WHEN v_part_interval = '30 mins' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''hour'', NEW.'||v_control||') + ''30min''::interval * floor(date_part(''minute'', NEW.'||v_control||') / 30.0);'; WHEN v_part_interval = '1 hour' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''hour'', NEW.'||v_control||');'; WHEN v_part_interval = '1 day' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''day'', NEW.'||v_control||');'; WHEN v_part_interval = '1 week' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''week'', NEW.'||v_control||');'; WHEN v_part_interval = '1 month' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''month'', NEW.'||v_control||');'; WHEN v_part_interval = '3 months' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''quarter'', NEW.'||v_control||');'; WHEN v_part_interval = '1 year' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''year'', NEW.'||v_control||');'; END CASE; v_trig_func := v_trig_func||' v_partition_name := '''||p_parent_table||'_p''|| to_char(v_partition_timestamp, '||quote_literal(v_datetime_string)||'); EXECUTE ''INSERT INTO ''||v_partition_name||'' VALUES($1.*)'' USING NEW; END IF; RETURN NULL; END $t$;'; --RAISE NOTICE 'v_trig_func: %',v_trig_func; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Added function for dynamic time table: '||p_parent_table); END IF; ELSE RAISE EXCEPTION 'ERROR: Invalid time partitioning type given: %', v_type; END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN CREATE FUNCTION: '||p_parent_table); v_step_id := add_step(v_job_id, 'Partition function maintenance for table '||p_parent_table||' failed'); ELSIF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'BAD', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; CREATE OR REPLACE FUNCTION create_next_time_partition (p_parent_table text) RETURNS void LANGUAGE plpgsql AS $$ DECLARE v_control text; v_datetime_string text; v_last_partition text; v_next_partition_timestamp timestamp; v_next_year text; v_part_interval interval; v_quarter text; v_tablename text; v_type @extschema@.partition_type; v_year text; BEGIN SELECT type , part_interval::interval , control , datetime_string , last_partition FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic') INTO v_type, v_part_interval, v_control, v_datetime_string, v_last_partition; IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; -- Double check that last created partition exists IF v_last_partition IS NOT NULL THEN SELECT tablename INTO v_tablename FROM pg_tables WHERE schemaname || '.' || tablename = v_last_partition; IF v_tablename IS NULL THEN RAISE EXCEPTION 'ERROR: previous partition table missing. Unable to determine next proper partition in sequence.'; END IF; ELSE RAISE EXCEPTION 'ERROR: last known partition missing from config table for parent table %.', p_parent_table; END IF; -- pull out datetime portion of last partition's tablename to make the next one IF v_part_interval != '3 months' THEN v_next_partition_timestamp := to_timestamp(substring(v_last_partition from char_length(p_parent_table||'_p')+1), v_datetime_string) + v_part_interval; ELSE -- to_timestamp doesn't recognize 'Q' date string formater. Handle it v_year := split_part(substring(v_last_partition from char_length(p_parent_table||'_p')+1), 'q', 1); v_next_year := extract('year' from to_date(v_year, 'YYYY')+'1year'::interval); v_quarter := split_part(substring(v_last_partition from char_length(p_parent_table||'_p')+1), 'q', 2); CASE WHEN v_quarter = '1' THEN v_next_partition_timestamp := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); WHEN v_quarter = '2' THEN v_next_partition_timestamp := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); WHEN v_quarter = '3' THEN v_next_partition_timestamp := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); WHEN v_quarter = '4' THEN v_next_partition_timestamp := to_timestamp(v_next_year || '-01-01', 'YYYY-MM-DD'); END CASE; END IF; EXECUTE 'SELECT @extschema@.create_time_partition('||quote_literal(p_parent_table)||','||quote_literal(v_control)||','||quote_literal(v_part_interval)||',' ||quote_literal(v_datetime_string)||','||quote_literal(ARRAY[v_next_partition_timestamp])||')' INTO v_last_partition; IF v_last_partition IS NOT NULL THEN UPDATE @extschema@.part_config SET last_partition = v_last_partition WHERE parent_table = p_parent_table; END IF; END $$; CREATE OR REPLACE FUNCTION create_prev_time_partition(p_parent_table text, p_batch int DEFAULT 1) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_control text; v_datetime_string text; v_last_partition_name text; v_max_partition_timestamp timestamp; v_min_control timestamp; v_min_partition_timestamp timestamp; v_part_interval interval; v_partition_timestamp timestamp[]; v_rowcount bigint; v_sql text; v_total_rows bigint := 0; v_type text; BEGIN SELECT type , part_interval::interval , control , datetime_string FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic') INTO v_type, v_part_interval, v_control, v_datetime_string; IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; EXECUTE 'SELECT min('||v_control||') FROM ONLY '||p_parent_table INTO v_min_control; IF v_min_control IS NULL THEN RETURN 0; END IF; CASE WHEN v_part_interval = '15 mins' THEN v_min_partition_timestamp := date_trunc('hour', v_min_control) + '15min'::interval * floor(date_part('minute', v_min_control) / 15.0); WHEN v_part_interval = '30 mins' THEN v_min_partition_timestamp := date_trunc('hour', v_min_control) + '30min'::interval * floor(date_part('minute', v_min_control) / 30.0); WHEN v_part_interval = '1 hour' THEN v_min_partition_timestamp := date_trunc('hour', v_min_control); WHEN v_part_interval = '1 day' THEN v_min_partition_timestamp := date_trunc('day', v_min_control); WHEN v_part_interval = '1 week' THEN v_min_partition_timestamp := date_trunc('week', v_min_control); WHEN v_part_interval = '1 month' THEN v_min_partition_timestamp := date_trunc('month', v_min_control); WHEN v_part_interval = '3 months' THEN v_min_partition_timestamp := date_trunc('quarter', v_min_control); WHEN v_part_interval = '1 year' THEN v_min_partition_timestamp := date_trunc('year', v_min_control); END CASE; -- Subtract 1 so that batch count number actually makes sense FOR i IN 0..p_batch-1 LOOP v_partition_timestamp := ARRAY[(v_min_partition_timestamp + (v_part_interval*i))::timestamp]; RAISE NOTICE 'v_partition_timestamp: %',v_partition_timestamp; v_max_partition_timestamp := v_min_partition_timestamp + (v_part_interval*(i+1)); RAISE NOTICE 'v_max_partition_timestamp: %',v_max_partition_timestamp; v_sql := 'SELECT @extschema@.create_time_partition('||quote_literal(p_parent_table)||','||quote_literal(v_control)||',' ||quote_literal(v_part_interval)||','||quote_literal(v_datetime_string)||','||quote_literal(v_partition_timestamp)||')'; RAISE NOTICE 'v_sql: %', v_sql; EXECUTE v_sql INTO v_last_partition_name; -- create_time_partition() already checks to see if the partition exists and skips creation if it does. -- So this function will still work with already existing partitions to get all data moved out of parent table up to and including when -- pg_partman was used to set up partitioning. v_sql := 'WITH partition_data AS ( DELETE FROM ONLY '||p_parent_table||' WHERE '||v_control||' >= '||quote_literal(v_min_partition_timestamp)|| ' AND '||v_control||' < '||quote_literal(v_max_partition_timestamp)||' RETURNING *) INSERT INTO '||v_last_partition_name||' SELECT * FROM partition_data'; RAISE NOTICE 'v_sql: %', v_sql; EXECUTE v_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total_rows := v_total_rows + v_rowcount; IF v_rowcount = 0 THEN EXIT; END IF; END LOOP; RETURN v_total_rows; END $$; CREATE OR REPLACE FUNCTION run_maintenance() RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_count int := 0; v_current_partition_timestamp timestamp; v_datetime_string text; v_job_id bigint; v_jobmon_schema text; v_last_partition_timestamp timestamp; v_old_search_path text; v_premade_count real; v_quarter text; v_step_id bigint; v_row record; v_year text; BEGIN v_adv_lock := pg_try_advisory_lock(hashtext('pg_partman run_maintenance')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'Partman maintenance already running.'; RETURN; END IF; SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN RUN MAINTENANCE'); v_step_id := add_step(v_job_id, 'Running maintenance loop'); END IF; FOR v_row IN SELECT parent_table , type , part_interval::interval , control , premake , datetime_string , last_partition FROM @extschema@.part_config WHERE type = 'time-static' OR type = 'time-dynamic' LOOP CASE WHEN v_row.part_interval = '15 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0); WHEN v_row.part_interval = '30 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0); WHEN v_row.part_interval = '1 hour' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP); WHEN v_row.part_interval = '1 day' THEN v_current_partition_timestamp := date_trunc('day', CURRENT_TIMESTAMP); WHEN v_row.part_interval = '1 week' THEN v_current_partition_timestamp := date_trunc('week', CURRENT_TIMESTAMP); WHEN v_row.part_interval = '1 month' THEN v_current_partition_timestamp := date_trunc('month', CURRENT_TIMESTAMP); WHEN v_row.part_interval = '3 months' THEN v_current_partition_timestamp := date_trunc('quarter', CURRENT_TIMESTAMP); WHEN v_row.part_interval = '1 year' THEN v_current_partition_timestamp := date_trunc('year', CURRENT_TIMESTAMP); END CASE; IF v_row.part_interval != '3 months' THEN v_last_partition_timestamp := to_timestamp(substring(v_row.last_partition from char_length(v_row.parent_table||'_p')+1), v_row.datetime_string); ELSE -- to_timestamp doesn't recognize 'Q' date string formater. Handle it v_year := split_part(substring(v_row.last_partition from char_length(v_row.parent_table||'_p')+1), 'q', 1); v_quarter := split_part(substring(v_row.last_partition from char_length(v_row.parent_table||'_p')+1), 'q', 2); CASE WHEN v_quarter = '1' THEN v_last_partition_timestamp := to_timestamp(v_year || '-01-01', 'YYYY-MM-DD'); WHEN v_quarter = '2' THEN v_last_partition_timestamp := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); WHEN v_quarter = '3' THEN v_last_partition_timestamp := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); WHEN v_quarter = '4' THEN v_last_partition_timestamp := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); END CASE; END IF; -- Check and see how many premade partitions there are. If it's less than premake in config table, make another v_premade_count = EXTRACT('epoch' FROM (v_last_partition_timestamp - v_current_partition_timestamp)::interval) / EXTRACT('epoch' FROM v_row.part_interval::interval); IF v_premade_count < v_row.premake THEN RAISE NOTICE 'Creating next partition'; EXECUTE 'SELECT @extschema@.create_next_time_partition('||quote_literal(v_row.parent_table)||')'; IF v_row.type = 'time-static' THEN EXECUTE 'SELECT @extschema@.create_time_function('||quote_literal(v_row.parent_table)||')'; END IF; END IF; v_count := v_count + 1; END LOOP; -- end of main loop IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Partition maintenance finished. '||v_count||' partitons made'); PERFORM close_job(v_job_id); END IF; PERFORM pg_advisory_unlock(hashtext('pg_partman run_maintenance')); EXCEPTION WHEN QUERY_CANCELED THEN PERFORM pg_advisory_unlock(hashtext('pg_partman run_maintenance')); RAISE EXCEPTION '%', SQLERRM; WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN RUN MAINTENANCE'); v_step_id := add_step(v_job_id, 'EXCEPTION before job logging started'); END IF; IF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'CRITICAL', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; PERFORM pg_advisory_unlock(hashtext('pg_partman run_maintenance')); RAISE EXCEPTION '%', SQLERRM; END $$; pg_partman-2.2.2/updates/pg_partman--0.1.2--0.2.0.sql000066400000000000000000000505761262146621700214300ustar00rootroot00000000000000-- 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 UPDATE @extschema@.part_config SET retention = NULL; ALTER TABLE @extschema@.part_config ALTER retention TYPE text; ALTER TABLE @extschema@.part_config ADD retention_keep_table boolean NOT NULL DEFAULT true; ALTER TABLE @extschema@.part_config ADD retention_keep_index boolean NOT NULL DEFAULT true; /* * Function to drop child tables from a time-based partition set. Options to drop indexes or actually drop the table from the database. */ CREATE FUNCTION drop_id_partition(p_parent_table text, p_keep_table boolean DEFAULT NULL, p_keep_index boolean DEFAULT NULL) RETURNS int LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_child_table text; v_control text; v_drop_count int := 0; v_index record; v_job_id bigint; v_jobmon_schema text; v_max bigint; v_old_search_path text; v_part_interval bigint; v_partition_id bigint; v_retention bigint; v_retention_keep_index boolean; v_retention_keep_table boolean; v_step_id bigint; BEGIN v_adv_lock := pg_try_advisory_lock(hashtext('pg_partman drop_id_partition')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'drop_id_partition already running.'; RETURN 0; END IF; SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; SELECT part_interval::bigint , control , retention::bigint , retention_keep_table , retention_keep_index INTO v_part_interval , v_control , v_retention , v_retention_keep_table , v_retention_keep_index FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic') AND retention IS NOT NULL; IF v_part_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; -- Allow override of keeping tables or indexes from input parameters IF p_keep_table IS NOT NULL THEN v_retention_keep_table = p_keep_table; END IF; IF p_keep_index IS NOT NULL THEN v_retention_keep_index = p_keep_index; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN DROP ID PARTITION: '|| p_parent_table); END IF; EXECUTE 'SELECT max('||v_control||') FROM '||p_parent_table INTO v_max; -- Loop through child tables of the given parent FOR v_child_table IN SELECT inhrelid::regclass FROM pg_catalog.pg_inherits WHERE inhparent::regclass = p_parent_table::regclass ORDER BY inhrelid::regclass ASC LOOP v_partition_id := substring(v_child_table from char_length(p_parent_table||'_p')+1)::bigint; -- Add one interval since partition names contain the start of the constraint period IF v_retention <= (v_max - (v_partition_id + v_part_interval)) THEN IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Uninherit table '||v_child_table||' from '||p_parent_table); END IF; EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; IF v_retention_keep_table = false THEN IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Drop table '||v_child_table); END IF; EXECUTE 'DROP TABLE '||v_child_table; 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 FOR v_index IN SELECT i.indexrelid::regclass AS name , c.conname FROM pg_catalog.pg_index i LEFT JOIN pg_catalog.pg_constraint c ON i.indexrelid = c.conindid WHERE i.indrelid = v_child_table::regclass LOOP IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Drop index '||v_index.name||' from '||v_child_table); END IF; IF v_index.conname IS NOT NULL THEN EXECUTE 'ALTER TABLE '||v_child_table||' DROP CONSTRAINT '||v_index.conname; ELSE EXECUTE 'DROP INDEX '||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; v_drop_count := v_drop_count + 1; END IF; -- End retention check IF END LOOP; -- End child table loop IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Finished partition drop maintenance'); PERFORM update_step(v_step_id, 'OK', v_drop_count||' partitions dropped.'); PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; PERFORM pg_advisory_unlock(hashtext('pg_partman drop_id_partition')); RETURN v_drop_count; EXCEPTION WHEN QUERY_CANCELED THEN PERFORM pg_advisory_unlock(hashtext('pg_partman drop_id_partition')); RAISE EXCEPTION '%', SQLERRM; WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN DROP ID PARTITION'); v_step_id := add_step(v_job_id, 'EXCEPTION before job logging started'); END IF; IF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'CRITICAL', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; PERFORM pg_advisory_unlock(hashtext('pg_partman drop_id_partition')); RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to drop child tables from a time-based partition set. Options to drop indexes or actually drop the table from the database. */ CREATE FUNCTION drop_time_partition(p_parent_table text, p_keep_table boolean DEFAULT NULL, p_keep_index boolean DEFAULT NULL) RETURNS int LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_child_table text; v_datetime_string text; v_drop_count int := 0; v_index record; v_job_id bigint; v_jobmon_schema text; v_old_search_path text; v_part_interval interval; v_partition_timestamp timestamp; v_quarter text; v_retention interval; v_retention_keep_index boolean; v_retention_keep_table boolean; v_step_id bigint; v_year text; BEGIN v_adv_lock := pg_try_advisory_lock(hashtext('pg_partman drop_time_partition')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'drop_time_partition already running.'; RETURN 0; END IF; SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; SELECT part_interval::interval , retention::interval , retention_keep_table , retention_keep_index , datetime_string INTO v_part_interval , v_retention , v_retention_keep_table , v_retention_keep_index , v_datetime_string FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic') AND retention IS NOT NULL; IF v_part_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; -- Allow override of keeping tables or indexes from input parameters IF p_keep_table IS NOT NULL THEN v_retention_keep_table = p_keep_table; END IF; IF p_keep_index IS NOT NULL THEN v_retention_keep_index = p_keep_index; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN DROP TIME PARTITION: '|| p_parent_table); END IF; -- Loop through child tables of the given parent FOR v_child_table IN SELECT inhrelid::regclass FROM pg_catalog.pg_inherits WHERE inhparent::regclass = p_parent_table::regclass ORDER BY inhrelid::regclass ASC LOOP -- pull out datetime portion of last partition's tablename to make the next one IF v_part_interval != '3 months' THEN v_partition_timestamp := to_timestamp(substring(v_child_table from char_length(p_parent_table||'_p')+1), v_datetime_string); ELSE -- to_timestamp doesn't recognize 'Q' date string formater. Handle it v_year := split_part(substring(v_child_table from char_length(p_parent_table||'_p')+1), 'q', 1); v_quarter := split_part(substring(v_child_table from char_length(p_parent_table||'_p')+1), 'q', 2); CASE WHEN v_quarter = '1' THEN v_partition_timestamp := to_timestamp(v_year || '-01-01', 'YYYY-MM-DD'); WHEN v_quarter = '2' THEN v_partition_timestamp := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); WHEN v_quarter = '3' THEN v_partition_timestamp := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); WHEN v_quarter = '4' THEN v_partition_timestamp := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); END CASE; END IF; -- Add one interval since partition names contain the start of the constraint period IF v_retention < (CURRENT_TIMESTAMP - (v_partition_timestamp + v_part_interval)) THEN IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Uninherit table '||v_child_table||' from '||p_parent_table); END IF; EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; IF v_retention_keep_table = false THEN IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Drop table '||v_child_table); END IF; EXECUTE 'DROP TABLE '||v_child_table; 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 FOR v_index IN SELECT i.indexrelid::regclass AS name , c.conname FROM pg_catalog.pg_index i LEFT JOIN pg_catalog.pg_constraint c ON i.indexrelid = c.conindid WHERE i.indrelid = v_child_table::regclass LOOP IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Drop index '||v_index.name||' from '||v_child_table); END IF; IF v_index.conname IS NOT NULL THEN EXECUTE 'ALTER TABLE '||v_child_table||' DROP CONSTRAINT '||v_index.conname; ELSE EXECUTE 'DROP INDEX '||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; v_drop_count := v_drop_count + 1; END IF; -- End retention check IF END LOOP; -- End child table loop IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Finished partition drop maintenance'); PERFORM update_step(v_step_id, 'OK', v_drop_count||' partitions dropped.'); PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; PERFORM pg_advisory_unlock(hashtext('pg_partman drop_time_partition')); RETURN v_drop_count; EXCEPTION WHEN QUERY_CANCELED THEN PERFORM pg_advisory_unlock(hashtext('pg_partman drop_time_partition')); RAISE EXCEPTION '%', SQLERRM; WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN DROP TIME PARTITION'); v_step_id := add_step(v_job_id, 'EXCEPTION before job logging started'); END IF; IF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'CRITICAL', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; PERFORM pg_advisory_unlock(hashtext('pg_partman drop_time_partition')); RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to manage pre-creation of the next partitions in a time-based partition set * Also manages dropping old partitions if the retention option is set */ CREATE OR REPLACE FUNCTION run_maintenance() RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_create_count int := 0; v_current_partition_timestamp timestamp; v_datetime_string text; v_drop_count int := 0; v_job_id bigint; v_jobmon_schema text; v_last_partition_timestamp timestamp; v_old_search_path text; v_premade_count real; v_quarter text; v_step_id bigint; v_row record; v_year text; BEGIN v_adv_lock := pg_try_advisory_lock(hashtext('pg_partman run_maintenance')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'Partman maintenance already running.'; RETURN; END IF; SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN RUN MAINTENANCE'); v_step_id := add_step(v_job_id, 'Running maintenance loop'); END IF; FOR v_row IN SELECT parent_table , type , part_interval::interval , control , premake , datetime_string , last_partition FROM @extschema@.part_config WHERE type = 'time-static' OR type = 'time-dynamic' LOOP CASE WHEN v_row.part_interval = '15 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0); WHEN v_row.part_interval = '30 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0); WHEN v_row.part_interval = '1 hour' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP); WHEN v_row.part_interval = '1 day' THEN v_current_partition_timestamp := date_trunc('day', CURRENT_TIMESTAMP); WHEN v_row.part_interval = '1 week' THEN v_current_partition_timestamp := date_trunc('week', CURRENT_TIMESTAMP); WHEN v_row.part_interval = '1 month' THEN v_current_partition_timestamp := date_trunc('month', CURRENT_TIMESTAMP); WHEN v_row.part_interval = '3 months' THEN v_current_partition_timestamp := date_trunc('quarter', CURRENT_TIMESTAMP); WHEN v_row.part_interval = '1 year' THEN v_current_partition_timestamp := date_trunc('year', CURRENT_TIMESTAMP); END CASE; IF v_row.part_interval != '3 months' THEN v_last_partition_timestamp := to_timestamp(substring(v_row.last_partition from char_length(v_row.parent_table||'_p')+1), v_row.datetime_string); ELSE -- to_timestamp doesn't recognize 'Q' date string formater. Handle it v_year := split_part(substring(v_row.last_partition from char_length(v_row.parent_table||'_p')+1), 'q', 1); v_quarter := split_part(substring(v_row.last_partition from char_length(v_row.parent_table||'_p')+1), 'q', 2); CASE WHEN v_quarter = '1' THEN v_last_partition_timestamp := to_timestamp(v_year || '-01-01', 'YYYY-MM-DD'); WHEN v_quarter = '2' THEN v_last_partition_timestamp := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); WHEN v_quarter = '3' THEN v_last_partition_timestamp := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); WHEN v_quarter = '4' THEN v_last_partition_timestamp := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); END CASE; END IF; -- Check and see how many premade partitions there are. If it's less than premake in config table, make another v_premade_count = EXTRACT('epoch' FROM (v_last_partition_timestamp - v_current_partition_timestamp)::interval) / EXTRACT('epoch' FROM v_row.part_interval::interval); IF v_premade_count < v_row.premake THEN EXECUTE 'SELECT @extschema@.create_next_time_partition('||quote_literal(v_row.parent_table)||')'; v_create_count := v_create_count + 1; IF v_row.type = 'time-static' THEN EXECUTE 'SELECT @extschema@.create_time_function('||quote_literal(v_row.parent_table)||')'; END IF; END IF; END LOOP; -- end of creation loop -- Manage dropping old partitions if retention option is set FOR v_row IN SELECT parent_table FROM @extschema@.part_config WHERE retention IS NOT NULL AND (type = 'time-static' OR type = 'time-dynamic') LOOP v_drop_count := v_drop_count + @extschema@.drop_time_partition(v_row.parent_table); END LOOP; FOR v_row IN SELECT parent_table FROM @extschema@.part_config WHERE retention IS NOT NULL AND (type = 'id-static' OR type = 'id-dynamic') LOOP v_drop_count := v_drop_count + @extschema@.drop_id_partition(v_row.parent_table); END LOOP; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Partition maintenance finished. '||v_create_count||' partitons made. '||v_drop_count||' partitions dropped.'); PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; PERFORM pg_advisory_unlock(hashtext('pg_partman run_maintenance')); EXCEPTION WHEN QUERY_CANCELED THEN PERFORM pg_advisory_unlock(hashtext('pg_partman run_maintenance')); RAISE EXCEPTION '%', SQLERRM; WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN RUN MAINTENANCE'); v_step_id := add_step(v_job_id, 'EXCEPTION before job logging started'); END IF; IF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'CRITICAL', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; PERFORM pg_advisory_unlock(hashtext('pg_partman run_maintenance')); RAISE EXCEPTION '%', SQLERRM; END $$; pg_partman-2.2.2/updates/pg_partman--0.2.0--0.3.0.sql000066400000000000000000000265041262146621700214220ustar00rootroot00000000000000-- 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. CREATE TABLE part_grants ( parent_table text PRIMARY KEY REFERENCES @extschema@.part_config (parent_table) ON DELETE CASCADE ON UPDATE CASCADE, grants text, roles text ); /* * Function to apply grants on parent & child tables */ CREATE FUNCTION apply_grants(p_parent_table text) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_child_table text; v_grants text; v_roles text; BEGIN SELECT grants, roles INTO v_grants, v_roles FROM @extschema@.part_grants WHERE parent_table = p_parent_table; IF v_grants IS NOT NULL AND v_roles IS NOT NULL THEN EXECUTE 'GRANT '||v_grants||' ON '||p_parent_table||' TO '||v_roles; FOR v_child_table IN SELECT inhrelid::regclass FROM pg_catalog.pg_inherits WHERE inhparent::regclass = p_parent_table::regclass ORDER BY inhrelid::regclass ASC LOOP EXECUTE 'GRANT '||v_grants||' ON TABLE '||v_child_table||' TO '||v_roles; END LOOP; END IF; END $$; /* * Function to create id partitions */ CREATE OR REPLACE FUNCTION create_id_partition (p_parent_table text, p_control text, p_interval bigint, p_partition_ids bigint[]) RETURNS text LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_job_id bigint; v_jobmon_schema text; v_old_search_path text; v_partition_name text; v_step_id bigint; v_tablename text; v_id bigint; BEGIN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; FOREACH v_id IN ARRAY p_partition_ids LOOP v_partition_name := p_parent_table||'_p'||v_id; SELECT schemaname ||'.'|| tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_tablename IS NOT NULL THEN CONTINUE; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating new partition '||v_partition_name||' with interval from '||v_id||' to '||(v_id + p_interval)-1); END IF; IF position('.' in p_parent_table) > 0 THEN v_tablename := substring(v_partition_name from position('.' in v_partition_name)+1); END IF; EXECUTE 'CREATE TABLE '||v_partition_name||' (LIKE '||p_parent_table||' INCLUDING DEFAULTS INCLUDING INDEXES)'; EXECUTE 'ALTER TABLE '||v_partition_name||' ADD CONSTRAINT '||v_tablename||'_partition_check CHECK ('||p_control||'>='||quote_literal(v_id)||' AND '||p_control||'<'||quote_literal(v_id + p_interval)||')'; EXECUTE 'ALTER TABLE '||v_partition_name||' INHERIT '||p_parent_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); PERFORM close_job(v_job_id); END IF; END LOOP; -- Apply grants if configured PERFORM @extschema@.apply_grants(p_parent_table); IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_partition_name; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); v_step_id := add_step(v_job_id, 'Partition maintenance for table '||p_parent_table||' failed'); ELSIF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'BAD', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to create a child table in a time-based partition set */ CREATE OR REPLACE FUNCTION create_time_partition (p_parent_table text, p_control text, p_interval interval, p_datetime_string text, p_partition_times timestamp[]) RETURNS text LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_job_id bigint; v_jobmon_schema text; v_old_search_path text; v_partition_name text; v_partition_timestamp_end timestamp; v_partition_timestamp_start timestamp; v_quarter text; v_step_id bigint; v_tablename text; v_time timestamp; v_year text; BEGIN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; FOREACH v_time IN ARRAY p_partition_times LOOP v_partition_name := p_parent_table || '_p'; IF p_interval = '1 year' OR p_interval = '1 month' OR p_interval = '1 day' OR p_interval = '1 hour' OR p_interval = '30 mins' OR p_interval = '15 mins' THEN v_partition_name := v_partition_name || to_char(v_time, 'YYYY'); IF p_interval = '1 month' OR p_interval = '1 day' OR p_interval = '1 hour' OR p_interval = '30 mins' OR p_interval = '15 mins' THEN v_partition_name := v_partition_name || '_' || to_char(v_time, 'MM'); IF p_interval = '1 day' OR p_interval = '1 hour' OR p_interval = '30 mins' OR p_interval = '15 mins' THEN v_partition_name := v_partition_name || '_' || to_char(v_time, 'DD'); IF p_interval = '1 hour' OR p_interval = '30 mins' OR p_interval = '15 mins' THEN v_partition_name := v_partition_name || '_' || to_char(v_time, 'HH24'); IF p_interval <> '30 mins' AND p_interval <> '15 mins' THEN v_partition_name := v_partition_name || '00'; ELSIF p_interval = '15 mins' THEN IF date_part('minute', v_time) < 15 THEN v_partition_name := v_partition_name || '00'; ELSIF date_part('minute', v_time) >= 15 AND date_part('minute', v_time) < 30 THEN v_partition_name := v_partition_name || '15'; ELSIF date_part('minute', v_time) >= 30 AND date_part('minute', v_time) < 45 THEN v_partition_name := v_partition_name || '30'; ELSE v_partition_name := v_partition_name || '45'; END IF; ELSIF p_interval = '30 mins' THEN IF date_part('minute', v_time) < 30 THEN v_partition_name := v_partition_name || '00'; ELSE v_partition_name := v_partition_name || '30'; END IF; END IF; END IF; -- end hour IF END IF; -- end day IF END IF; -- end month IF ELSIF p_interval = '1 week' THEN v_partition_name := v_partition_name || to_char(v_time, 'IYYY') || 'w' || to_char(v_time, 'IW'); END IF; -- end year/week IF -- pull out datetime portion of last partition's tablename v_partition_timestamp_start := to_timestamp(substring(v_partition_name from char_length(p_parent_table||'_p')+1), p_datetime_string); v_partition_timestamp_end := to_timestamp(substring(v_partition_name from char_length(p_parent_table||'_p')+1), p_datetime_string) + p_interval; -- "Q" is ignored in to_timestamp, so handle special case IF p_interval = '3 months' THEN v_year := to_char(v_time, 'YYYY'); v_quarter := to_char(v_time, 'Q'); v_partition_name := v_partition_name || v_year || 'q' || v_quarter; CASE WHEN v_quarter = '1' THEN v_partition_timestamp_start := to_timestamp(v_year || '-01-01', 'YYYY-MM-DD'); WHEN v_quarter = '2' THEN v_partition_timestamp_start := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); WHEN v_quarter = '3' THEN v_partition_timestamp_start := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); WHEN v_quarter = '4' THEN v_partition_timestamp_start := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); END CASE; v_partition_timestamp_end := v_partition_timestamp_start + p_interval; END IF; SELECT schemaname ||'.'|| tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_tablename IS NOT NULL THEN CONTINUE; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating new partition '||v_partition_name||' with interval from '||v_partition_timestamp_start||' to '||(v_partition_timestamp_end-'1sec'::interval)); END IF; IF position('.' in p_parent_table) > 0 THEN v_tablename := substring(v_partition_name from position('.' in v_partition_name)+1); END IF; EXECUTE 'CREATE TABLE '||v_partition_name||' (LIKE '||p_parent_table||' INCLUDING DEFAULTS INCLUDING INDEXES)'; EXECUTE 'ALTER TABLE '||v_partition_name||' ADD CONSTRAINT '||v_tablename||'_partition_check CHECK ('||p_control||'>='||quote_literal(v_partition_timestamp_start)||' AND '||p_control||'<'||quote_literal(v_partition_timestamp_end)||')'; EXECUTE 'ALTER TABLE '||v_partition_name||' INHERIT '||p_parent_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); PERFORM close_job(v_job_id); END IF; END LOOP; -- Apply grants if configured PERFORM @extschema@.apply_grants(p_parent_table); IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_partition_name; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); v_step_id := add_step(v_job_id, 'Partition maintenance for table '||p_parent_table||' failed'); ELSIF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'BAD', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; pg_partman-2.2.2/updates/pg_partman--0.3.0--0.3.1.sql000066400000000000000000000461601262146621700214240ustar00rootroot00000000000000-- 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. /* * Create the trigger function for the parent table of a time-based partition set */ CREATE OR REPLACE FUNCTION create_time_function(p_parent_table text) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_1st_partition_name text; v_1st_partition_timestamp timestamp; v_2nd_partition_name text; v_2nd_partition_timestamp timestamp; v_control text; v_current_partition_name text; v_current_partition_timestamp timestamp; v_datetime_string text; v_final_partition_timestamp timestamp; v_job_id bigint; v_jobmon_schema text; v_old_search_path text; v_part_interval interval; v_prev_partition_name text; v_prev_partition_timestamp timestamp; v_step_id bigint; v_trig_func text; v_type text; BEGIN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE FUNCTION: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating partition function for table '||p_parent_table); END IF; SELECT type , part_interval::interval , control , datetime_string FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic') INTO v_type, v_part_interval, v_control, v_datetime_string; IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF v_type = 'time-static' THEN CASE WHEN v_part_interval = '15 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0); WHEN v_part_interval = '30 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0); WHEN v_part_interval = '1 hour' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP); WHEN v_part_interval = '1 day' THEN v_current_partition_timestamp := date_trunc('day', CURRENT_TIMESTAMP); WHEN v_part_interval = '1 week' THEN v_current_partition_timestamp := date_trunc('week', CURRENT_TIMESTAMP); WHEN v_part_interval = '1 month' THEN v_current_partition_timestamp := date_trunc('month', CURRENT_TIMESTAMP); WHEN v_part_interval = '3 months' THEN v_current_partition_timestamp := date_trunc('quarter', CURRENT_TIMESTAMP); WHEN v_part_interval = '1 year' THEN v_current_partition_timestamp := date_trunc('year', CURRENT_TIMESTAMP); END CASE; v_prev_partition_timestamp := v_current_partition_timestamp - v_part_interval::interval; v_1st_partition_timestamp := v_current_partition_timestamp + v_part_interval::interval; v_2nd_partition_timestamp := v_1st_partition_timestamp + v_part_interval::interval; v_final_partition_timestamp := v_2nd_partition_timestamp + v_part_interval::interval; v_prev_partition_name := p_parent_table || '_p' || to_char(v_prev_partition_timestamp, v_datetime_string); v_current_partition_name := p_parent_table || '_p' || to_char(v_current_partition_timestamp, v_datetime_string); v_1st_partition_name := p_parent_table || '_p' || to_char(v_1st_partition_timestamp, v_datetime_string); v_2nd_partition_name := p_parent_table || '_p' || to_char(v_2nd_partition_timestamp, v_datetime_string); v_trig_func := 'CREATE OR REPLACE FUNCTION '||p_parent_table||'_part_trig_func() RETURNS trigger LANGUAGE plpgsql AS $t$ BEGIN IF TG_OP = ''INSERT'' THEN IF NEW.'||v_control||' >= '||quote_literal(v_current_partition_timestamp)||' AND NEW.'||v_control||' < '||quote_literal(v_1st_partition_timestamp)|| ' THEN INSERT INTO '||v_current_partition_name||' VALUES (NEW.*); ELSIF NEW.'||v_control||' >= '||quote_literal(v_1st_partition_timestamp)||' AND NEW.'||v_control||' < '||quote_literal(v_2nd_partition_timestamp)|| ' THEN INSERT INTO '||v_1st_partition_name||' VALUES (NEW.*); ELSIF NEW.'||v_control||' >= '||quote_literal(v_2nd_partition_timestamp)||' AND NEW.'||v_control||' < '||quote_literal(v_final_partition_timestamp)|| ' THEN INSERT INTO '||v_2nd_partition_name||' VALUES (NEW.*); ELSIF NEW.'||v_control||' >= '||quote_literal(v_prev_partition_timestamp)||' AND NEW.'||v_control||' < '||quote_literal(v_current_partition_timestamp)|| ' THEN INSERT INTO '||v_prev_partition_name||' VALUES (NEW.*); ELSE RETURN NEW; END IF; END IF; RETURN NULL; END $t$;'; -- RAISE NOTICE 'v_trig_func: %',v_trig_func; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Added function for current time interval: '||v_current_partition_timestamp||' to '||(v_1st_partition_timestamp-'1sec'::interval)); END IF; ELSIF v_type = 'time-dynamic' THEN v_trig_func := 'CREATE OR REPLACE FUNCTION '||p_parent_table||'_part_trig_func() RETURNS trigger LANGUAGE plpgsql AS $t$ DECLARE v_count int; v_partition_name text; v_partition_timestamp timestamp; v_schemaname text; v_tablename text; BEGIN IF TG_OP = ''INSERT'' THEN '; CASE WHEN v_part_interval = '15 mins' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''hour'', NEW.'||v_control||') + ''15min''::interval * floor(date_part(''minute'', NEW.'||v_control||') / 15.0);'; WHEN v_part_interval = '30 mins' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''hour'', NEW.'||v_control||') + ''30min''::interval * floor(date_part(''minute'', NEW.'||v_control||') / 30.0);'; WHEN v_part_interval = '1 hour' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''hour'', NEW.'||v_control||');'; WHEN v_part_interval = '1 day' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''day'', NEW.'||v_control||');'; WHEN v_part_interval = '1 week' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''week'', NEW.'||v_control||');'; WHEN v_part_interval = '1 month' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''month'', NEW.'||v_control||');'; WHEN v_part_interval = '3 months' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''quarter'', NEW.'||v_control||');'; WHEN v_part_interval = '1 year' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''year'', NEW.'||v_control||');'; END CASE; v_trig_func := v_trig_func||' v_partition_name := '''||p_parent_table||'_p''|| to_char(v_partition_timestamp, '||quote_literal(v_datetime_string)||'); v_schemaname := split_part(v_partition_name, ''.'', 1); v_tablename := split_part(v_partition_name, ''.'', 2); SELECT count(*) INTO v_count FROM pg_tables WHERE schemaname = v_schemaname AND tablename = v_tablename; IF v_count > 0 THEN EXECUTE ''INSERT INTO ''||v_partition_name||'' VALUES($1.*)'' USING NEW; ELSE RETURN NEW; END IF; END IF; RETURN NULL; END $t$;'; --RAISE NOTICE 'v_trig_func: %',v_trig_func; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Added function for dynamic time table: '||p_parent_table); END IF; ELSE RAISE EXCEPTION 'ERROR: Invalid time partitioning type given: %', v_type; END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN CREATE FUNCTION: '||p_parent_table); v_step_id := add_step(v_job_id, 'Partition function maintenance for table '||p_parent_table||' failed'); ELSIF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'BAD', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Create the trigger function for the parent table of an id-based partition set */ CREATE OR REPLACE FUNCTION create_id_function(p_parent_table text, p_current_id bigint) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_1st_partition_name text; v_1st_partition_id bigint; v_2nd_partition_name text; v_2nd_partition_id bigint; v_control text; v_current_partition_name text; v_current_partition_id bigint; v_datetime_string text; v_final_partition_id bigint; v_job_id bigint; v_jobmon_schema text; v_last_partition text; v_old_search_path text; v_part_interval bigint; v_premake int; v_prev_partition_name text; v_prev_partition_id bigint; v_step_id bigint; v_trig_func text; v_type text; BEGIN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE FUNCTION: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating partition function for table '||p_parent_table); END IF; SELECT type , part_interval::bigint , control , premake , last_partition FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic') INTO v_type, v_part_interval, v_control, v_premake, v_last_partition; IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF v_type = 'id-static' THEN v_current_partition_id := p_current_id - (p_current_id % v_part_interval); v_prev_partition_id := v_current_partition_id - v_part_interval; v_1st_partition_id := v_current_partition_id + v_part_interval; v_2nd_partition_id := v_1st_partition_id + v_part_interval; v_final_partition_id := v_2nd_partition_id + v_part_interval; v_prev_partition_name := p_parent_table || '_p' || v_prev_partition_id::text; v_current_partition_name := p_parent_table || '_p' || v_current_partition_id::text; v_1st_partition_name := p_parent_table || '_p' || v_1st_partition_id::text; v_2nd_partition_name := p_parent_table || '_p' || v_2nd_partition_id::text; v_trig_func := 'CREATE OR REPLACE FUNCTION '||p_parent_table||'_part_trig_func() RETURNS trigger LANGUAGE plpgsql AS $t$ DECLARE v_current_partition_id bigint; v_last_partition text := '||quote_literal(v_last_partition)||'; v_next_partition_id bigint; v_next_partition_name text; BEGIN IF TG_OP = ''INSERT'' THEN IF NEW.'||v_control||' >= '||v_current_partition_id||' AND NEW.'||v_control||' < '||v_1st_partition_id|| ' THEN INSERT INTO '||v_current_partition_name||' VALUES (NEW.*); ELSIF NEW.'||v_control||' >= '||v_1st_partition_id||' AND NEW.'||v_control||' < '||v_2nd_partition_id|| ' THEN INSERT INTO '||v_1st_partition_name||' VALUES (NEW.*); ELSIF NEW.'||v_control||' >= '||v_2nd_partition_id||' AND NEW.'||v_control||' < '||quote_literal(v_final_partition_id)|| ' THEN INSERT INTO '||v_2nd_partition_name||' VALUES (NEW.*); '; -- If the first partition's function, don't have rule for previous partition IF v_prev_partition_id >= 0 THEN v_trig_func := v_trig_func ||'ELSIF NEW.'||v_control||' >= '||v_prev_partition_id||' AND NEW.'||v_control||' < '||v_current_partition_id|| ' THEN INSERT INTO '||v_prev_partition_name||' VALUES (NEW.*); '; END IF; v_trig_func := v_trig_func ||'ELSE RETURN NEW; END IF; v_current_partition_id := NEW.'||v_control||' - (NEW.'||v_control||' % '||v_part_interval||'); IF (NEW.'||v_control||' % '||v_part_interval||') > ('||v_part_interval||' / 2) THEN v_next_partition_id := (substring(v_last_partition from char_length('||quote_literal(p_parent_table||'_p')||')+1)::bigint) + '||v_part_interval||'; IF ((v_next_partition_id - v_current_partition_id) / '||v_part_interval||') <= '||v_premake||' THEN v_next_partition_name := @extschema@.create_id_partition('||quote_literal(p_parent_table)||', '||quote_literal(v_control)||',' ||v_part_interval||', ARRAY[v_next_partition_id]); UPDATE @extschema@.part_config SET last_partition = v_next_partition_name WHERE parent_table = '||quote_literal(p_parent_table)||'; PERFORM @extschema@.create_id_function('||quote_literal(p_parent_table)||', NEW.'||v_control||'); END IF; END IF; END IF; RETURN NULL; END $t$;'; --RAISE NOTICE 'v_trig_func: %',v_trig_func; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Added function for current id interval: '||v_current_partition_id||' to '||v_1st_partition_id-1); END IF; ELSIF v_type = 'id-dynamic' THEN v_trig_func := 'CREATE OR REPLACE FUNCTION '||p_parent_table||'_part_trig_func() RETURNS trigger LANGUAGE plpgsql AS $t$ DECLARE v_count int; v_current_partition_id bigint; v_current_partition_name text; v_last_partition text := '||quote_literal(v_last_partition)||'; v_last_partition_id bigint; v_next_partition_id bigint; v_next_partition_name text; v_schemaname text; v_tablename text; BEGIN IF TG_OP = ''INSERT'' THEN v_current_partition_id := NEW.'||v_control||' - (NEW.'||v_control||' % '||v_part_interval||'); v_current_partition_name := '''||p_parent_table||'_p''||v_current_partition_id; IF (NEW.'||v_control||' % '||v_part_interval||') > ('||v_part_interval||' / 2) THEN v_last_partition_id = substring(v_last_partition from char_length('||quote_literal(p_parent_table||'_p')||')+1)::bigint; v_next_partition_id := v_last_partition_id + '||v_part_interval||'; IF NEW.'||v_control||' >= v_next_partition_id THEN RETURN NEW; END IF; IF ((v_next_partition_id - v_current_partition_id) / '||quote_literal(v_part_interval)||') <= '||quote_literal(v_premake)||' THEN v_next_partition_name := @extschema@.create_id_partition('||quote_literal(p_parent_table)||', '||quote_literal(v_control)||',' ||quote_literal(v_part_interval)||', ARRAY[v_next_partition_id]); IF v_next_partition_name IS NOT NULL THEN UPDATE @extschema@.part_config SET last_partition = v_next_partition_name WHERE parent_table = '||quote_literal(p_parent_table)||'; PERFORM @extschema@.create_id_function('||quote_literal(p_parent_table)||', NEW.'||v_control||'); END IF; END IF; END IF; v_schemaname := split_part(v_current_partition_name, ''.'', 1); v_tablename := split_part(v_current_partition_name, ''.'', 2); SELECT count(*) INTO v_count FROM pg_tables WHERE schemaname = v_schemaname AND tablename = v_tablename; IF v_count > 0 THEN EXECUTE ''INSERT INTO ''||v_current_partition_name||'' VALUES($1.*)'' USING NEW; ELSE RETURN NEW; END IF; END IF; RETURN NULL; END $t$;'; -- RAISE NOTICE 'v_trig_func: %',v_trig_func; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Added function for dynamic id table: '||p_parent_table); END IF; ELSE RAISE EXCEPTION 'ERROR: Invalid id partitioning type given: %', v_type; END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN CREATE FUNCTION: '||p_parent_table); v_step_id := add_step(v_job_id, 'Partition function maintenance for table '||p_parent_table||' failed'); ELSIF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'BAD', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; pg_partman-2.2.2/updates/pg_partman--0.3.1--0.3.2.sql000066400000000000000000000222701262146621700214220ustar00rootroot00000000000000-- 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 ALTER TABLE @extschema@.part_grants DROP CONSTRAINT part_grants_pkey; ALTER TABLE @extschema@.part_grants DROP CONSTRAINT part_grants_parent_table_fkey; ALTER TABLE @extschema@.part_grants ADD CONSTRAINT part_grants_parent_table_fkey FOREIGN KEY (parent_table) REFERENCES @extschema@.part_config (parent_table) ON DELETE CASCADE ON UPDATE CASCADE; ALTER TABLE @extschema@.part_grants ADD CONSTRAINT part_grants_unique_grant UNIQUE (parent_table, grants, roles); /* * Function to apply grants on parent & child tables */ CREATE OR REPLACE FUNCTION apply_grants(p_parent_table text) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_child_table text; v_grants text; v_roles text; v_row record; BEGIN FOR v_row IN SELECT grants, roles FROM @extschema@.part_grants WHERE parent_table = p_parent_table LOOP EXECUTE 'GRANT '||v_row.grants||' ON '||p_parent_table||' TO '||v_row.roles; FOR v_child_table IN SELECT inhrelid::regclass FROM pg_catalog.pg_inherits WHERE inhparent::regclass = p_parent_table::regclass ORDER BY inhrelid::regclass ASC LOOP EXECUTE 'GRANT '||v_row.grants||' ON '||v_child_table||' TO '||v_row.roles; END LOOP; END LOOP; END $$; /* * Function to turn a table into the parent of a partition set */ CREATE OR REPLACE FUNCTION create_parent(p_parent_table text, p_control text, p_type @extschema@.partition_type, p_interval text, p_premake int DEFAULT 4, p_debug boolean DEFAULT false) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_current_id bigint; v_datetime_string text; v_id_interval bigint; v_job_id bigint; v_jobmon_schema text; v_last_partition_name text; v_old_search_path text; v_partition_time timestamp[]; v_partition_id bigint[]; v_max bigint; v_notnull boolean; v_starting_partition_id bigint; v_step_id bigint; v_tablename text; v_time_interval interval; BEGIN SELECT tablename INTO v_tablename FROM pg_tables WHERE schemaname || '.' || tablename = p_parent_table; IF v_tablename IS NULL THEN RAISE EXCEPTION 'Please create given parent table first: %', p_parent_table; END IF; SELECT attnotnull INTO v_notnull FROM pg_attribute WHERE attrelid = p_parent_table::regclass AND attname = p_control; IF v_notnull = false THEN RAISE EXCEPTION 'Control column (%) for parent table (%) must be NOT NULL', p_control, p_parent_table; END IF; EXECUTE 'LOCK TABLE '||p_parent_table||' IN ACCESS EXCLUSIVE MODE'; SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN SETUP PARENT: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating initial partitions on new parent table: '||p_parent_table); END IF; CASE WHEN p_interval = 'yearly' THEN v_time_interval = '1 year'; v_datetime_string := 'YYYY'; WHEN p_interval = 'quarterly' THEN v_time_interval = '3 months'; v_datetime_string = 'YYYY"q"Q'; WHEN p_interval = 'monthly' THEN v_time_interval = '1 month'; v_datetime_string := 'YYYY_MM'; WHEN p_interval = 'weekly' THEN v_time_interval = '1 week'; v_datetime_string := 'IYYY"w"IW'; WHEN p_interval = 'daily' THEN v_time_interval = '1 day'; v_datetime_string := 'YYYY_MM_DD'; WHEN p_interval = 'hourly' THEN v_time_interval = '1 hour'; v_datetime_string := 'YYYY_MM_DD_HH24MI'; WHEN p_interval = 'half-hour' THEN v_time_interval = '30 mins'; v_datetime_string := 'YYYY_MM_DD_HH24MI'; WHEN p_interval = 'quarter-hour' THEN v_time_interval = '15 mins'; v_datetime_string := 'YYYY_MM_DD_HH24MI'; ELSE IF p_type = 'id-static' OR p_type = 'id-dynamic' THEN v_id_interval := p_interval::bigint; ELSE RAISE EXCEPTION 'Invalid interval for time based partitioning: %', p_interval; END IF; END CASE; IF p_type = 'time-static' OR p_type = 'time-dynamic' THEN FOR i IN 0..p_premake LOOP v_partition_time := array_append(v_partition_time, quote_literal(CURRENT_TIMESTAMP + (v_time_interval*i))::timestamp); END LOOP; EXECUTE 'SELECT @extschema@.create_time_partition('||quote_literal(p_parent_table)||','||quote_literal(p_control)||',' ||quote_literal(v_time_interval)||','||quote_literal(v_datetime_string)||','||quote_literal(v_partition_time)||')' INTO v_last_partition_name; INSERT INTO @extschema@.part_config (parent_table, type, part_interval, control, premake, datetime_string, last_partition) VALUES (p_parent_table, p_type, v_time_interval, p_control, p_premake, v_datetime_string, v_last_partition_name); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Time partitions premade: '||p_premake); END IF; END IF; IF p_type = 'id-static' OR p_type = 'id-dynamic' THEN -- If there is already data, start partitioning with the highest current value EXECUTE 'SELECT COALESCE(max('||p_control||')::bigint, 0) FROM '||p_parent_table||' LIMIT 1' INTO v_max; v_starting_partition_id := v_max - (v_max % v_id_interval); FOR i IN 0..p_premake LOOP v_partition_id = array_append(v_partition_id, (v_id_interval*i)+v_starting_partition_id); END LOOP; EXECUTE 'SELECT @extschema@.create_id_partition('||quote_literal(p_parent_table)||','||quote_literal(p_control)||',' ||v_id_interval||','||quote_literal(v_partition_id)||')' INTO v_last_partition_name; INSERT INTO @extschema@.part_config (parent_table, type, part_interval, control, premake, last_partition) VALUES (p_parent_table, p_type, v_id_interval, p_control, p_premake, v_last_partition_name); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'ID partitions premade: '||p_premake); END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Creating partition function'); END IF; IF p_type = 'time-static' OR p_type = 'time-dynamic' THEN EXECUTE 'SELECT @extschema@.create_time_function('||quote_literal(p_parent_table)||')'; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Time function created'); END IF; ELSIF p_type = 'id-static' OR p_type = 'id-dynamic' THEN v_current_id := COALESCE(v_max, 0); EXECUTE 'SELECT @extschema@.create_id_function('||quote_literal(p_parent_table)||','||v_current_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; EXECUTE 'SELECT @extschema@.create_trigger('||quote_literal(p_parent_table)||')'; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN CREATE PARENT: '||p_parent_table); v_step_id := add_step(v_job_id, 'Partition creation for table '||p_parent_table||' failed'); ELSIF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'BAD', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to monitor for data getting inserted into parent tables managed by extension */ CREATE OR REPLACE FUNCTION check_parent() RETURNS SETOF @extschema@.check_parent_table LANGUAGE plpgsql STABLE SECURITY DEFINER AS $$ DECLARE v_count bigint = 0; v_sql text; v_tables record; v_trouble @extschema@.check_parent_table%rowtype; BEGIN FOR v_tables IN SELECT DISTINCT parent_table FROM @extschema@.part_config LOOP v_sql := 'SELECT count(1) AS n FROM ONLY '||v_tables.parent_table; EXECUTE v_sql INTO v_count; IF v_count > 0 THEN v_trouble.parent_table := v_tables.parent_table; v_trouble.count := v_count; RETURN NEXT v_trouble; END IF; v_count := 0; END LOOP; RETURN; END $$; pg_partman-2.2.2/updates/pg_partman--0.3.2--0.4.0.sql000066400000000000000000000617441262146621700214330ustar00rootroot00000000000000-- 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. /* * Check function for config table partition types */ CREATE FUNCTION check_partition_type (p_type text) RETURNS boolean LANGUAGE plpgsql IMMUTABLE SECURITY DEFINER AS $$ DECLARE v_result boolean; BEGIN SELECT p_type IN ('time-static', 'time-dynamic', 'id-static', 'id-dynamic') INTO v_result; RETURN v_result; END $$; DROP FUNCTION @extschema@.create_parent(text, text, @extschema@.partition_type, text, int, boolean); DROP TABLE @extschema@.part_grants; ALTER TABLE @extschema@.part_config ADD COLUMN new_type text; UPDATE @extschema@.part_config SET new_type = type; ALTER TABLE @extschema@.part_config ALTER new_type SET NOT NULL; ALTER TABLE @extschema@.part_config DROP COLUMN type; DROP TYPE @extschema@.partition_type; ALTER TABLE @extschema@.part_config RENAME new_type TO type; ALTER TABLE @extschema@.part_config ADD CONSTRAINT part_config_type_check CHECK (@extschema@.check_partition_type(type)); /* * Function to apply ownership & privileges on child tables using parent table as reference */ CREATE OR REPLACE FUNCTION apply_grants(p_parent_table text) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_all text[] := ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER']; v_child_table text; v_count int := 0; v_grant text; v_grantees text[]; v_owner text; v_owner_sql text; v_revoke text[]; v_revoke_sql text; v_row record; v_sql text; BEGIN SELECT count(parent_table) INTO v_count FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_count = 0 THEN RAISE EXCEPTION 'Given table is not managed by this extention: %', p_parent_table; END IF; SELECT tableowner INTO v_owner FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; FOR v_child_table IN SELECT inhrelid::regclass FROM pg_catalog.pg_inherits WHERE inhparent::regclass = p_parent_table::regclass ORDER BY inhrelid::regclass ASC LOOP v_grantees := NULL; FOR v_row IN SELECT array_agg(privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = p_parent_table GROUP BY grantee LOOP EXECUTE 'GRANT '||array_to_string(v_row.types, ',')||' ON '||v_child_table||' TO '||v_row.grantee; SELECT array_agg(r) INTO v_revoke FROM (SELECT unnest(v_all) AS r EXCEPT SELECT unnest(v_row.types)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE '||array_to_string(v_revoke, ',')||' ON '||v_child_table||' FROM '||v_row.grantee||' CASCADE'; END IF; v_grantees := array_append(v_grantees, v_row.grantee::text); END LOOP; -- Revoke all privileges from roles that have none on the parent IF v_grantees IS NOT NULL THEN SELECT array_agg(r) INTO v_revoke FROM ( SELECT DISTINCT grantee::text AS r FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = v_child_table EXCEPT SELECT unnest(v_grantees)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE ALL ON '||v_child_table||' FROM '||array_to_string(v_revoke, ','); END IF; END IF; EXECUTE 'ALTER TABLE '||v_child_table||' OWNER TO '||v_owner; END LOOP; END $$; /* * Function to turn a table into the parent of a partition set */ CREATE FUNCTION create_parent(p_parent_table text, p_control text, p_type text, p_interval text, p_premake int DEFAULT 4, p_debug boolean DEFAULT false) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_current_id bigint; v_datetime_string text; v_id_interval bigint; v_job_id bigint; v_jobmon_schema text; v_last_partition_name text; v_old_search_path text; v_partition_time timestamp[]; v_partition_id bigint[]; v_max bigint; v_notnull boolean; v_starting_partition_id bigint; v_step_id bigint; v_tablename text; v_time_interval interval; BEGIN IF position('.' in p_parent_table) = 0 THEN RAISE EXCEPTION 'Parent table must be schema qualified'; END IF; SELECT tablename INTO v_tablename FROM pg_tables WHERE schemaname || '.' || tablename = p_parent_table; IF v_tablename IS NULL THEN RAISE EXCEPTION 'Please create given parent table first: %', p_parent_table; END IF; SELECT attnotnull INTO v_notnull FROM pg_attribute WHERE attrelid = p_parent_table::regclass AND attname = p_control; IF v_notnull = false THEN RAISE EXCEPTION 'Control column (%) for parent table (%) must be NOT NULL', p_control, p_parent_table; END IF; EXECUTE 'LOCK TABLE '||p_parent_table||' IN ACCESS EXCLUSIVE MODE'; SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN SETUP PARENT: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating initial partitions on new parent table: '||p_parent_table); END IF; CASE WHEN p_interval = 'yearly' THEN v_time_interval = '1 year'; v_datetime_string := 'YYYY'; WHEN p_interval = 'quarterly' THEN v_time_interval = '3 months'; v_datetime_string = 'YYYY"q"Q'; WHEN p_interval = 'monthly' THEN v_time_interval = '1 month'; v_datetime_string := 'YYYY_MM'; WHEN p_interval = 'weekly' THEN v_time_interval = '1 week'; v_datetime_string := 'IYYY"w"IW'; WHEN p_interval = 'daily' THEN v_time_interval = '1 day'; v_datetime_string := 'YYYY_MM_DD'; WHEN p_interval = 'hourly' THEN v_time_interval = '1 hour'; v_datetime_string := 'YYYY_MM_DD_HH24MI'; WHEN p_interval = 'half-hour' THEN v_time_interval = '30 mins'; v_datetime_string := 'YYYY_MM_DD_HH24MI'; WHEN p_interval = 'quarter-hour' THEN v_time_interval = '15 mins'; v_datetime_string := 'YYYY_MM_DD_HH24MI'; ELSE IF p_type = 'id-static' OR p_type = 'id-dynamic' THEN v_id_interval := p_interval::bigint; ELSE RAISE EXCEPTION 'Invalid interval for time based partitioning: %', p_interval; END IF; END CASE; IF p_type = 'time-static' OR p_type = 'time-dynamic' THEN FOR i IN 0..p_premake LOOP v_partition_time := array_append(v_partition_time, quote_literal(CURRENT_TIMESTAMP + (v_time_interval*i))::timestamp); END LOOP; INSERT INTO @extschema@.part_config (parent_table, type, part_interval, control, premake, datetime_string) VALUES (p_parent_table, p_type, v_time_interval, p_control, p_premake, v_datetime_string); EXECUTE 'SELECT @extschema@.create_time_partition('||quote_literal(p_parent_table)||','||quote_literal(p_control)||',' ||quote_literal(v_time_interval)||','||quote_literal(v_datetime_string)||','||quote_literal(v_partition_time)||')' INTO v_last_partition_name; -- Doing separate update because create function needs parent table in config table for apply_grants() UPDATE @extschema@.part_config SET last_partition = v_last_partition_name WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Time partitions premade: '||p_premake); END IF; END IF; IF p_type = 'id-static' OR p_type = 'id-dynamic' THEN -- If there is already data, start partitioning with the highest current value EXECUTE 'SELECT COALESCE(max('||p_control||')::bigint, 0) FROM '||p_parent_table||' LIMIT 1' INTO v_max; v_starting_partition_id := v_max - (v_max % v_id_interval); FOR i IN 0..p_premake LOOP v_partition_id = array_append(v_partition_id, (v_id_interval*i)+v_starting_partition_id); END LOOP; INSERT INTO @extschema@.part_config (parent_table, type, part_interval, control, premake) VALUES (p_parent_table, p_type, v_id_interval, p_control, p_premake); EXECUTE 'SELECT @extschema@.create_id_partition('||quote_literal(p_parent_table)||','||quote_literal(p_control)||',' ||v_id_interval||','||quote_literal(v_partition_id)||')' INTO v_last_partition_name; -- Doing separate update because create function needs parent table in config table for apply_grants() UPDATE @extschema@.part_config SET last_partition = v_last_partition_name WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'ID partitions premade: '||p_premake); END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Creating partition function'); END IF; IF p_type = 'time-static' OR p_type = 'time-dynamic' THEN EXECUTE 'SELECT @extschema@.create_time_function('||quote_literal(p_parent_table)||')'; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Time function created'); END IF; ELSIF p_type = 'id-static' OR p_type = 'id-dynamic' THEN v_current_id := COALESCE(v_max, 0); EXECUTE 'SELECT @extschema@.create_id_function('||quote_literal(p_parent_table)||','||v_current_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; EXECUTE 'SELECT @extschema@.create_trigger('||quote_literal(p_parent_table)||')'; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN CREATE PARENT: '||p_parent_table); v_step_id := add_step(v_job_id, 'Partition creation for table '||p_parent_table||' failed'); ELSIF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'BAD', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Create the next partition in sequence for a time-based partition set */ CREATE OR REPLACE FUNCTION create_next_time_partition (p_parent_table text) RETURNS void LANGUAGE plpgsql AS $$ DECLARE v_control text; v_datetime_string text; v_last_partition text; v_next_partition_timestamp timestamp; v_next_year text; v_part_interval interval; v_quarter text; v_tablename text; v_type text; v_year text; BEGIN SELECT type , part_interval::interval , control , datetime_string , last_partition FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic') INTO v_type, v_part_interval, v_control, v_datetime_string, v_last_partition; IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; -- Double check that last created partition exists IF v_last_partition IS NOT NULL THEN SELECT tablename INTO v_tablename FROM pg_tables WHERE schemaname || '.' || tablename = v_last_partition; IF v_tablename IS NULL THEN RAISE EXCEPTION 'ERROR: previous partition table missing. Unable to determine next proper partition in sequence.'; END IF; ELSE RAISE EXCEPTION 'ERROR: last known partition missing from config table for parent table %.', p_parent_table; END IF; -- pull out datetime portion of last partition's tablename to make the next one IF v_part_interval != '3 months' THEN v_next_partition_timestamp := to_timestamp(substring(v_last_partition from char_length(p_parent_table||'_p')+1), v_datetime_string) + v_part_interval; ELSE -- to_timestamp doesn't recognize 'Q' date string formater. Handle it v_year := split_part(substring(v_last_partition from char_length(p_parent_table||'_p')+1), 'q', 1); v_next_year := extract('year' from to_date(v_year, 'YYYY')+'1year'::interval); v_quarter := split_part(substring(v_last_partition from char_length(p_parent_table||'_p')+1), 'q', 2); CASE WHEN v_quarter = '1' THEN v_next_partition_timestamp := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); WHEN v_quarter = '2' THEN v_next_partition_timestamp := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); WHEN v_quarter = '3' THEN v_next_partition_timestamp := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); WHEN v_quarter = '4' THEN v_next_partition_timestamp := to_timestamp(v_next_year || '-01-01', 'YYYY-MM-DD'); END CASE; END IF; EXECUTE 'SELECT @extschema@.create_time_partition('||quote_literal(p_parent_table)||','||quote_literal(v_control)||','||quote_literal(v_part_interval)||',' ||quote_literal(v_datetime_string)||','||quote_literal(ARRAY[v_next_partition_timestamp])||')' INTO v_last_partition; IF v_last_partition IS NOT NULL THEN UPDATE @extschema@.part_config SET last_partition = v_last_partition WHERE parent_table = p_parent_table; END IF; END $$; /* * Function to create a child table in a time-based partition set */ CREATE OR REPLACE FUNCTION create_time_partition (p_parent_table text, p_control text, p_interval interval, p_datetime_string text, p_partition_times timestamp[]) RETURNS text LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_job_id bigint; v_jobmon_schema text; v_old_search_path text; v_partition_name text; v_partition_timestamp_end timestamp; v_partition_timestamp_start timestamp; v_quarter text; v_step_id bigint; v_tablename text; v_time timestamp; v_year text; BEGIN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; FOREACH v_time IN ARRAY p_partition_times LOOP v_partition_name := p_parent_table || '_p'; IF p_interval = '1 year' OR p_interval = '1 month' OR p_interval = '1 day' OR p_interval = '1 hour' OR p_interval = '30 mins' OR p_interval = '15 mins' THEN v_partition_name := v_partition_name || to_char(v_time, 'YYYY'); IF p_interval = '1 month' OR p_interval = '1 day' OR p_interval = '1 hour' OR p_interval = '30 mins' OR p_interval = '15 mins' THEN v_partition_name := v_partition_name || '_' || to_char(v_time, 'MM'); IF p_interval = '1 day' OR p_interval = '1 hour' OR p_interval = '30 mins' OR p_interval = '15 mins' THEN v_partition_name := v_partition_name || '_' || to_char(v_time, 'DD'); IF p_interval = '1 hour' OR p_interval = '30 mins' OR p_interval = '15 mins' THEN v_partition_name := v_partition_name || '_' || to_char(v_time, 'HH24'); IF p_interval <> '30 mins' AND p_interval <> '15 mins' THEN v_partition_name := v_partition_name || '00'; ELSIF p_interval = '15 mins' THEN IF date_part('minute', v_time) < 15 THEN v_partition_name := v_partition_name || '00'; ELSIF date_part('minute', v_time) >= 15 AND date_part('minute', v_time) < 30 THEN v_partition_name := v_partition_name || '15'; ELSIF date_part('minute', v_time) >= 30 AND date_part('minute', v_time) < 45 THEN v_partition_name := v_partition_name || '30'; ELSE v_partition_name := v_partition_name || '45'; END IF; ELSIF p_interval = '30 mins' THEN IF date_part('minute', v_time) < 30 THEN v_partition_name := v_partition_name || '00'; ELSE v_partition_name := v_partition_name || '30'; END IF; END IF; END IF; -- end hour IF END IF; -- end day IF END IF; -- end month IF ELSIF p_interval = '1 week' THEN v_partition_name := v_partition_name || to_char(v_time, 'IYYY') || 'w' || to_char(v_time, 'IW'); END IF; -- end year/week IF -- pull out datetime portion of last partition's tablename v_partition_timestamp_start := to_timestamp(substring(v_partition_name from char_length(p_parent_table||'_p')+1), p_datetime_string); v_partition_timestamp_end := to_timestamp(substring(v_partition_name from char_length(p_parent_table||'_p')+1), p_datetime_string) + p_interval; -- "Q" is ignored in to_timestamp, so handle special case IF p_interval = '3 months' THEN v_year := to_char(v_time, 'YYYY'); v_quarter := to_char(v_time, 'Q'); v_partition_name := v_partition_name || v_year || 'q' || v_quarter; CASE WHEN v_quarter = '1' THEN v_partition_timestamp_start := to_timestamp(v_year || '-01-01', 'YYYY-MM-DD'); WHEN v_quarter = '2' THEN v_partition_timestamp_start := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); WHEN v_quarter = '3' THEN v_partition_timestamp_start := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); WHEN v_quarter = '4' THEN v_partition_timestamp_start := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); END CASE; v_partition_timestamp_end := v_partition_timestamp_start + p_interval; END IF; SELECT schemaname ||'.'|| tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_tablename IS NOT NULL THEN CONTINUE; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating new partition '||v_partition_name||' with interval from '||v_partition_timestamp_start||' to '||(v_partition_timestamp_end-'1sec'::interval)); END IF; IF position('.' in p_parent_table) > 0 THEN v_tablename := substring(v_partition_name from position('.' in v_partition_name)+1); END IF; EXECUTE 'CREATE TABLE '||v_partition_name||' (LIKE '||p_parent_table||' INCLUDING DEFAULTS INCLUDING INDEXES)'; EXECUTE 'ALTER TABLE '||v_partition_name||' ADD CONSTRAINT '||v_tablename||'_partition_check CHECK ('||p_control||'>='||quote_literal(v_partition_timestamp_start)||' AND '||p_control||'<'||quote_literal(v_partition_timestamp_end)||')'; EXECUTE 'ALTER TABLE '||v_partition_name||' INHERIT '||p_parent_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); PERFORM close_job(v_job_id); END IF; END LOOP; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN APPLYING GRANTS: '||p_parent_table); v_step_id := add_step(v_job_id, 'Looping through all child tables applying privileges of the parent'); END IF; PERFORM @extschema@.apply_grants(p_parent_table); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); PERFORM close_job(v_job_id); END IF; IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_partition_name; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); v_step_id := add_step(v_job_id, 'Partition maintenance for table '||p_parent_table||' failed'); ELSIF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'BAD', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to create id partitions */ CREATE OR REPLACE FUNCTION create_id_partition (p_parent_table text, p_control text, p_interval bigint, p_partition_ids bigint[]) RETURNS text LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_job_id bigint; v_jobmon_schema text; v_old_search_path text; v_partition_name text; v_step_id bigint; v_tablename text; v_id bigint; BEGIN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; FOREACH v_id IN ARRAY p_partition_ids LOOP v_partition_name := p_parent_table||'_p'||v_id; SELECT schemaname ||'.'|| tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_tablename IS NOT NULL THEN CONTINUE; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating new partition '||v_partition_name||' with interval from '||v_id||' to '||(v_id + p_interval)-1); END IF; IF position('.' in p_parent_table) > 0 THEN v_tablename := substring(v_partition_name from position('.' in v_partition_name)+1); END IF; EXECUTE 'CREATE TABLE '||v_partition_name||' (LIKE '||p_parent_table||' INCLUDING DEFAULTS INCLUDING INDEXES)'; EXECUTE 'ALTER TABLE '||v_partition_name||' ADD CONSTRAINT '||v_tablename||'_partition_check CHECK ('||p_control||'>='||quote_literal(v_id)||' AND '||p_control||'<'||quote_literal(v_id + p_interval)||')'; EXECUTE 'ALTER TABLE '||v_partition_name||' INHERIT '||p_parent_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); PERFORM close_job(v_job_id); END IF; END LOOP; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN APPLYING GRANTS: '||p_parent_table); v_step_id := add_step(v_job_id, 'Looping through all child tables applying privileges of the parent'); END IF; PERFORM @extschema@.apply_grants(p_parent_table); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); PERFORM close_job(v_job_id); END IF; IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_partition_name; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); v_step_id := add_step(v_job_id, 'Partition maintenance for table '||p_parent_table||' failed'); ELSIF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'BAD', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; pg_partman-2.2.2/updates/pg_partman--0.4.0--0.4.1.sql000066400000000000000000000467201262146621700214300ustar00rootroot00000000000000-- 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. DROP FUNCTION apply_grants(text); DROP TYPE IF EXISTS @extschema@.partition_type; /* * Function to re-apply ownership & privileges on all child tables in a partition set using parent table as reference */ CREATE FUNCTION reapply_privileges(p_parent_table text) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_all text[] := ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER']; v_child_owner text; v_child_table text; v_child_grant record; v_count int; v_grant text; v_grantees text[]; v_job_id bigint; v_jobmon_schema text; v_match boolean; v_old_search_path text; v_parent_owner text; v_owner_sql text; v_revoke text[]; v_parent_grant record; v_sql text; v_step_id bigint; BEGIN SELECT count(*) INTO v_count FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_count = 0 THEN RAISE EXCEPTION 'Given table is not managed by this extention: %', p_parent_table; END IF; SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN RE-APPLYING PRIVILEGES TO ALL CHILD TABLES OF: '||p_parent_table); v_step_id := add_step(v_job_id, 'Setting new child table privileges'); END IF; SELECT tableowner INTO v_parent_owner FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; FOR v_child_table IN SELECT inhrelid::regclass FROM pg_catalog.pg_inherits WHERE inhparent::regclass = p_parent_table::regclass ORDER BY inhrelid::regclass ASC LOOP PERFORM update_step(v_step_id, 'PENDING', 'Currently on child partition in ascending order: '||v_child_table); v_grantees := NULL; FOR v_parent_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = p_parent_table GROUP BY grantee LOOP -- Compare parent & child grants. Don't re-apply if it already exists v_match := false; FOR v_child_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = v_child_table GROUP BY grantee LOOP IF v_parent_grant.types = v_child_grant.types AND v_parent_grant.grantee = v_child_grant.grantee THEN v_match := true; END IF; END LOOP; IF v_match = false THEN EXECUTE 'GRANT '||array_to_string(v_parent_grant.types, ',')||' ON '||v_child_table||' TO '||v_parent_grant.grantee; SELECT array_agg(r) INTO v_revoke FROM (SELECT unnest(v_all) AS r EXCEPT SELECT unnest(v_parent_grant.types)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE '||array_to_string(v_revoke, ',')||' ON '||v_child_table||' FROM '||v_parent_grant.grantee||' CASCADE'; END IF; END IF; v_grantees := array_append(v_grantees, v_parent_grant.grantee::text); END LOOP; -- Revoke all privileges from roles that have none on the parent IF v_grantees IS NOT NULL THEN SELECT array_agg(r) INTO v_revoke FROM ( SELECT DISTINCT grantee::text AS r FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = v_child_table EXCEPT SELECT unnest(v_grantees)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE ALL ON '||v_child_table||' FROM '||array_to_string(v_revoke, ','); END IF; END IF; SELECT tableowner INTO v_child_owner FROM pg_tables WHERE schemaname ||'.'|| tablename = v_child_table; IF v_parent_owner <> v_child_owner THEN EXECUTE 'ALTER TABLE '||v_child_table||' OWNER TO '||v_parent_owner; END IF; END LOOP; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); PERFORM close_job(v_job_id); END IF; IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); v_step_id := add_step(v_job_id, 'Partition maintenance for table '||p_parent_table||' failed'); ELSIF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'BAD', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to create id partitions */ CREATE OR REPLACE FUNCTION create_id_partition (p_parent_table text, p_control text, p_interval bigint, p_partition_ids bigint[]) RETURNS text LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_all text[] := ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER']; v_grantees text[]; v_job_id bigint; v_jobmon_schema text; v_old_search_path text; v_parent_grant record; v_parent_owner text; v_partition_name text; v_revoke text[]; v_step_id bigint; v_tablename text; v_id bigint; BEGIN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; SELECT tableowner INTO v_parent_owner FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; FOREACH v_id IN ARRAY p_partition_ids LOOP v_partition_name := p_parent_table||'_p'||v_id; SELECT schemaname ||'.'|| tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_tablename IS NOT NULL THEN CONTINUE; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating new partition '||v_partition_name||' with interval from '||v_id||' to '||(v_id + p_interval)-1); END IF; IF position('.' in p_parent_table) > 0 THEN v_tablename := substring(v_partition_name from position('.' in v_partition_name)+1); END IF; EXECUTE 'CREATE TABLE '||v_partition_name||' (LIKE '||p_parent_table||' INCLUDING DEFAULTS INCLUDING INDEXES)'; EXECUTE 'ALTER TABLE '||v_partition_name||' ADD CONSTRAINT '||v_tablename||'_partition_check CHECK ('||p_control||'>='||quote_literal(v_id)||' AND '||p_control||'<'||quote_literal(v_id + p_interval)||')'; EXECUTE 'ALTER TABLE '||v_partition_name||' INHERIT '||p_parent_table; FOR v_parent_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = p_parent_table GROUP BY grantee LOOP EXECUTE 'GRANT '||array_to_string(v_parent_grant.types, ',')||' ON '||v_partition_name||' TO '||v_parent_grant.grantee; SELECT array_agg(r) INTO v_revoke FROM (SELECT unnest(v_all) AS r EXCEPT SELECT unnest(v_parent_grant.types)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE '||array_to_string(v_revoke, ',')||' ON '||v_partition_name||' FROM '||v_parent_grant.grantee||' CASCADE'; END IF; v_grantees := array_append(v_grantees, v_parent_grant.grantee::text); END LOOP; -- Revoke all privileges from roles that have none on the parent IF v_grantees IS NOT NULL THEN SELECT array_agg(r) INTO v_revoke FROM ( SELECT DISTINCT grantee::text AS r FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = v_partition_name EXCEPT SELECT unnest(v_grantees)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE ALL ON '||v_partition_name||' FROM '||array_to_string(v_revoke, ','); END IF; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' OWNER TO '||v_parent_owner; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); PERFORM close_job(v_job_id); END IF; END LOOP; IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_partition_name; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); v_step_id := add_step(v_job_id, 'Partition maintenance for table '||p_parent_table||' failed'); ELSIF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'BAD', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to create a child table in a time-based partition set */ CREATE OR REPLACE FUNCTION create_time_partition (p_parent_table text, p_control text, p_interval interval, p_datetime_string text, p_partition_times timestamp[]) RETURNS text LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_all text[] := ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER']; v_grantees text[]; v_job_id bigint; v_jobmon_schema text; v_old_search_path text; v_parent_grant record; v_parent_owner text; v_partition_name text; v_partition_timestamp_end timestamp; v_partition_timestamp_start timestamp; v_quarter text; v_revoke text[]; v_step_id bigint; v_tablename text; v_time timestamp; v_year text; BEGIN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; SELECT tableowner INTO v_parent_owner FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; FOREACH v_time IN ARRAY p_partition_times LOOP v_partition_name := p_parent_table || '_p'; IF p_interval = '1 year' OR p_interval = '1 month' OR p_interval = '1 day' OR p_interval = '1 hour' OR p_interval = '30 mins' OR p_interval = '15 mins' THEN v_partition_name := v_partition_name || to_char(v_time, 'YYYY'); IF p_interval = '1 month' OR p_interval = '1 day' OR p_interval = '1 hour' OR p_interval = '30 mins' OR p_interval = '15 mins' THEN v_partition_name := v_partition_name || '_' || to_char(v_time, 'MM'); IF p_interval = '1 day' OR p_interval = '1 hour' OR p_interval = '30 mins' OR p_interval = '15 mins' THEN v_partition_name := v_partition_name || '_' || to_char(v_time, 'DD'); IF p_interval = '1 hour' OR p_interval = '30 mins' OR p_interval = '15 mins' THEN v_partition_name := v_partition_name || '_' || to_char(v_time, 'HH24'); IF p_interval <> '30 mins' AND p_interval <> '15 mins' THEN v_partition_name := v_partition_name || '00'; ELSIF p_interval = '15 mins' THEN IF date_part('minute', v_time) < 15 THEN v_partition_name := v_partition_name || '00'; ELSIF date_part('minute', v_time) >= 15 AND date_part('minute', v_time) < 30 THEN v_partition_name := v_partition_name || '15'; ELSIF date_part('minute', v_time) >= 30 AND date_part('minute', v_time) < 45 THEN v_partition_name := v_partition_name || '30'; ELSE v_partition_name := v_partition_name || '45'; END IF; ELSIF p_interval = '30 mins' THEN IF date_part('minute', v_time) < 30 THEN v_partition_name := v_partition_name || '00'; ELSE v_partition_name := v_partition_name || '30'; END IF; END IF; END IF; -- end hour IF END IF; -- end day IF END IF; -- end month IF ELSIF p_interval = '1 week' THEN v_partition_name := v_partition_name || to_char(v_time, 'IYYY') || 'w' || to_char(v_time, 'IW'); END IF; -- end year/week IF -- pull out datetime portion of last partition's tablename v_partition_timestamp_start := to_timestamp(substring(v_partition_name from char_length(p_parent_table||'_p')+1), p_datetime_string); v_partition_timestamp_end := to_timestamp(substring(v_partition_name from char_length(p_parent_table||'_p')+1), p_datetime_string) + p_interval; -- "Q" is ignored in to_timestamp, so handle special case IF p_interval = '3 months' THEN v_year := to_char(v_time, 'YYYY'); v_quarter := to_char(v_time, 'Q'); v_partition_name := v_partition_name || v_year || 'q' || v_quarter; CASE WHEN v_quarter = '1' THEN v_partition_timestamp_start := to_timestamp(v_year || '-01-01', 'YYYY-MM-DD'); WHEN v_quarter = '2' THEN v_partition_timestamp_start := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); WHEN v_quarter = '3' THEN v_partition_timestamp_start := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); WHEN v_quarter = '4' THEN v_partition_timestamp_start := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); END CASE; v_partition_timestamp_end := v_partition_timestamp_start + p_interval; END IF; SELECT schemaname ||'.'|| tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_tablename IS NOT NULL THEN CONTINUE; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating new partition '||v_partition_name||' with interval from '||v_partition_timestamp_start||' to '||(v_partition_timestamp_end-'1sec'::interval)); END IF; IF position('.' in p_parent_table) > 0 THEN v_tablename := substring(v_partition_name from position('.' in v_partition_name)+1); END IF; EXECUTE 'CREATE TABLE '||v_partition_name||' (LIKE '||p_parent_table||' INCLUDING DEFAULTS INCLUDING INDEXES)'; EXECUTE 'ALTER TABLE '||v_partition_name||' ADD CONSTRAINT '||v_tablename||'_partition_check CHECK ('||p_control||'>='||quote_literal(v_partition_timestamp_start)||' AND '||p_control||'<'||quote_literal(v_partition_timestamp_end)||')'; EXECUTE 'ALTER TABLE '||v_partition_name||' INHERIT '||p_parent_table; FOR v_parent_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = p_parent_table GROUP BY grantee LOOP EXECUTE 'GRANT '||array_to_string(v_parent_grant.types, ',')||' ON '||v_partition_name||' TO '||v_parent_grant.grantee; SELECT array_agg(r) INTO v_revoke FROM (SELECT unnest(v_all) AS r EXCEPT SELECT unnest(v_parent_grant.types)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE '||array_to_string(v_revoke, ',')||' ON '||v_partition_name||' FROM '||v_parent_grant.grantee||' CASCADE'; END IF; v_grantees := array_append(v_grantees, v_parent_grant.grantee::text); END LOOP; -- Revoke all privileges from roles that have none on the parent IF v_grantees IS NOT NULL THEN SELECT array_agg(r) INTO v_revoke FROM ( SELECT DISTINCT grantee::text AS r FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = v_partition_name EXCEPT SELECT unnest(v_grantees)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE ALL ON '||v_partition_name||' FROM '||array_to_string(v_revoke, ','); END IF; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' OWNER TO '||v_parent_owner; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); PERFORM close_job(v_job_id); END IF; END LOOP; IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_partition_name; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); v_step_id := add_step(v_job_id, 'Partition maintenance for table '||p_parent_table||' failed'); ELSIF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'BAD', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; pg_partman-2.2.2/updates/pg_partman--0.4.1--0.4.2.sql000066400000000000000000000673231262146621700214340ustar00rootroot00000000000000-- 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. ALTER TABLE @extschema@.part_config ADD CONSTRAINT positive_premake_check CHECK (premake > 0); /* * Create the trigger function for the parent table of a time-based partition set */ CREATE OR REPLACE FUNCTION create_time_function(p_parent_table text) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_control text; v_current_partition_name text; v_current_partition_timestamp timestamp; v_datetime_string text; v_final_partition_timestamp timestamp; v_job_id bigint; v_jobmon_schema text; v_old_search_path text; v_next_partition_name text; v_next_partition_timestamp timestamp; v_part_interval interval; v_premake int; v_prev_partition_name text; v_prev_partition_timestamp timestamp; v_step_id bigint; v_trig_func text; v_type text; BEGIN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE FUNCTION: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating partition function for table '||p_parent_table); END IF; SELECT type , part_interval::interval , control , premake , datetime_string INTO v_type , v_part_interval , v_control , v_premake , v_datetime_string FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF v_type = 'time-static' THEN CASE WHEN v_part_interval = '15 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0); WHEN v_part_interval = '30 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0); WHEN v_part_interval = '1 hour' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP); WHEN v_part_interval = '1 day' THEN v_current_partition_timestamp := date_trunc('day', CURRENT_TIMESTAMP); WHEN v_part_interval = '1 week' THEN v_current_partition_timestamp := date_trunc('week', CURRENT_TIMESTAMP); WHEN v_part_interval = '1 month' THEN v_current_partition_timestamp := date_trunc('month', CURRENT_TIMESTAMP); WHEN v_part_interval = '3 months' THEN v_current_partition_timestamp := date_trunc('quarter', CURRENT_TIMESTAMP); WHEN v_part_interval = '1 year' THEN v_current_partition_timestamp := date_trunc('year', CURRENT_TIMESTAMP); END CASE; v_current_partition_name := p_parent_table || '_p' || to_char(v_current_partition_timestamp, v_datetime_string); v_next_partition_timestamp := v_current_partition_timestamp + v_part_interval::interval; v_trig_func := 'CREATE OR REPLACE FUNCTION '||p_parent_table||'_part_trig_func() RETURNS trigger LANGUAGE plpgsql AS $t$ BEGIN IF TG_OP = ''INSERT'' THEN IF NEW.'||v_control||' >= '||quote_literal(v_current_partition_timestamp)||' AND NEW.'||v_control||' < '||quote_literal(v_next_partition_timestamp)|| ' THEN INSERT INTO '||v_current_partition_name||' VALUES (NEW.*); '; FOR i IN 1..v_premake LOOP v_prev_partition_timestamp := v_current_partition_timestamp - (v_part_interval::interval * i); v_next_partition_timestamp := v_current_partition_timestamp + (v_part_interval::interval * i); v_final_partition_timestamp := v_next_partition_timestamp + (v_part_interval::interval); v_prev_partition_name := p_parent_table || '_p' || to_char(v_prev_partition_timestamp, v_datetime_string); v_next_partition_name := p_parent_table || '_p' || to_char(v_next_partition_timestamp, v_datetime_string); v_trig_func := v_trig_func ||' ELSIF NEW.'||v_control||' >= '||quote_literal(v_prev_partition_timestamp)||' AND NEW.'||v_control||' < '|| quote_literal(v_prev_partition_timestamp + v_part_interval::interval)|| ' THEN INSERT INTO '||v_prev_partition_name||' VALUES (NEW.*); ELSIF NEW.'||v_control||' >= '||quote_literal(v_next_partition_timestamp)||' AND NEW.'||v_control||' < '|| quote_literal(v_final_partition_timestamp)|| ' THEN INSERT INTO '||v_next_partition_name||' VALUES (NEW.*); '; END LOOP; v_trig_func := v_trig_func ||' ELSE RETURN NEW; END IF; END IF; RETURN NULL; END $t$;'; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Added function for current time interval: '|| v_current_partition_timestamp||' to '||(v_final_partition_timestamp-'1sec'::interval)); END IF; ELSIF v_type = 'time-dynamic' THEN v_trig_func := 'CREATE OR REPLACE FUNCTION '||p_parent_table||'_part_trig_func() RETURNS trigger LANGUAGE plpgsql AS $t$ DECLARE v_count int; v_partition_name text; v_partition_timestamp timestamp; v_schemaname text; v_tablename text; BEGIN IF TG_OP = ''INSERT'' THEN '; CASE WHEN v_part_interval = '15 mins' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''hour'', NEW.'||v_control||') + ''15min''::interval * floor(date_part(''minute'', NEW.'||v_control||') / 15.0);'; WHEN v_part_interval = '30 mins' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''hour'', NEW.'||v_control||') + ''30min''::interval * floor(date_part(''minute'', NEW.'||v_control||') / 30.0);'; WHEN v_part_interval = '1 hour' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''hour'', NEW.'||v_control||');'; WHEN v_part_interval = '1 day' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''day'', NEW.'||v_control||');'; WHEN v_part_interval = '1 week' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''week'', NEW.'||v_control||');'; WHEN v_part_interval = '1 month' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''month'', NEW.'||v_control||');'; WHEN v_part_interval = '3 months' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''quarter'', NEW.'||v_control||');'; WHEN v_part_interval = '1 year' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''year'', NEW.'||v_control||');'; END CASE; v_trig_func := v_trig_func||' v_partition_name := '''||p_parent_table||'_p''|| to_char(v_partition_timestamp, '||quote_literal(v_datetime_string)||'); v_schemaname := split_part(v_partition_name, ''.'', 1); v_tablename := split_part(v_partition_name, ''.'', 2); SELECT count(*) INTO v_count FROM pg_tables WHERE schemaname = v_schemaname AND tablename = v_tablename; IF v_count > 0 THEN EXECUTE ''INSERT INTO ''||v_partition_name||'' VALUES($1.*)'' USING NEW; ELSE RETURN NEW; END IF; END IF; RETURN NULL; END $t$;'; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Added function for dynamic time table: '||p_parent_table); END IF; ELSE RAISE EXCEPTION 'ERROR: Invalid time partitioning type given: %', v_type; END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN CREATE FUNCTION: '||p_parent_table); v_step_id := add_step(v_job_id, 'Partition function maintenance for table '||p_parent_table||' failed'); ELSIF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'BAD', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Create the trigger function for the parent table of an id-based partition set */ CREATE OR REPLACE FUNCTION create_id_function(p_parent_table text, p_current_id bigint) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_control text; v_current_partition_name text; v_current_partition_id bigint; v_datetime_string text; v_final_partition_id bigint; v_job_id bigint; v_jobmon_schema text; v_last_partition text; v_next_partition_id bigint; v_next_partition_name text; v_old_search_path text; v_part_interval bigint; v_premake int; v_prev_partition_id bigint; v_prev_partition_name text; v_step_id bigint; v_trig_func text; v_type text; BEGIN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE FUNCTION: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating partition function for table '||p_parent_table); END IF; SELECT type , part_interval::bigint , control , premake , last_partition INTO v_type , v_part_interval , v_control , v_premake , v_last_partition FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF v_type = 'id-static' THEN v_current_partition_id := p_current_id - (p_current_id % v_part_interval); v_next_partition_id := v_current_partition_id + v_part_interval; v_current_partition_name := p_parent_table || '_p' || v_current_partition_id::text; v_trig_func := 'CREATE OR REPLACE FUNCTION '||p_parent_table||'_part_trig_func() RETURNS trigger LANGUAGE plpgsql AS $t$ DECLARE v_current_partition_id bigint; v_last_partition text := '||quote_literal(v_last_partition)||'; v_next_partition_id bigint; v_next_partition_name text; BEGIN IF TG_OP = ''INSERT'' THEN IF NEW.'||v_control||' >= '||v_current_partition_id||' AND NEW.'||v_control||' < '||v_next_partition_id|| ' THEN INSERT INTO '||v_current_partition_name||' VALUES (NEW.*); '; FOR i IN 1..v_premake LOOP v_prev_partition_id := v_current_partition_id - (v_part_interval * i); v_next_partition_id := v_current_partition_id + (v_part_interval * i); v_final_partition_id := v_next_partition_id + v_part_interval; v_prev_partition_name := p_parent_table || '_p' || v_prev_partition_id::text; v_next_partition_name := p_parent_table || '_p' || v_next_partition_id::text; -- Only make previous partitions if they're starting above zero IF v_prev_partition_id >= 0 THEN v_trig_func := v_trig_func ||' ELSIF NEW.'||v_control||' >= '||v_prev_partition_id||' AND NEW.'||v_control||' < '||v_prev_partition_id + v_part_interval|| ' THEN INSERT INTO '||v_prev_partition_name||' VALUES (NEW.*); '; END IF; v_trig_func := v_trig_func ||' ELSIF NEW.'||v_control||' >= '||v_next_partition_id||' AND NEW.'||v_control||' < '||v_final_partition_id|| ' THEN INSERT INTO '||v_next_partition_name||' VALUES (NEW.*); '; END LOOP; v_trig_func := v_trig_func ||' ELSE RETURN NEW; END IF; v_current_partition_id := NEW.'||v_control||' - (NEW.'||v_control||' % '||v_part_interval||'); IF (NEW.'||v_control||' % '||v_part_interval||') > ('||v_part_interval||' / 2) THEN v_next_partition_id := (substring(v_last_partition from char_length('||quote_literal(p_parent_table||'_p')||')+1)::bigint) + '||v_part_interval||'; IF ((v_next_partition_id - v_current_partition_id) / '||v_part_interval||') <= '||v_premake||' THEN v_next_partition_name := @extschema@.create_id_partition('||quote_literal(p_parent_table)||', '||quote_literal(v_control)||',' ||v_part_interval||', ARRAY[v_next_partition_id]); UPDATE @extschema@.part_config SET last_partition = v_next_partition_name WHERE parent_table = '||quote_literal(p_parent_table)||'; PERFORM @extschema@.create_id_function('||quote_literal(p_parent_table)||', NEW.'||v_control||'); END IF; END IF; END IF; RETURN NULL; END $t$;'; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Added function for current id interval: '||v_current_partition_id||' to '||v_final_partition_id-1); END IF; ELSIF v_type = 'id-dynamic' THEN v_trig_func := 'CREATE OR REPLACE FUNCTION '||p_parent_table||'_part_trig_func() RETURNS trigger LANGUAGE plpgsql AS $t$ DECLARE v_count int; v_current_partition_id bigint; v_current_partition_name text; v_last_partition text := '||quote_literal(v_last_partition)||'; v_last_partition_id bigint; v_next_partition_id bigint; v_next_partition_name text; v_schemaname text; v_tablename text; BEGIN IF TG_OP = ''INSERT'' THEN v_current_partition_id := NEW.'||v_control||' - (NEW.'||v_control||' % '||v_part_interval||'); v_current_partition_name := '''||p_parent_table||'_p''||v_current_partition_id; IF (NEW.'||v_control||' % '||v_part_interval||') > ('||v_part_interval||' / 2) THEN v_last_partition_id = substring(v_last_partition from char_length('||quote_literal(p_parent_table||'_p')||')+1)::bigint; v_next_partition_id := v_last_partition_id + '||v_part_interval||'; IF NEW.'||v_control||' >= v_next_partition_id THEN RETURN NEW; END IF; IF ((v_next_partition_id - v_current_partition_id) / '||quote_literal(v_part_interval)||') <= '||quote_literal(v_premake)||' THEN v_next_partition_name := @extschema@.create_id_partition('||quote_literal(p_parent_table)||', '||quote_literal(v_control)||',' ||quote_literal(v_part_interval)||', ARRAY[v_next_partition_id]); IF v_next_partition_name IS NOT NULL THEN UPDATE @extschema@.part_config SET last_partition = v_next_partition_name WHERE parent_table = '||quote_literal(p_parent_table)||'; PERFORM @extschema@.create_id_function('||quote_literal(p_parent_table)||', NEW.'||v_control||'); END IF; END IF; END IF; v_schemaname := split_part(v_current_partition_name, ''.'', 1); v_tablename := split_part(v_current_partition_name, ''.'', 2); SELECT count(*) INTO v_count FROM pg_tables WHERE schemaname = v_schemaname AND tablename = v_tablename; IF v_count > 0 THEN EXECUTE ''INSERT INTO ''||v_current_partition_name||'' VALUES($1.*)'' USING NEW; ELSE RETURN NEW; END IF; END IF; RETURN NULL; END $t$;'; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Added function for dynamic id table: '||p_parent_table); END IF; ELSE RAISE EXCEPTION 'ERROR: Invalid id partitioning type given: %', v_type; END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN CREATE FUNCTION: '||p_parent_table); v_step_id := add_step(v_job_id, 'Partition function maintenance for table '||p_parent_table||' failed'); ELSIF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'BAD', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to turn a table into the parent of a partition set */ CREATE OR REPLACE FUNCTION create_parent(p_parent_table text, p_control text, p_type text, p_interval text, p_premake int DEFAULT 4, p_debug boolean DEFAULT false) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_current_id bigint; v_datetime_string text; v_id_interval bigint; v_job_id bigint; v_jobmon_schema text; v_last_partition_name text; v_old_search_path text; v_partition_time timestamp[]; v_partition_id bigint[]; v_max bigint; v_notnull boolean; v_starting_partition_id bigint; v_step_id bigint; v_tablename text; v_time_interval interval; BEGIN IF position('.' in p_parent_table) = 0 THEN RAISE EXCEPTION 'Parent table must be schema qualified'; END IF; SELECT tablename INTO v_tablename FROM pg_tables WHERE schemaname || '.' || tablename = p_parent_table; IF v_tablename IS NULL THEN RAISE EXCEPTION 'Please create given parent table first: %', p_parent_table; END IF; SELECT attnotnull INTO v_notnull FROM pg_attribute WHERE attrelid = p_parent_table::regclass AND attname = p_control; IF v_notnull = false THEN RAISE EXCEPTION 'Control column (%) for parent table (%) must be NOT NULL', p_control, p_parent_table; END IF; EXECUTE 'LOCK TABLE '||p_parent_table||' IN ACCESS EXCLUSIVE MODE'; SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN SETUP PARENT: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating initial partitions on new parent table: '||p_parent_table); END IF; CASE WHEN p_interval = 'yearly' THEN v_time_interval = '1 year'; v_datetime_string := 'YYYY'; WHEN p_interval = 'quarterly' THEN v_time_interval = '3 months'; v_datetime_string = 'YYYY"q"Q'; WHEN p_interval = 'monthly' THEN v_time_interval = '1 month'; v_datetime_string := 'YYYY_MM'; WHEN p_interval = 'weekly' THEN v_time_interval = '1 week'; v_datetime_string := 'IYYY"w"IW'; WHEN p_interval = 'daily' THEN v_time_interval = '1 day'; v_datetime_string := 'YYYY_MM_DD'; WHEN p_interval = 'hourly' THEN v_time_interval = '1 hour'; v_datetime_string := 'YYYY_MM_DD_HH24MI'; WHEN p_interval = 'half-hour' THEN v_time_interval = '30 mins'; v_datetime_string := 'YYYY_MM_DD_HH24MI'; WHEN p_interval = 'quarter-hour' THEN v_time_interval = '15 mins'; v_datetime_string := 'YYYY_MM_DD_HH24MI'; ELSE IF p_type = 'id-static' OR p_type = 'id-dynamic' THEN v_id_interval := p_interval::bigint; IF v_id_interval <= 0 THEN RAISE EXCEPTION 'Interval for serial partitioning must be greater than zero'; END IF; ELSE RAISE EXCEPTION 'Invalid interval for time based partitioning: %', p_interval; END IF; END CASE; IF p_type = 'time-static' OR p_type = 'time-dynamic' THEN FOR i IN 0..p_premake LOOP IF i > 0 THEN -- also create previous partitions equal to premake, but avoid duplicating current v_partition_time := array_append(v_partition_time, quote_literal(CURRENT_TIMESTAMP - (v_time_interval*i))::timestamp); END IF; v_partition_time := array_append(v_partition_time, quote_literal(CURRENT_TIMESTAMP + (v_time_interval*i))::timestamp); END LOOP; INSERT INTO @extschema@.part_config (parent_table, type, part_interval, control, premake, datetime_string) VALUES (p_parent_table, p_type, v_time_interval, p_control, p_premake, v_datetime_string); EXECUTE 'SELECT @extschema@.create_time_partition('||quote_literal(p_parent_table)||','||quote_literal(p_control)||',' ||quote_literal(v_time_interval)||','||quote_literal(v_datetime_string)||','||quote_literal(v_partition_time)||')' INTO v_last_partition_name; -- Doing separate update because create function needs parent table in config table for apply_grants() UPDATE @extschema@.part_config SET last_partition = v_last_partition_name WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Time partitions premade: '||p_premake); END IF; END IF; IF p_type = 'id-static' OR p_type = 'id-dynamic' THEN -- If there is already data, start partitioning with the highest current value EXECUTE 'SELECT COALESCE(max('||p_control||')::bigint, 0) FROM '||p_parent_table||' LIMIT 1' INTO v_max; 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 IF (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_append(v_partition_id, (v_starting_partition_id - v_id_interval*i)); END IF; v_partition_id = array_append(v_partition_id, (v_id_interval*i) + v_starting_partition_id); END LOOP; INSERT INTO @extschema@.part_config (parent_table, type, part_interval, control, premake) VALUES (p_parent_table, p_type, v_id_interval, p_control, p_premake); EXECUTE 'SELECT @extschema@.create_id_partition('||quote_literal(p_parent_table)||','||quote_literal(p_control)||',' ||v_id_interval||','||quote_literal(v_partition_id)||')' INTO v_last_partition_name; -- Doing separate update because create function needs parent table in config table for apply_grants() UPDATE @extschema@.part_config SET last_partition = v_last_partition_name WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'ID partitions premade: '||p_premake); END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Creating partition function'); END IF; IF p_type = 'time-static' OR p_type = 'time-dynamic' THEN EXECUTE 'SELECT @extschema@.create_time_function('||quote_literal(p_parent_table)||')'; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Time function created'); END IF; ELSIF p_type = 'id-static' OR p_type = 'id-dynamic' THEN v_current_id := COALESCE(v_max, 0); EXECUTE 'SELECT @extschema@.create_id_function('||quote_literal(p_parent_table)||','||v_current_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; EXECUTE 'SELECT @extschema@.create_trigger('||quote_literal(p_parent_table)||')'; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN CREATE PARENT: '||p_parent_table); v_step_id := add_step(v_job_id, 'Partition creation for table '||p_parent_table||' failed'); ELSIF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'BAD', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; pg_partman-2.2.2/updates/pg_partman--0.4.2--1.0.0.sql000066400000000000000000001765271262146621700214370ustar00rootroot00000000000000-- 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. DROP FUNCTION create_prev_id_partition(text, int); DROP FUNCTION create_prev_time_partition(text, int); ALTER TABLE @extschema@.part_config ADD COLUMN undo_in_progress boolean NOT NULL DEFAULT false; /* * Populate the child table(s) of an id-based partition set with old data from the original parent */ CREATE FUNCTION partition_data_id(p_parent_table text, p_batch_interval int DEFAULT NULL, p_batch_count int DEFAULT 1) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_control text; v_last_partition_name text; v_max_partition_id bigint; v_min_control bigint; v_min_partition_id bigint; v_part_interval bigint; v_partition_id bigint[]; v_rowcount bigint; v_sql text; v_total_rows bigint := 0; BEGIN SELECT part_interval::bigint, control INTO v_part_interval, v_control FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF p_batch_interval IS NULL OR p_batch_interval > v_part_interval THEN p_batch_interval := v_part_interval; END IF; FOR i IN 1..p_batch_count LOOP EXECUTE 'SELECT min('||v_control||') FROM ONLY '||p_parent_table INTO v_min_control; IF v_min_control IS NULL THEN RETURN 0; END IF; v_min_partition_id = v_min_control - (v_min_control % v_part_interval); v_partition_id := ARRAY[v_min_partition_id]; RAISE NOTICE 'v_partition_id: %',v_partition_id; IF (v_min_control + p_batch_interval) >= (v_min_partition_id + v_part_interval) THEN v_max_partition_id := v_min_partition_id + v_part_interval; ELSE v_max_partition_id := v_min_control + p_batch_interval; END IF; RAISE NOTICE 'v_max_partition_id: %',v_max_partition_id; v_sql := 'SELECT @extschema@.create_id_partition('||quote_literal(p_parent_table)||','||quote_literal(v_control)||',' ||v_part_interval||','||quote_literal(v_partition_id)||')'; RAISE NOTICE 'v_sql: %', v_sql; EXECUTE v_sql INTO v_last_partition_name; v_sql := 'WITH partition_data AS ( DELETE FROM ONLY '||p_parent_table||' WHERE '||v_control||' >= '||v_min_control|| ' AND '||v_control||' < '||v_max_partition_id||' RETURNING *) INSERT INTO '||v_last_partition_name||' SELECT * FROM partition_data'; RAISE NOTICE 'v_sql: %', v_sql; EXECUTE v_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total_rows := v_total_rows + v_rowcount; IF v_rowcount = 0 THEN EXIT; END IF; END LOOP; RETURN v_total_rows; END $$; /* * Populate the child table(s) of a time-based partition set with old data from the original parent */ CREATE FUNCTION partition_data_time(p_parent_table text, p_batch_interval interval DEFAULT NULL, p_batch_count int DEFAULT 1) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_control text; v_datetime_string text; v_last_partition_name text; v_max_partition_timestamp timestamp; v_min_control timestamp; v_min_partition_timestamp timestamp; v_part_interval interval; v_partition_timestamp timestamp[]; v_rowcount bigint; v_sql text; v_total_rows bigint := 0; BEGIN SELECT part_interval::interval, control, datetime_string INTO v_part_interval, v_control, v_datetime_string FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF p_batch_interval IS NULL OR p_batch_interval > v_part_interval THEN p_batch_interval := v_part_interval; END IF; FOR i IN 1..p_batch_count LOOP EXECUTE 'SELECT min('||v_control||') FROM ONLY '||p_parent_table INTO v_min_control; IF v_min_control IS NULL THEN RETURN 0; END IF; CASE WHEN v_part_interval = '15 mins' THEN v_min_partition_timestamp := date_trunc('hour', v_min_control) + '15min'::interval * floor(date_part('minute', v_min_control) / 15.0); WHEN v_part_interval = '30 mins' THEN v_min_partition_timestamp := date_trunc('hour', v_min_control) + '30min'::interval * floor(date_part('minute', v_min_control) / 30.0); WHEN v_part_interval = '1 hour' THEN v_min_partition_timestamp := date_trunc('hour', v_min_control); WHEN v_part_interval = '1 day' THEN v_min_partition_timestamp := date_trunc('day', v_min_control); WHEN v_part_interval = '1 week' THEN v_min_partition_timestamp := date_trunc('week', v_min_control); WHEN v_part_interval = '1 month' THEN v_min_partition_timestamp := date_trunc('month', v_min_control); WHEN v_part_interval = '3 months' THEN v_min_partition_timestamp := date_trunc('quarter', v_min_control); WHEN v_part_interval = '1 year' THEN v_min_partition_timestamp := date_trunc('year', v_min_control); END CASE; v_partition_timestamp := ARRAY[v_min_partition_timestamp]; RAISE NOTICE 'v_partition_timestamp: %',v_partition_timestamp; IF (v_min_control + p_batch_interval) >= (v_min_partition_timestamp + v_part_interval) THEN v_max_partition_timestamp := v_min_partition_timestamp + v_part_interval; ELSE v_max_partition_timestamp := v_min_control + p_batch_interval; END IF; RAISE NOTICE 'v_max_partition_timestamp: %',v_max_partition_timestamp; v_sql := 'SELECT @extschema@.create_time_partition('||quote_literal(p_parent_table)||','||quote_literal(v_control)||',' ||quote_literal(v_part_interval)||','||quote_literal(v_datetime_string)||','||quote_literal(v_partition_timestamp)||')'; RAISE NOTICE 'v_sql: %', v_sql; EXECUTE v_sql INTO v_last_partition_name; v_sql := 'WITH partition_data AS ( DELETE FROM ONLY '||p_parent_table||' WHERE '||v_control||' >= '||quote_literal(v_min_control)|| ' AND '||v_control||' < '||quote_literal(v_max_partition_timestamp)||' RETURNING *) INSERT INTO '||v_last_partition_name||' SELECT * FROM partition_data'; RAISE NOTICE 'v_sql: %', v_sql; EXECUTE v_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total_rows := v_total_rows + v_rowcount; IF v_rowcount = 0 THEN EXIT; END IF; END LOOP; RETURN v_total_rows; END $$; /* * Function to manage pre-creation of the next partitions in a time-based partition set. * Also manages dropping old partitions if the retention option is set. */ CREATE OR REPLACE FUNCTION run_maintenance() RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_create_count int := 0; v_current_partition_timestamp timestamp; v_datetime_string text; v_drop_count int := 0; v_job_id bigint; v_jobmon_schema text; v_last_partition_timestamp timestamp; v_old_search_path text; v_premade_count real; v_quarter text; v_step_id bigint; v_row record; v_year text; BEGIN v_adv_lock := pg_try_advisory_lock(hashtext('pg_partman run_maintenance')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'Partman maintenance already running.'; RETURN; END IF; SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN RUN MAINTENANCE'); v_step_id := add_step(v_job_id, 'Running maintenance loop'); END IF; FOR v_row IN SELECT parent_table , type , part_interval::interval , control , premake , datetime_string , last_partition , undo_in_progress FROM @extschema@.part_config WHERE type = 'time-static' OR type = 'time-dynamic' LOOP CONTINUE WHEN v_row.undo_in_progress; CASE WHEN v_row.part_interval = '15 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0); WHEN v_row.part_interval = '30 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0); WHEN v_row.part_interval = '1 hour' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP); WHEN v_row.part_interval = '1 day' THEN v_current_partition_timestamp := date_trunc('day', CURRENT_TIMESTAMP); WHEN v_row.part_interval = '1 week' THEN v_current_partition_timestamp := date_trunc('week', CURRENT_TIMESTAMP); WHEN v_row.part_interval = '1 month' THEN v_current_partition_timestamp := date_trunc('month', CURRENT_TIMESTAMP); WHEN v_row.part_interval = '3 months' THEN v_current_partition_timestamp := date_trunc('quarter', CURRENT_TIMESTAMP); WHEN v_row.part_interval = '1 year' THEN v_current_partition_timestamp := date_trunc('year', CURRENT_TIMESTAMP); END CASE; IF v_row.part_interval != '3 months' THEN v_last_partition_timestamp := to_timestamp(substring(v_row.last_partition from char_length(v_row.parent_table||'_p')+1), v_row.datetime_string); ELSE -- to_timestamp doesn't recognize 'Q' date string formater. Handle it v_year := split_part(substring(v_row.last_partition from char_length(v_row.parent_table||'_p')+1), 'q', 1); v_quarter := split_part(substring(v_row.last_partition from char_length(v_row.parent_table||'_p')+1), 'q', 2); CASE WHEN v_quarter = '1' THEN v_last_partition_timestamp := to_timestamp(v_year || '-01-01', 'YYYY-MM-DD'); WHEN v_quarter = '2' THEN v_last_partition_timestamp := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); WHEN v_quarter = '3' THEN v_last_partition_timestamp := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); WHEN v_quarter = '4' THEN v_last_partition_timestamp := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); END CASE; END IF; -- Check and see how many premade partitions there are. If it's less than premake in config table, make another v_premade_count = EXTRACT('epoch' FROM age(v_last_partition_timestamp, v_current_partition_timestamp)) / EXTRACT('epoch' FROM v_row.part_interval::interval); -- 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 EXECUTE 'SELECT @extschema@.create_next_time_partition('||quote_literal(v_row.parent_table)||')'; v_create_count := v_create_count + 1; IF v_row.type = 'time-static' THEN EXECUTE 'SELECT @extschema@.create_time_function('||quote_literal(v_row.parent_table)||')'; END IF; v_last_partition_timestamp := v_last_partition_timestamp + v_row.part_interval; v_premade_count = EXTRACT('epoch' FROM age(v_last_partition_timestamp, v_current_partition_timestamp)) / EXTRACT('epoch' FROM v_row.part_interval::interval); END LOOP; END LOOP; -- end of creation loop -- Manage dropping old partitions if retention option is set FOR v_row IN SELECT parent_table FROM @extschema@.part_config WHERE retention IS NOT NULL AND undo_in_progress = false AND (type = 'time-static' OR type = 'time-dynamic') LOOP v_drop_count := v_drop_count + @extschema@.drop_time_partition(v_row.parent_table); END LOOP; FOR v_row IN SELECT parent_table FROM @extschema@.part_config WHERE retention IS NOT NULL AND undo_in_progress = false AND (type = 'id-static' OR type = 'id-dynamic') LOOP v_drop_count := v_drop_count + @extschema@.drop_id_partition(v_row.parent_table); END LOOP; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Partition maintenance finished. '||v_create_count||' partitons made. '||v_drop_count||' partitions dropped.'); PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; PERFORM pg_advisory_unlock(hashtext('pg_partman run_maintenance')); EXCEPTION WHEN QUERY_CANCELED THEN PERFORM pg_advisory_unlock(hashtext('pg_partman run_maintenance')); RAISE EXCEPTION '%', SQLERRM; WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN RUN MAINTENANCE'); v_step_id := add_step(v_job_id, 'EXCEPTION before job logging started'); END IF; IF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'CRITICAL', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; PERFORM pg_advisory_unlock(hashtext('pg_partman run_maintenance')); RAISE EXCEPTION '%', SQLERRM; END $$; /* * Create the trigger function for the parent table of an id-based partition set */ CREATE OR REPLACE FUNCTION create_id_function(p_parent_table text, p_current_id bigint) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_control text; v_current_partition_name text; v_current_partition_id bigint; v_datetime_string text; v_final_partition_id bigint; v_job_id bigint; v_jobmon_schema text; v_last_partition text; v_next_partition_id bigint; v_next_partition_name text; v_old_search_path text; v_part_interval bigint; v_premake int; v_prev_partition_id bigint; v_prev_partition_name text; v_step_id bigint; v_trig_func text; v_type text; BEGIN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE FUNCTION: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating partition function for table '||p_parent_table); END IF; SELECT type , part_interval::bigint , control , premake , last_partition INTO v_type , v_part_interval , v_control , v_premake , v_last_partition FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF v_type = 'id-static' THEN v_current_partition_id := p_current_id - (p_current_id % v_part_interval); v_next_partition_id := v_current_partition_id + v_part_interval; v_current_partition_name := p_parent_table || '_p' || v_current_partition_id::text; v_trig_func := 'CREATE OR REPLACE FUNCTION '||p_parent_table||'_part_trig_func() RETURNS trigger LANGUAGE plpgsql AS $t$ DECLARE v_current_partition_id bigint; v_last_partition text := '||quote_literal(v_last_partition)||'; v_next_partition_id bigint; v_next_partition_name text; BEGIN IF TG_OP = ''INSERT'' THEN IF NEW.'||v_control||' >= '||v_current_partition_id||' AND NEW.'||v_control||' < '||v_next_partition_id|| ' THEN INSERT INTO '||v_current_partition_name||' VALUES (NEW.*); '; FOR i IN 1..v_premake LOOP v_prev_partition_id := v_current_partition_id - (v_part_interval * i); v_next_partition_id := v_current_partition_id + (v_part_interval * i); v_final_partition_id := v_next_partition_id + v_part_interval; v_prev_partition_name := p_parent_table || '_p' || v_prev_partition_id::text; v_next_partition_name := p_parent_table || '_p' || v_next_partition_id::text; -- Only make previous partitions if they're starting above zero IF v_prev_partition_id >= 0 THEN v_trig_func := v_trig_func ||' ELSIF NEW.'||v_control||' >= '||v_prev_partition_id||' AND NEW.'||v_control||' < '||v_prev_partition_id + v_part_interval|| ' THEN INSERT INTO '||v_prev_partition_name||' VALUES (NEW.*); '; END IF; v_trig_func := v_trig_func ||' ELSIF NEW.'||v_control||' >= '||v_next_partition_id||' AND NEW.'||v_control||' < '||v_final_partition_id|| ' THEN INSERT INTO '||v_next_partition_name||' VALUES (NEW.*); '; END LOOP; v_trig_func := v_trig_func ||' ELSE RETURN NEW; END IF; v_current_partition_id := NEW.'||v_control||' - (NEW.'||v_control||' % '||v_part_interval||'); IF (NEW.'||v_control||' % '||v_part_interval||') > ('||v_part_interval||' / 2) THEN v_next_partition_id := (substring(v_last_partition from char_length('||quote_literal(p_parent_table||'_p')||')+1)::bigint) + '||v_part_interval||'; WHILE ((v_next_partition_id - v_current_partition_id) / '||v_part_interval||') <= '||v_premake||' LOOP v_next_partition_name := @extschema@.create_id_partition('||quote_literal(p_parent_table)||', '||quote_literal(v_control)||',' ||v_part_interval||', ARRAY[v_next_partition_id]); UPDATE @extschema@.part_config SET last_partition = v_next_partition_name WHERE parent_table = '||quote_literal(p_parent_table)||'; PERFORM @extschema@.create_id_function('||quote_literal(p_parent_table)||', NEW.'||v_control||'); v_next_partition_id := v_next_partition_id + '||v_part_interval||'; END LOOP; END IF; END IF; RETURN NULL; END $t$;'; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Added function for current id interval: '||v_current_partition_id||' to '||v_final_partition_id-1); END IF; ELSIF v_type = 'id-dynamic' THEN -- The return inside the partition creation check is there to keep really high ID values from creating new partitions. v_trig_func := 'CREATE OR REPLACE FUNCTION '||p_parent_table||'_part_trig_func() RETURNS trigger LANGUAGE plpgsql AS $t$ DECLARE v_count int; v_current_partition_id bigint; v_current_partition_name text; v_last_partition text := '||quote_literal(v_last_partition)||'; v_last_partition_id bigint; v_next_partition_id bigint; v_next_partition_name text; BEGIN IF TG_OP = ''INSERT'' THEN v_current_partition_id := NEW.'||v_control||' - (NEW.'||v_control||' % '||v_part_interval||'); v_current_partition_name := '''||p_parent_table||'_p''||v_current_partition_id; IF (NEW.'||v_control||' % '||v_part_interval||') > ('||v_part_interval||' / 2) THEN v_last_partition_id = substring(v_last_partition from char_length('||quote_literal(p_parent_table||'_p')||')+1)::bigint; v_next_partition_id := v_last_partition_id + '||v_part_interval||'; IF NEW.'||v_control||' >= v_next_partition_id THEN RETURN NEW; END IF; WHILE ((v_next_partition_id - v_current_partition_id) / '||v_part_interval||') <= '||v_premake||' LOOP v_next_partition_name := @extschema@.create_id_partition('||quote_literal(p_parent_table)||', '||quote_literal(v_control)||',' ||quote_literal(v_part_interval)||', ARRAY[v_next_partition_id]); IF v_next_partition_name IS NOT NULL THEN UPDATE @extschema@.part_config SET last_partition = v_next_partition_name WHERE parent_table = '||quote_literal(p_parent_table)||'; PERFORM @extschema@.create_id_function('||quote_literal(p_parent_table)||', NEW.'||v_control||'); END IF; v_next_partition_id := v_next_partition_id + '||v_part_interval||'; END LOOP; END IF; SELECT count(*) INTO v_count FROM pg_tables WHERE schemaname ||''.''|| tablename = v_current_partition_name; IF v_count > 0 THEN EXECUTE ''INSERT INTO ''||v_current_partition_name||'' VALUES($1.*)'' USING NEW; ELSE RETURN NEW; END IF; END IF; RETURN NULL; END $t$;'; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Added function for dynamic id table: '||p_parent_table); END IF; ELSE RAISE EXCEPTION 'ERROR: Invalid id partitioning type given: %', v_type; END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN CREATE FUNCTION: '||p_parent_table); v_step_id := add_step(v_job_id, 'Partition function maintenance for table '||p_parent_table||' failed'); ELSIF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'CRITICAL', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to create a child table in a time-based partition set */ CREATE OR REPLACE FUNCTION create_time_partition (p_parent_table text, p_control text, p_interval interval, p_datetime_string text, p_partition_times timestamp[]) RETURNS text LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_all text[] := ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER']; v_grantees text[]; v_job_id bigint; v_jobmon_schema text; v_old_search_path text; v_parent_grant record; v_parent_owner text; v_partition_name text; v_partition_timestamp_end timestamp; v_partition_timestamp_start timestamp; v_quarter text; v_revoke text[]; v_step_id bigint; v_tablename text; v_trunc_value text; v_time timestamp; v_year text; BEGIN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; SELECT tableowner INTO v_parent_owner FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; FOREACH v_time IN ARRAY p_partition_times LOOP v_partition_name := p_parent_table || '_p'; IF p_interval = '1 year' OR p_interval = '1 month' OR p_interval = '1 day' OR p_interval = '1 hour' OR p_interval = '30 mins' OR p_interval = '15 mins' THEN v_partition_name := v_partition_name || to_char(v_time, 'YYYY'); v_trunc_value := 'year'; IF p_interval = '1 month' OR p_interval = '1 day' OR p_interval = '1 hour' OR p_interval = '30 mins' OR p_interval = '15 mins' THEN v_partition_name := v_partition_name || '_' || to_char(v_time, 'MM'); v_trunc_value := 'month'; IF p_interval = '1 day' OR p_interval = '1 hour' OR p_interval = '30 mins' OR p_interval = '15 mins' THEN v_partition_name := v_partition_name || '_' || to_char(v_time, 'DD'); v_trunc_value := 'day'; IF p_interval = '1 hour' OR p_interval = '30 mins' OR p_interval = '15 mins' THEN v_partition_name := v_partition_name || '_' || to_char(v_time, 'HH24'); IF p_interval <> '30 mins' AND p_interval <> '15 mins' THEN v_partition_name := v_partition_name || '00'; v_trunc_value := 'hour'; ELSIF p_interval = '15 mins' THEN IF date_part('minute', v_time) < 15 THEN v_partition_name := v_partition_name || '00'; ELSIF date_part('minute', v_time) >= 15 AND date_part('minute', v_time) < 30 THEN v_partition_name := v_partition_name || '15'; ELSIF date_part('minute', v_time) >= 30 AND date_part('minute', v_time) < 45 THEN v_partition_name := v_partition_name || '30'; ELSE v_partition_name := v_partition_name || '45'; END IF; v_trunc_value := 'minute'; ELSIF p_interval = '30 mins' THEN IF date_part('minute', v_time) < 30 THEN v_partition_name := v_partition_name || '00'; ELSE v_partition_name := v_partition_name || '30'; END IF; v_trunc_value := 'minute'; END IF; END IF; -- end hour IF END IF; -- end day IF END IF; -- end month IF ELSIF p_interval = '1 week' THEN v_partition_name := v_partition_name || to_char(v_time, 'IYYY') || 'w' || to_char(v_time, 'IW'); v_trunc_value := 'week'; END IF; -- end year/week IF -- pull out datetime portion of last partition's tablename if it matched one of the above partitioning intervals IF v_trunc_value IS NOT NULL THEN v_partition_timestamp_start := date_trunc(v_trunc_value, to_timestamp(substring(v_partition_name from char_length(p_parent_table||'_p')+1), p_datetime_string)); v_partition_timestamp_end := date_trunc(v_trunc_value, to_timestamp(substring(v_partition_name from char_length(p_parent_table||'_p')+1), p_datetime_string) + p_interval); END IF; -- "Q" is ignored in to_timestamp, so handle special case IF p_interval = '3 months' THEN v_year := to_char(v_time, 'YYYY'); v_quarter := to_char(v_time, 'Q'); v_partition_name := v_partition_name || v_year || 'q' || v_quarter; v_trunc_value := 'quarter'; CASE WHEN v_quarter = '1' THEN v_partition_timestamp_start := date_trunc(v_trunc_value, to_timestamp(v_year || '-01-01', 'YYYY-MM-DD')); WHEN v_quarter = '2' THEN v_partition_timestamp_start := date_trunc(v_trunc_value, to_timestamp(v_year || '-04-01', 'YYYY-MM-DD')); WHEN v_quarter = '3' THEN v_partition_timestamp_start := date_trunc(v_trunc_value, to_timestamp(v_year || '-07-01', 'YYYY-MM-DD')); WHEN v_quarter = '4' THEN v_partition_timestamp_start := date_trunc(v_trunc_value, to_timestamp(v_year || '-10-01', 'YYYY-MM-DD')); END CASE; v_partition_timestamp_end := date_trunc(v_trunc_value, (v_partition_timestamp_start + p_interval)); END IF; SELECT schemaname ||'.'|| tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_tablename IS NOT NULL THEN CONTINUE; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating new partition '||v_partition_name||' with interval from '||v_partition_timestamp_start||' to '||(v_partition_timestamp_end-'1sec'::interval)); END IF; IF position('.' in p_parent_table) > 0 THEN v_tablename := substring(v_partition_name from position('.' in v_partition_name)+1); END IF; EXECUTE 'CREATE TABLE '||v_partition_name||' (LIKE '||p_parent_table||' INCLUDING DEFAULTS INCLUDING INDEXES)'; EXECUTE 'ALTER TABLE '||v_partition_name||' ADD CONSTRAINT '||v_tablename||'_partition_check CHECK ('||p_control||'>='||quote_literal(v_partition_timestamp_start)||' AND '||p_control||'<'||quote_literal(v_partition_timestamp_end)||')'; EXECUTE 'ALTER TABLE '||v_partition_name||' INHERIT '||p_parent_table; FOR v_parent_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = p_parent_table GROUP BY grantee LOOP EXECUTE 'GRANT '||array_to_string(v_parent_grant.types, ',')||' ON '||v_partition_name||' TO '||v_parent_grant.grantee; SELECT array_agg(r) INTO v_revoke FROM (SELECT unnest(v_all) AS r EXCEPT SELECT unnest(v_parent_grant.types)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE '||array_to_string(v_revoke, ',')||' ON '||v_partition_name||' FROM '||v_parent_grant.grantee||' CASCADE'; END IF; v_grantees := array_append(v_grantees, v_parent_grant.grantee::text); END LOOP; -- Revoke all privileges from roles that have none on the parent IF v_grantees IS NOT NULL THEN SELECT array_agg(r) INTO v_revoke FROM ( SELECT DISTINCT grantee::text AS r FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = v_partition_name EXCEPT SELECT unnest(v_grantees)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE ALL ON '||v_partition_name||' FROM '||array_to_string(v_revoke, ','); END IF; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' OWNER TO '||v_parent_owner; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); PERFORM close_job(v_job_id); END IF; END LOOP; IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_partition_name; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); v_step_id := add_step(v_job_id, 'Partition maintenance for table '||p_parent_table||' failed'); ELSIF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'CRITICAL', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Create the trigger function for the parent table of a time-based partition set */ CREATE OR REPLACE FUNCTION create_time_function(p_parent_table text) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_control text; v_current_partition_name text; v_current_partition_timestamp timestamptz; v_datetime_string text; v_final_partition_timestamp timestamptz; v_job_id bigint; v_jobmon_schema text; v_old_search_path text; v_next_partition_name text; v_next_partition_timestamp timestamptz; v_part_interval interval; v_premake int; v_prev_partition_name text; v_prev_partition_timestamp timestamptz; v_step_id bigint; v_trig_func text; v_type text; BEGIN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE FUNCTION: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating partition function for table '||p_parent_table); END IF; SELECT type , part_interval::interval , control , premake , datetime_string INTO v_type , v_part_interval , v_control , v_premake , v_datetime_string FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF v_type = 'time-static' THEN CASE WHEN v_part_interval = '15 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0); WHEN v_part_interval = '30 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0); WHEN v_part_interval = '1 hour' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP); WHEN v_part_interval = '1 day' THEN v_current_partition_timestamp := date_trunc('day', CURRENT_TIMESTAMP); WHEN v_part_interval = '1 week' THEN v_current_partition_timestamp := date_trunc('week', CURRENT_TIMESTAMP); WHEN v_part_interval = '1 month' THEN v_current_partition_timestamp := date_trunc('month', CURRENT_TIMESTAMP); WHEN v_part_interval = '3 months' THEN v_current_partition_timestamp := date_trunc('quarter', CURRENT_TIMESTAMP); WHEN v_part_interval = '1 year' THEN v_current_partition_timestamp := date_trunc('year', CURRENT_TIMESTAMP); END CASE; v_current_partition_name := p_parent_table || '_p' || to_char(v_current_partition_timestamp, v_datetime_string); v_next_partition_timestamp := v_current_partition_timestamp + v_part_interval::interval; v_trig_func := 'CREATE OR REPLACE FUNCTION '||p_parent_table||'_part_trig_func() RETURNS trigger LANGUAGE plpgsql AS $t$ BEGIN IF TG_OP = ''INSERT'' THEN IF NEW.'||v_control||' >= '||quote_literal(v_current_partition_timestamp)||' AND NEW.'||v_control||' < '||quote_literal(v_next_partition_timestamp)|| ' THEN INSERT INTO '||v_current_partition_name||' VALUES (NEW.*); '; FOR i IN 1..v_premake LOOP v_prev_partition_timestamp := v_current_partition_timestamp - (v_part_interval::interval * i); v_next_partition_timestamp := v_current_partition_timestamp + (v_part_interval::interval * i); v_final_partition_timestamp := v_next_partition_timestamp + (v_part_interval::interval); v_prev_partition_name := p_parent_table || '_p' || to_char(v_prev_partition_timestamp, v_datetime_string); v_next_partition_name := p_parent_table || '_p' || to_char(v_next_partition_timestamp, v_datetime_string); v_trig_func := v_trig_func ||' ELSIF NEW.'||v_control||' >= '||quote_literal(v_prev_partition_timestamp)||' AND NEW.'||v_control||' < '|| quote_literal(v_prev_partition_timestamp + v_part_interval::interval)|| ' THEN INSERT INTO '||v_prev_partition_name||' VALUES (NEW.*); ELSIF NEW.'||v_control||' >= '||quote_literal(v_next_partition_timestamp)||' AND NEW.'||v_control||' < '|| quote_literal(v_final_partition_timestamp)|| ' THEN INSERT INTO '||v_next_partition_name||' VALUES (NEW.*); '; END LOOP; v_trig_func := v_trig_func ||' ELSE RETURN NEW; END IF; END IF; RETURN NULL; END $t$;'; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Added function for current time interval: '|| v_current_partition_timestamp||' to '||(v_final_partition_timestamp-'1sec'::interval)); END IF; ELSIF v_type = 'time-dynamic' THEN v_trig_func := 'CREATE OR REPLACE FUNCTION '||p_parent_table||'_part_trig_func() RETURNS trigger LANGUAGE plpgsql AS $t$ DECLARE v_count int; v_partition_name text; v_partition_timestamp timestamptz; v_schemaname text; v_tablename text; BEGIN IF TG_OP = ''INSERT'' THEN '; CASE WHEN v_part_interval = '15 mins' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''hour'', NEW.'||v_control||') + ''15min''::interval * floor(date_part(''minute'', NEW.'||v_control||') / 15.0);'; WHEN v_part_interval = '30 mins' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''hour'', NEW.'||v_control||') + ''30min''::interval * floor(date_part(''minute'', NEW.'||v_control||') / 30.0);'; WHEN v_part_interval = '1 hour' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''hour'', NEW.'||v_control||');'; WHEN v_part_interval = '1 day' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''day'', NEW.'||v_control||');'; WHEN v_part_interval = '1 week' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''week'', NEW.'||v_control||');'; WHEN v_part_interval = '1 month' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''month'', NEW.'||v_control||');'; WHEN v_part_interval = '3 months' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''quarter'', NEW.'||v_control||');'; WHEN v_part_interval = '1 year' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''year'', NEW.'||v_control||');'; END CASE; v_trig_func := v_trig_func||' v_partition_name := '''||p_parent_table||'_p''|| to_char(v_partition_timestamp, '||quote_literal(v_datetime_string)||'); v_schemaname := split_part(v_partition_name, ''.'', 1); v_tablename := split_part(v_partition_name, ''.'', 2); SELECT count(*) INTO v_count FROM pg_tables WHERE schemaname = v_schemaname AND tablename = v_tablename; IF v_count > 0 THEN EXECUTE ''INSERT INTO ''||v_partition_name||'' VALUES($1.*)'' USING NEW; ELSE RETURN NEW; END IF; END IF; RETURN NULL; END $t$;'; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Added function for dynamic time table: '||p_parent_table); END IF; ELSE RAISE EXCEPTION 'ERROR: Invalid time partitioning type given: %', v_type; END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN CREATE FUNCTION: '||p_parent_table); v_step_id := add_step(v_job_id, 'Partition function maintenance for table '||p_parent_table||' failed'); ELSIF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'CRITICAL', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to undo partitioning. * Will actually work on any parent/child table set, not just ones created by pg_partman. */ CREATE FUNCTION undo_partition(p_parent_table text, p_batch_count int DEFAULT 1, p_keep_table boolean DEFAULT true) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_child_table text; v_copy_sql text; v_job_id bigint; v_jobmon_schema text; v_old_search_path text; v_part_interval interval; v_rowcount bigint; v_step_id bigint; v_tablename text; v_total bigint := 0; v_undo_count int := 0; BEGIN v_adv_lock := pg_try_advisory_lock(hashtext('pg_partman undo_partition')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'undo_partition already running.'; RETURN 0; END IF; SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN UNDO PARTITIONING: '||p_parent_table); v_step_id := add_step(v_job_id, 'Undoing partitioning for table '||p_parent_table); END IF; -- Stops new time partitons 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; -- Stop data going into child tables and stop new id partitions from being made. v_tablename := substring(p_parent_table from position('.' in p_parent_table)+1); EXECUTE 'DROP TRIGGER IF EXISTS '||v_tablename||'_part_trig ON '||p_parent_table; EXECUTE 'DROP FUNCTION IF EXISTS '||p_parent_table||'_part_trig_func()'; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Stopped partition creation process. Removed trigger & trigger function'); END IF; FOR i IN 1..p_batch_count LOOP SELECT inhrelid::regclass INTO v_child_table FROM pg_catalog.pg_inherits WHERE inhparent::regclass = p_parent_table::regclass ORDER BY inhrelid::regclass::text ASC; EXIT WHEN v_child_table IS NULL; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing child partition: '||v_child_table); END IF; v_copy_sql := 'INSERT INTO '||p_parent_table||' SELECT * FROM '||v_child_table; EXECUTE v_copy_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total := v_total + v_rowcount; EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; IF p_keep_table = false THEN EXECUTE 'DROP TABLE '||v_child_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table DROPPED. Moved '||v_rowcount||' rows to parent'); END IF; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table UNINHERITED, not DROPPED. Copied '||v_rowcount||' rows to parent'); END IF; END IF; v_undo_count := v_undo_count + 1; END LOOP; IF v_undo_count = 0 THEN -- FOR loop never ran, so there's no child tables left. DELETE FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing config from pg_partman (if it existed)'); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; RAISE NOTICE 'Copied % row(s) from % child table(s) to the parent: %', v_total, v_undo_count, p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Final stats'); PERFORM update_step(v_step_id, 'OK', 'Copied '||v_total||' row(s) from '||v_undo_count||' child table(s) to the parent'); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; PERFORM pg_advisory_unlock(hashtext('pg_partman undo_partition')); RETURN v_total; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN UNDO PARTITIONING: '||p_parent_table); v_step_id := add_step(v_job_id, 'Partition function maintenance for table '||p_parent_table||' failed'); ELSIF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'CRITICAL', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to undo time-based partitioning created by this extension */ CREATE FUNCTION undo_partition_time(p_parent_table text, p_batch_count int DEFAULT 1, p_batch_interval interval DEFAULT NULL, p_keep_table boolean DEFAULT true) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_batch_loop_count int := 0; v_child_min timestamptz; v_child_loop_total bigint := 0; v_child_table text; v_control text; v_inner_loop_count int; v_job_id bigint; v_jobmon_schema text; v_move_sql text; v_old_search_path text; v_part_interval interval; v_row record; v_rowcount bigint; v_step_id bigint; v_tablename text; v_total bigint := 0; v_undo_count int := 0; BEGIN v_adv_lock := pg_try_advisory_lock(hashtext('pg_partman undo_time_partition')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'undo_time_partition already running.'; RETURN 0; END IF; SELECT part_interval::interval , control INTO v_part_interval , v_control FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic'); IF v_part_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN UNDO PARTITIONING: '||p_parent_table); v_step_id := add_step(v_job_id, 'Undoing partitioning for table '||p_parent_table); END IF; IF p_batch_interval IS NULL THEN p_batch_interval := v_part_interval; END IF; -- Stops new time partitons 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; -- Stop data going into child tables and stop new id partitions from being made. v_tablename := substring(p_parent_table from position('.' in p_parent_table)+1); EXECUTE 'DROP TRIGGER IF EXISTS '||v_tablename||'_part_trig ON '||p_parent_table; EXECUTE 'DROP FUNCTION IF EXISTS '||p_parent_table||'_part_trig_func()'; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Stopped partition creation process. Removed trigger & trigger function'); END IF; <> WHILE v_batch_loop_count < p_batch_count LOOP SELECT inhrelid::regclass INTO v_child_table FROM pg_catalog.pg_inherits WHERE inhparent::regclass = p_parent_table::regclass ORDER BY inhrelid::regclass::text ASC; EXIT WHEN v_child_table IS NULL; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing child partition: '||v_child_table); END IF; EXECUTE 'SELECT min('||v_control||') FROM '||v_child_table INTO v_child_min; IF v_child_min IS NULL THEN -- No rows left in this child table. Remove from partition set. EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; IF p_keep_table = false THEN EXECUTE 'DROP TABLE '||v_child_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table DROPPED. Moved '||v_child_loop_total||' rows to parent'); END IF; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table UNINHERITED, not DROPPED. Moved '||v_child_loop_total||' rows to parent'); END IF; END IF; v_undo_count := v_undo_count + 1; CONTINUE outer_child_loop; END IF; v_inner_loop_count := 1; v_child_loop_total := 0; <> LOOP -- Get everything from the current child minimum up to the multiples of the given interval v_move_sql := 'WITH move_data AS (DELETE FROM '||v_child_table|| ' WHERE '||v_control||' <= '||quote_literal(v_child_min + (p_batch_interval * v_inner_loop_count))||' RETURNING *) INSERT INTO '||p_parent_table||' SELECT * FROM move_data'; EXECUTE v_move_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total := v_total + v_rowcount; v_child_loop_total := v_child_loop_total + v_rowcount; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Moved '||v_child_loop_total||' rows to parent.'); END IF; EXIT inner_child_loop WHEN v_rowcount = 0; -- exit before loop incr if table is empty v_inner_loop_count := v_inner_loop_count + 1; v_batch_loop_count := v_batch_loop_count + 1; EXIT outer_child_loop WHEN v_batch_loop_count >= p_batch_count; -- Exit outer FOR loop if p_batch_count is reached END LOOP inner_child_loop; END LOOP outer_child_loop; IF v_batch_loop_count < p_batch_count THEN -- FOR loop never ran, so there's no child tables left. DELETE FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing config from pg_partman'); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; RAISE NOTICE 'Copied % row(s) to the parent. Removed % partitions.', v_total, v_undo_count; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Final stats'); PERFORM update_step(v_step_id, 'OK', 'Copied '||v_total||' row(s) to the parent. Removed '||v_undo_count||' partitions.'); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; PERFORM pg_advisory_unlock(hashtext('pg_partman undo_time_partition')); RETURN v_total; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN UNDO PARTITIONING: '||p_parent_table); v_step_id := add_step(v_job_id, 'Partition function maintenance for table '||p_parent_table||' failed'); ELSIF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'CRITICAL', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to undo id-based partitioning created by this extension */ CREATE FUNCTION undo_partition_id(p_parent_table text, p_batch_count int DEFAULT 1, p_batch_interval bigint DEFAULT NULL, p_keep_table boolean DEFAULT true) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_batch_loop_count int := 0; v_child_loop_total bigint := 0; v_child_min bigint; v_child_table text; v_control text; v_inner_loop_count int; v_job_id bigint; v_jobmon_schema text; v_move_sql text; v_old_search_path text; v_part_interval bigint; v_row record; v_rowcount bigint; v_step_id bigint; v_tablename text; v_total bigint := 0; v_undo_count int := 0; BEGIN v_adv_lock := pg_try_advisory_lock(hashtext('pg_partman undo_id_partition')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'undo_id_partition already running.'; RETURN 0; END IF; SELECT part_interval::bigint , control INTO v_part_interval , v_control FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic'); IF v_part_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN UNDO PARTITIONING: '||p_parent_table); v_step_id := add_step(v_job_id, 'Undoing partitioning for table '||p_parent_table); END IF; IF p_batch_interval IS NULL THEN p_batch_interval := v_part_interval; END IF; -- Stops new time partitons 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; -- Stop data going into child tables and stop new id partitions from being made. v_tablename := substring(p_parent_table from position('.' in p_parent_table)+1); EXECUTE 'DROP TRIGGER IF EXISTS '||v_tablename||'_part_trig ON '||p_parent_table; EXECUTE 'DROP FUNCTION IF EXISTS '||p_parent_table||'_part_trig_func()'; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Stopped partition creation process. Removed trigger & trigger function'); END IF; <> WHILE v_batch_loop_count < p_batch_count LOOP SELECT inhrelid::regclass INTO v_child_table FROM pg_catalog.pg_inherits WHERE inhparent::regclass = p_parent_table::regclass ORDER BY inhrelid::regclass::text ASC; EXIT WHEN v_child_table IS NULL; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing child partition: '||v_child_table); END IF; EXECUTE 'SELECT min('||v_control||') FROM '||v_child_table INTO v_child_min; IF v_child_min IS NULL THEN -- No rows left in this child table. Remove from partition set. EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; IF p_keep_table = false THEN EXECUTE 'DROP TABLE '||v_child_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table DROPPED. Moved '||v_child_loop_total||' rows to parent'); END IF; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table UNINHERITED, not DROPPED. Moved '||v_child_loop_total||' rows to parent'); END IF; END IF; v_undo_count := v_undo_count + 1; CONTINUE outer_child_loop; END IF; v_inner_loop_count := 1; v_child_loop_total := 0; <> LOOP -- Get everything from the current child minimum up to the multiples of the given interval v_move_sql := 'WITH move_data AS (DELETE FROM '||v_child_table|| ' WHERE '||v_control||' <= '||quote_literal(v_child_min + (p_batch_interval * v_inner_loop_count))||' RETURNING *) INSERT INTO '||p_parent_table||' SELECT * FROM move_data'; EXECUTE v_move_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total := v_total + v_rowcount; v_child_loop_total := v_child_loop_total + v_rowcount; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Moved '||v_child_loop_total||' rows to parent.'); END IF; EXIT inner_child_loop WHEN v_rowcount = 0; -- exit before loop incr if table is empty v_inner_loop_count := v_inner_loop_count + 1; v_batch_loop_count := v_batch_loop_count + 1; EXIT outer_child_loop WHEN v_batch_loop_count >= p_batch_count; -- Exit outer FOR loop if p_batch_count is reached END LOOP inner_child_loop; END LOOP outer_child_loop; IF v_batch_loop_count < p_batch_count THEN -- FOR loop never ran, so there's no child tables left. DELETE FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing config from pg_partman'); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; RAISE NOTICE 'Copied % row(s) to the parent. Removed % partitions.', v_total, v_undo_count; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Final stats'); PERFORM update_step(v_step_id, 'OK', 'Copied '||v_total||' row(s) to the parent. Removed '||v_undo_count||' partitions.'); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; PERFORM pg_advisory_unlock(hashtext('pg_partman undo_id_partition')); RETURN v_total; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN UNDO PARTITIONING: '||p_parent_table); v_step_id := add_step(v_job_id, 'Partition function maintenance for table '||p_parent_table||' failed'); ELSIF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'CRITICAL', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; pg_partman-2.2.2/updates/pg_partman--1.0.0--1.1.0.sql000066400000000000000000000667521262146621700214310ustar00rootroot00000000000000-- 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. DROP FUNCTION @extschema@.partition_data_id(text, int, int); DROP FUNCTION @extschema@.partition_data_time(text, interval, int); /* * Populate the child table(s) of an id-based partition set with old data from the original parent */ CREATE FUNCTION partition_data_id(p_parent_table text, p_batch_count int DEFAULT 1, p_batch_interval int DEFAULT NULL) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_control text; v_last_partition_name text; v_max_partition_id bigint; v_min_control bigint; v_min_partition_id bigint; v_part_interval bigint; v_partition_id bigint[]; v_rowcount bigint; v_sql text; v_total_rows bigint := 0; BEGIN SELECT part_interval::bigint, control INTO v_part_interval, v_control FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF p_batch_interval IS NULL OR p_batch_interval > v_part_interval THEN p_batch_interval := v_part_interval; END IF; FOR i IN 1..p_batch_count LOOP EXECUTE 'SELECT min('||v_control||') FROM ONLY '||p_parent_table INTO v_min_control; IF v_min_control IS NULL THEN RETURN 0; END IF; v_min_partition_id = v_min_control - (v_min_control % v_part_interval); v_partition_id := ARRAY[v_min_partition_id]; -- RAISE NOTICE 'v_partition_id: %',v_partition_id; IF (v_min_control + p_batch_interval) >= (v_min_partition_id + v_part_interval) THEN v_max_partition_id := v_min_partition_id + v_part_interval; ELSE v_max_partition_id := v_min_control + p_batch_interval; END IF; -- RAISE NOTICE 'v_max_partition_id: %',v_max_partition_id; v_sql := 'SELECT @extschema@.create_id_partition('||quote_literal(p_parent_table)||','||quote_literal(v_control)||',' ||v_part_interval||','||quote_literal(v_partition_id)||')'; -- RAISE NOTICE 'v_sql: %', v_sql; EXECUTE v_sql INTO v_last_partition_name; v_sql := 'WITH partition_data AS ( DELETE FROM ONLY '||p_parent_table||' WHERE '||v_control||' >= '||v_min_control|| ' AND '||v_control||' < '||v_max_partition_id||' RETURNING *) INSERT INTO '||v_last_partition_name||' SELECT * FROM partition_data'; -- RAISE NOTICE 'v_sql: %', v_sql; EXECUTE v_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total_rows := v_total_rows + v_rowcount; IF v_rowcount = 0 THEN EXIT; END IF; END LOOP; RETURN v_total_rows; END $$; /* * Populate the child table(s) of a time-based partition set with old data from the original parent */ CREATE FUNCTION partition_data_time(p_parent_table text, p_batch_count int DEFAULT 1, p_batch_interval interval DEFAULT NULL) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_control text; v_datetime_string text; v_last_partition_name text; v_max_partition_timestamp timestamp; v_min_control timestamp; v_min_partition_timestamp timestamp; v_part_interval interval; v_partition_timestamp timestamp[]; v_rowcount bigint; v_sql text; v_total_rows bigint := 0; BEGIN SELECT part_interval::interval, control, datetime_string INTO v_part_interval, v_control, v_datetime_string FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF p_batch_interval IS NULL OR p_batch_interval > v_part_interval THEN p_batch_interval := v_part_interval; END IF; FOR i IN 1..p_batch_count LOOP EXECUTE 'SELECT min('||v_control||') FROM ONLY '||p_parent_table INTO v_min_control; IF v_min_control IS NULL THEN RETURN 0; END IF; CASE WHEN v_part_interval = '15 mins' THEN v_min_partition_timestamp := date_trunc('hour', v_min_control) + '15min'::interval * floor(date_part('minute', v_min_control) / 15.0); WHEN v_part_interval = '30 mins' THEN v_min_partition_timestamp := date_trunc('hour', v_min_control) + '30min'::interval * floor(date_part('minute', v_min_control) / 30.0); WHEN v_part_interval = '1 hour' THEN v_min_partition_timestamp := date_trunc('hour', v_min_control); WHEN v_part_interval = '1 day' THEN v_min_partition_timestamp := date_trunc('day', v_min_control); WHEN v_part_interval = '1 week' THEN v_min_partition_timestamp := date_trunc('week', v_min_control); WHEN v_part_interval = '1 month' THEN v_min_partition_timestamp := date_trunc('month', v_min_control); WHEN v_part_interval = '3 months' THEN v_min_partition_timestamp := date_trunc('quarter', v_min_control); WHEN v_part_interval = '1 year' THEN v_min_partition_timestamp := date_trunc('year', v_min_control); END CASE; v_partition_timestamp := ARRAY[v_min_partition_timestamp]; -- RAISE NOTICE 'v_partition_timestamp: %',v_partition_timestamp; IF (v_min_control + p_batch_interval) >= (v_min_partition_timestamp + v_part_interval) THEN v_max_partition_timestamp := v_min_partition_timestamp + v_part_interval; ELSE v_max_partition_timestamp := v_min_control + p_batch_interval; END IF; -- RAISE NOTICE 'v_max_partition_timestamp: %',v_max_partition_timestamp; v_sql := 'SELECT @extschema@.create_time_partition('||quote_literal(p_parent_table)||','||quote_literal(v_control)||',' ||quote_literal(v_part_interval)||','||quote_literal(v_datetime_string)||','||quote_literal(v_partition_timestamp)||')'; -- RAISE NOTICE 'v_sql: %', v_sql; EXECUTE v_sql INTO v_last_partition_name; v_sql := 'WITH partition_data AS ( DELETE FROM ONLY '||p_parent_table||' WHERE '||v_control||' >= '||quote_literal(v_min_control)|| ' AND '||v_control||' < '||quote_literal(v_max_partition_timestamp)||' RETURNING *) INSERT INTO '||v_last_partition_name||' SELECT * FROM partition_data'; -- RAISE NOTICE 'v_sql: %', v_sql; EXECUTE v_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total_rows := v_total_rows + v_rowcount; IF v_rowcount = 0 THEN EXIT; END IF; END LOOP; RETURN v_total_rows; END $$; /* * Function to undo partitioning. * Will actually work on any parent/child table set, not just ones created by pg_partman. */ CREATE OR REPLACE FUNCTION undo_partition(p_parent_table text, p_batch_count int DEFAULT 1, p_keep_table boolean DEFAULT true) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_batch_loop_count bigint := 0; v_child_count bigint; v_child_table text; v_copy_sql text; v_job_id bigint; v_jobmon_schema text; v_old_search_path text; v_part_interval interval; v_rowcount bigint; v_step_id bigint; v_tablename text; v_total bigint := 0; v_undo_count int := 0; BEGIN v_adv_lock := pg_try_advisory_lock(hashtext('pg_partman undo_partition')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'undo_partition already running.'; RETURN 0; END IF; SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN UNDO PARTITIONING: '||p_parent_table); v_step_id := add_step(v_job_id, 'Undoing partitioning for table '||p_parent_table); END IF; -- Stops new time partitons 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; -- Stop data going into child tables and stop new id partitions from being made. v_tablename := substring(p_parent_table from position('.' in p_parent_table)+1); EXECUTE 'DROP TRIGGER IF EXISTS '||v_tablename||'_part_trig ON '||p_parent_table; EXECUTE 'DROP FUNCTION IF EXISTS '||p_parent_table||'_part_trig_func()'; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Stopped partition creation process. Removed trigger & trigger function'); END IF; WHILE v_batch_loop_count < p_batch_count LOOP SELECT inhrelid::regclass INTO v_child_table FROM pg_catalog.pg_inherits WHERE inhparent::regclass = p_parent_table::regclass ORDER BY inhrelid ASC; EXIT WHEN v_child_table IS NULL; EXECUTE 'SELECT count(*) FROM '||v_child_table INTO v_child_count; IF v_child_count = 0 THEN -- No rows left in this child table. Remove from partition set. EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; IF p_keep_table = false THEN EXECUTE 'DROP TABLE '||v_child_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table DROPPED. Moved '||coalesce(v_rowcount, 0)||' rows to parent'); END IF; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table UNINHERITED, not DROPPED. Copied '||coalesce(v_rowcount, 0)||' rows to parent'); END IF; END IF; v_undo_count := v_undo_count + 1; CONTINUE; END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing child partition: '||v_child_table); END IF; v_copy_sql := 'INSERT INTO '||p_parent_table||' SELECT * FROM '||v_child_table; EXECUTE v_copy_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total := v_total + v_rowcount; EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; IF p_keep_table = false THEN EXECUTE 'DROP TABLE '||v_child_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table DROPPED. Moved '||v_rowcount||' rows to parent'); END IF; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table UNINHERITED, not DROPPED. Copied '||v_rowcount||' rows to parent'); END IF; END IF; v_batch_loop_count := v_batch_loop_count + 1; v_undo_count := v_undo_count + 1; END LOOP; IF v_undo_count = 0 THEN -- FOR loop never ran, so there's no child tables left. DELETE FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing config from pg_partman (if it existed)'); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; RAISE NOTICE 'Copied % row(s) from % child table(s) to the parent: %', v_total, v_undo_count, p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Final stats'); PERFORM update_step(v_step_id, 'OK', 'Copied '||v_total||' row(s) from '||v_undo_count||' child table(s) to the parent'); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; PERFORM pg_advisory_unlock(hashtext('pg_partman undo_partition')); RETURN v_total; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN UNDO PARTITIONING: '||p_parent_table); v_step_id := add_step(v_job_id, 'Partition function maintenance for table '||p_parent_table||' failed'); ELSIF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'CRITICAL', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to undo id-based partitioning created by this extension */ CREATE OR REPLACE FUNCTION undo_partition_id(p_parent_table text, p_batch_count int DEFAULT 1, p_batch_interval bigint DEFAULT NULL, p_keep_table boolean DEFAULT true) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_batch_loop_count int := 0; v_child_loop_total bigint := 0; v_child_min bigint; v_child_table text; v_control text; v_inner_loop_count int; v_job_id bigint; v_jobmon_schema text; v_move_sql text; v_old_search_path text; v_part_interval bigint; v_row record; v_rowcount bigint; v_step_id bigint; v_tablename text; v_total bigint := 0; v_undo_count int := 0; BEGIN v_adv_lock := pg_try_advisory_lock(hashtext('pg_partman undo_id_partition')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'undo_id_partition already running.'; RETURN 0; END IF; SELECT part_interval::bigint , control INTO v_part_interval , v_control FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic'); IF v_part_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN UNDO PARTITIONING: '||p_parent_table); v_step_id := add_step(v_job_id, 'Undoing partitioning for table '||p_parent_table); END IF; IF p_batch_interval IS NULL THEN p_batch_interval := v_part_interval; END IF; -- Stops new time partitons 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; -- Stop data going into child tables and stop new id partitions from being made. v_tablename := substring(p_parent_table from position('.' in p_parent_table)+1); EXECUTE 'DROP TRIGGER IF EXISTS '||v_tablename||'_part_trig ON '||p_parent_table; EXECUTE 'DROP FUNCTION IF EXISTS '||p_parent_table||'_part_trig_func()'; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Stopped partition creation process. Removed trigger & trigger function'); END IF; <> WHILE v_batch_loop_count < p_batch_count LOOP SELECT inhrelid::regclass INTO v_child_table FROM pg_catalog.pg_inherits WHERE inhparent::regclass = p_parent_table::regclass ORDER BY inhrelid ASC; EXIT WHEN v_child_table IS NULL; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing child partition: '||v_child_table); END IF; EXECUTE 'SELECT min('||v_control||') FROM '||v_child_table INTO v_child_min; IF v_child_min IS NULL THEN -- No rows left in this child table. Remove from partition set. EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; IF p_keep_table = false THEN EXECUTE 'DROP TABLE '||v_child_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table DROPPED. Moved '||v_child_loop_total||' rows to parent'); END IF; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table UNINHERITED, not DROPPED. Moved '||v_child_loop_total||' rows to parent'); END IF; END IF; v_undo_count := v_undo_count + 1; CONTINUE outer_child_loop; END IF; v_inner_loop_count := 1; v_child_loop_total := 0; <> LOOP -- Get everything from the current child minimum up to the multiples of the given interval v_move_sql := 'WITH move_data AS (DELETE FROM '||v_child_table|| ' WHERE '||v_control||' <= '||quote_literal(v_child_min + (p_batch_interval * v_inner_loop_count))||' RETURNING *) INSERT INTO '||p_parent_table||' SELECT * FROM move_data'; EXECUTE v_move_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total := v_total + v_rowcount; v_child_loop_total := v_child_loop_total + v_rowcount; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Moved '||v_child_loop_total||' rows to parent.'); END IF; EXIT inner_child_loop WHEN v_rowcount = 0; -- exit before loop incr if table is empty v_inner_loop_count := v_inner_loop_count + 1; v_batch_loop_count := v_batch_loop_count + 1; EXIT outer_child_loop WHEN v_batch_loop_count >= p_batch_count; -- Exit outer FOR loop if p_batch_count is reached END LOOP inner_child_loop; END LOOP outer_child_loop; IF v_batch_loop_count < p_batch_count THEN -- FOR loop never ran, so there's no child tables left. DELETE FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing config from pg_partman'); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; RAISE NOTICE 'Copied % row(s) to the parent. Removed % partitions.', v_total, v_undo_count; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Final stats'); PERFORM update_step(v_step_id, 'OK', 'Copied '||v_total||' row(s) to the parent. Removed '||v_undo_count||' partitions.'); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; PERFORM pg_advisory_unlock(hashtext('pg_partman undo_id_partition')); RETURN v_total; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN UNDO PARTITIONING: '||p_parent_table); v_step_id := add_step(v_job_id, 'Partition function maintenance for table '||p_parent_table||' failed'); ELSIF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'CRITICAL', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to undo time-based partitioning created by this extension */ CREATE OR REPLACE FUNCTION undo_partition_time(p_parent_table text, p_batch_count int DEFAULT 1, p_batch_interval interval DEFAULT NULL, p_keep_table boolean DEFAULT true) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_batch_loop_count int := 0; v_child_min timestamptz; v_child_loop_total bigint := 0; v_child_table text; v_control text; v_inner_loop_count int; v_job_id bigint; v_jobmon_schema text; v_move_sql text; v_old_search_path text; v_part_interval interval; v_row record; v_rowcount bigint; v_step_id bigint; v_tablename text; v_total bigint := 0; v_undo_count int := 0; BEGIN v_adv_lock := pg_try_advisory_lock(hashtext('pg_partman undo_time_partition')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'undo_time_partition already running.'; RETURN 0; END IF; SELECT part_interval::interval , control INTO v_part_interval , v_control FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic'); IF v_part_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN UNDO PARTITIONING: '||p_parent_table); v_step_id := add_step(v_job_id, 'Undoing partitioning for table '||p_parent_table); END IF; IF p_batch_interval IS NULL THEN p_batch_interval := v_part_interval; END IF; -- Stops new time partitons 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; -- Stop data going into child tables and stop new id partitions from being made. v_tablename := substring(p_parent_table from position('.' in p_parent_table)+1); EXECUTE 'DROP TRIGGER IF EXISTS '||v_tablename||'_part_trig ON '||p_parent_table; EXECUTE 'DROP FUNCTION IF EXISTS '||p_parent_table||'_part_trig_func()'; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Stopped partition creation process. Removed trigger & trigger function'); END IF; <> WHILE v_batch_loop_count < p_batch_count LOOP SELECT inhrelid::regclass INTO v_child_table FROM pg_catalog.pg_inherits WHERE inhparent::regclass = p_parent_table::regclass ORDER BY inhrelid ASC; EXIT WHEN v_child_table IS NULL; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing child partition: '||v_child_table); END IF; EXECUTE 'SELECT min('||v_control||') FROM '||v_child_table INTO v_child_min; IF v_child_min IS NULL THEN -- No rows left in this child table. Remove from partition set. EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; IF p_keep_table = false THEN EXECUTE 'DROP TABLE '||v_child_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table DROPPED. Moved '||v_child_loop_total||' rows to parent'); END IF; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table UNINHERITED, not DROPPED. Moved '||v_child_loop_total||' rows to parent'); END IF; END IF; v_undo_count := v_undo_count + 1; CONTINUE outer_child_loop; END IF; v_inner_loop_count := 1; v_child_loop_total := 0; <> LOOP -- Get everything from the current child minimum up to the multiples of the given interval v_move_sql := 'WITH move_data AS (DELETE FROM '||v_child_table|| ' WHERE '||v_control||' <= '||quote_literal(v_child_min + (p_batch_interval * v_inner_loop_count))||' RETURNING *) INSERT INTO '||p_parent_table||' SELECT * FROM move_data'; EXECUTE v_move_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total := v_total + v_rowcount; v_child_loop_total := v_child_loop_total + v_rowcount; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Moved '||v_child_loop_total||' rows to parent.'); END IF; EXIT inner_child_loop WHEN v_rowcount = 0; -- exit before loop incr if table is empty v_inner_loop_count := v_inner_loop_count + 1; v_batch_loop_count := v_batch_loop_count + 1; EXIT outer_child_loop WHEN v_batch_loop_count >= p_batch_count; -- Exit outer FOR loop if p_batch_count is reached END LOOP inner_child_loop; END LOOP outer_child_loop; IF v_batch_loop_count < p_batch_count THEN -- FOR loop never ran, so there's no child tables left. DELETE FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing config from pg_partman'); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; RAISE NOTICE 'Copied % row(s) to the parent. Removed % partitions.', v_total, v_undo_count; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Final stats'); PERFORM update_step(v_step_id, 'OK', 'Copied '||v_total||' row(s) to the parent. Removed '||v_undo_count||' partitions.'); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; PERFORM pg_advisory_unlock(hashtext('pg_partman undo_time_partition')); RETURN v_total; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN UNDO PARTITIONING: '||p_parent_table); v_step_id := add_step(v_job_id, 'Partition function maintenance for table '||p_parent_table||' failed'); ELSIF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'CRITICAL', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; pg_partman-2.2.2/updates/pg_partman--1.1.0--1.2.0.sql000066400000000000000000001404551262146621700214240ustar00rootroot00000000000000-- 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. DROP FUNCTION @extschema@.drop_time_partition(text, boolean, boolean); DROP FUNCTION @extschema@.drop_id_partition(text, boolean, boolean); /* * Function to drop child tables from a time-based partition set. Options to drop indexes or actually drop the table from the database. */ CREATE FUNCTION drop_partition_time(p_parent_table text, p_retention interval DEFAULT NULL, p_keep_table boolean DEFAULT NULL, p_keep_index boolean DEFAULT NULL) RETURNS int LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_child_table text; v_datetime_string text; v_drop_count int := 0; v_index record; v_job_id bigint; v_jobmon_schema text; v_old_search_path text; v_part_interval interval; v_partition_timestamp timestamp; v_quarter text; v_retention interval; v_retention_keep_index boolean; v_retention_keep_table boolean; v_step_id bigint; v_year text; BEGIN v_adv_lock := pg_try_advisory_lock(hashtext('pg_partman drop_partition_time')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'drop_partition_time already running.'; RETURN 0; END IF; SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; -- Allow override of configuration options IF p_retention IS NULL THEN SELECT part_interval::interval , retention::interval , retention_keep_table , retention_keep_index , datetime_string INTO v_part_interval , v_retention , v_retention_keep_table , v_retention_keep_index , v_datetime_string FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic') AND retention IS NOT NULL; IF v_part_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table with a retention period not found: %', p_parent_table; END IF; ELSE SELECT part_interval::interval , retention_keep_table , retention_keep_index , datetime_string INTO v_part_interval , v_retention_keep_table , v_retention_keep_index , v_datetime_string FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic'); v_retention := p_retention; IF v_part_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; END IF; IF p_keep_table IS NOT NULL THEN v_retention_keep_table = p_keep_table; END IF; IF p_keep_index IS NOT NULL THEN v_retention_keep_index = p_keep_index; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN DROP TIME PARTITION: '|| p_parent_table); END IF; -- Loop through child tables of the given parent FOR v_child_table IN SELECT n.nspname||'.'||c.relname FROM pg_inherits i join pg_class c ON i.inhrelid = c.oid join pg_namespace n ON c.relnamespace = n.oid WHERE i.inhparent::regclass = p_parent_table::regclass ORDER BY i.inhrelid ASC LOOP -- pull out datetime portion of last partition's tablename to make the next one IF v_part_interval != '3 months' THEN v_partition_timestamp := to_timestamp(substring(v_child_table from char_length(p_parent_table||'_p')+1), v_datetime_string); ELSE -- to_timestamp doesn't recognize 'Q' date string formater. Handle it v_year := split_part(substring(v_child_table from char_length(p_parent_table||'_p')+1), 'q', 1); v_quarter := split_part(substring(v_child_table from char_length(p_parent_table||'_p')+1), 'q', 2); CASE WHEN v_quarter = '1' THEN v_partition_timestamp := to_timestamp(v_year || '-01-01', 'YYYY-MM-DD'); WHEN v_quarter = '2' THEN v_partition_timestamp := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); WHEN v_quarter = '3' THEN v_partition_timestamp := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); WHEN v_quarter = '4' THEN v_partition_timestamp := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); END CASE; END IF; -- Add one interval since partition names contain the start of the constraint period IF v_retention < (CURRENT_TIMESTAMP - (v_partition_timestamp + v_part_interval)) THEN IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Uninherit table '||v_child_table||' from '||p_parent_table); END IF; EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; IF v_retention_keep_table = false THEN IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Drop table '||v_child_table); END IF; EXECUTE 'DROP TABLE '||v_child_table; 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 FOR v_index IN SELECT i.indexrelid::regclass AS name , c.conname FROM pg_catalog.pg_index i LEFT JOIN pg_catalog.pg_constraint c ON i.indexrelid = c.conindid WHERE i.indrelid = v_child_table::regclass LOOP IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Drop index '||v_index.name||' from '||v_child_table); END IF; IF v_index.conname IS NOT NULL THEN EXECUTE 'ALTER TABLE '||v_child_table||' DROP CONSTRAINT '||v_index.conname; ELSE EXECUTE 'DROP INDEX '||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; v_drop_count := v_drop_count + 1; END IF; -- End retention check IF END LOOP; -- End child table loop IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Finished partition drop maintenance'); PERFORM update_step(v_step_id, 'OK', v_drop_count||' partitions dropped.'); PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; PERFORM pg_advisory_unlock(hashtext('pg_partman drop_partition_time')); RETURN v_drop_count; EXCEPTION WHEN QUERY_CANCELED THEN PERFORM pg_advisory_unlock(hashtext('pg_partman drop_partition_time')); RAISE EXCEPTION '%', SQLERRM; WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN DROP TIME PARTITION'); v_step_id := add_step(v_job_id, 'EXCEPTION before job logging started'); END IF; IF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'CRITICAL', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; PERFORM pg_advisory_unlock(hashtext('pg_partman drop_partition_time')); RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to drop child tables from a time-based partition set. Options to drop indexes or actually drop the table from the database. */ CREATE FUNCTION drop_partition_id(p_parent_table text, p_retention bigint DEFAULT NULL, p_keep_table boolean DEFAULT NULL, p_keep_index boolean DEFAULT NULL) RETURNS int LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_child_table text; v_control text; v_drop_count int := 0; v_index record; v_job_id bigint; v_jobmon_schema text; v_max bigint; v_old_search_path text; v_part_interval bigint; v_partition_id bigint; v_retention bigint; v_retention_keep_index boolean; v_retention_keep_table boolean; v_step_id bigint; BEGIN v_adv_lock := pg_try_advisory_lock(hashtext('pg_partman drop_partition_id')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'drop_partition_id already running.'; RETURN 0; END IF; SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; -- Allow override of configuration options IF p_retention IS NULL THEN SELECT part_interval::bigint , control , retention::bigint , retention_keep_table , retention_keep_index INTO v_part_interval , v_control , v_retention , v_retention_keep_table , v_retention_keep_index FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic') AND retention IS NOT NULL; IF v_part_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table with a retention period not found: %', p_parent_table; END IF; ELSE SELECT part_interval::bigint , control , retention_keep_table , retention_keep_index INTO v_part_interval , v_control , v_retention_keep_table , v_retention_keep_index FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic'); v_retention := p_retention; IF v_part_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; END IF; IF p_keep_table IS NOT NULL THEN v_retention_keep_table = p_keep_table; END IF; IF p_keep_index IS NOT NULL THEN v_retention_keep_index = p_keep_index; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN DROP ID PARTITION: '|| p_parent_table); END IF; EXECUTE 'SELECT max('||v_control||') FROM '||p_parent_table INTO v_max; -- Loop through child tables of the given parent FOR v_child_table IN SELECT n.nspname||'.'||c.relname FROM pg_inherits i join pg_class c ON i.inhrelid = c.oid join pg_namespace n ON c.relnamespace = n.oid WHERE i.inhparent::regclass = p_parent_table::regclass ORDER BY i.inhrelid ASC LOOP v_partition_id := substring(v_child_table from char_length(p_parent_table||'_p')+1)::bigint; -- Add one interval since partition names contain the start of the constraint period IF v_retention <= (v_max - (v_partition_id + v_part_interval)) THEN IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Uninherit table '||v_child_table||' from '||p_parent_table); END IF; EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; IF v_retention_keep_table = false THEN IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Drop table '||v_child_table); END IF; EXECUTE 'DROP TABLE '||v_child_table; 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 FOR v_index IN SELECT i.indexrelid::regclass AS name , c.conname FROM pg_catalog.pg_index i LEFT JOIN pg_catalog.pg_constraint c ON i.indexrelid = c.conindid WHERE i.indrelid = v_child_table::regclass LOOP IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Drop index '||v_index.name||' from '||v_child_table); END IF; IF v_index.conname IS NOT NULL THEN EXECUTE 'ALTER TABLE '||v_child_table||' DROP CONSTRAINT '||v_index.conname; ELSE EXECUTE 'DROP INDEX '||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; v_drop_count := v_drop_count + 1; END IF; -- End retention check IF END LOOP; -- End child table loop IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Finished partition drop maintenance'); PERFORM update_step(v_step_id, 'OK', v_drop_count||' partitions dropped.'); PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; PERFORM pg_advisory_unlock(hashtext('pg_partman drop_partition_id')); RETURN v_drop_count; EXCEPTION WHEN QUERY_CANCELED THEN PERFORM pg_advisory_unlock(hashtext('pg_partman drop_partition_id')); RAISE EXCEPTION '%', SQLERRM; WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN DROP ID PARTITION'); v_step_id := add_step(v_job_id, 'EXCEPTION before job logging started'); END IF; IF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'CRITICAL', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; PERFORM pg_advisory_unlock(hashtext('pg_partman drop_partition_id')); RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to re-apply ownership & privileges on all child tables in a partition set using parent table as reference */ CREATE OR REPLACE FUNCTION reapply_privileges(p_parent_table text) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_all text[] := ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER']; v_child_owner text; v_child_table text; v_child_grant record; v_count int; v_grant text; v_grantees text[]; v_job_id bigint; v_jobmon_schema text; v_match boolean; v_old_search_path text; v_parent_owner text; v_owner_sql text; v_revoke text[]; v_parent_grant record; v_sql text; v_step_id bigint; BEGIN SELECT count(*) INTO v_count FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_count = 0 THEN RAISE EXCEPTION 'Given table is not managed by this extention: %', p_parent_table; END IF; SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN RE-APPLYING PRIVILEGES TO ALL CHILD TABLES OF: '||p_parent_table); v_step_id := add_step(v_job_id, 'Setting new child table privileges'); END IF; SELECT tableowner INTO v_parent_owner FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; FOR v_child_table IN SELECT n.nspname||'.'||c.relname FROM pg_inherits i join pg_class c ON i.inhrelid = c.oid join pg_namespace n ON c.relnamespace = n.oid WHERE i.inhparent::regclass = p_parent_table::regclass ORDER BY i.inhrelid ASC LOOP IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'PENDING', 'Currently on child partition in ascending order: '||v_child_table); END IF; v_grantees := NULL; FOR v_parent_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = p_parent_table GROUP BY grantee LOOP -- Compare parent & child grants. Don't re-apply if it already exists v_match := false; FOR v_child_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = v_child_table GROUP BY grantee LOOP IF v_parent_grant.types = v_child_grant.types AND v_parent_grant.grantee = v_child_grant.grantee THEN v_match := true; END IF; END LOOP; IF v_match = false THEN EXECUTE 'GRANT '||array_to_string(v_parent_grant.types, ',')||' ON '||v_child_table||' TO '||v_parent_grant.grantee; SELECT array_agg(r) INTO v_revoke FROM (SELECT unnest(v_all) AS r EXCEPT SELECT unnest(v_parent_grant.types)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE '||array_to_string(v_revoke, ',')||' ON '||v_child_table||' FROM '||v_parent_grant.grantee||' CASCADE'; END IF; END IF; v_grantees := array_append(v_grantees, v_parent_grant.grantee::text); END LOOP; -- Revoke all privileges from roles that have none on the parent IF v_grantees IS NOT NULL THEN SELECT array_agg(r) INTO v_revoke FROM ( SELECT DISTINCT grantee::text AS r FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = v_child_table EXCEPT SELECT unnest(v_grantees)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE ALL ON '||v_child_table||' FROM '||array_to_string(v_revoke, ','); END IF; END IF; SELECT tableowner INTO v_child_owner FROM pg_tables WHERE schemaname ||'.'|| tablename = v_child_table; IF v_parent_owner <> v_child_owner THEN EXECUTE 'ALTER TABLE '||v_child_table||' OWNER TO '||v_parent_owner; END IF; END LOOP; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); PERFORM close_job(v_job_id); END IF; IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); v_step_id := add_step(v_job_id, 'Partition maintenance for table '||p_parent_table||' failed'); ELSIF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'BAD', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to undo partitioning. * Will actually work on any parent/child table set, not just ones created by pg_partman. */ CREATE OR REPLACE FUNCTION undo_partition(p_parent_table text, p_batch_count int DEFAULT 1, p_keep_table boolean DEFAULT true) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_batch_loop_count bigint := 0; v_child_count bigint; v_child_table text; v_copy_sql text; v_job_id bigint; v_jobmon_schema text; v_old_search_path text; v_part_interval interval; v_rowcount bigint; v_step_id bigint; v_tablename text; v_total bigint := 0; v_undo_count int := 0; BEGIN v_adv_lock := pg_try_advisory_lock(hashtext('pg_partman undo_partition')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'undo_partition already running.'; RETURN 0; END IF; SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN UNDO PARTITIONING: '||p_parent_table); v_step_id := add_step(v_job_id, 'Undoing partitioning for table '||p_parent_table); END IF; -- Stops new time partitons 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; -- Stop data going into child tables and stop new id partitions from being made. v_tablename := substring(p_parent_table from position('.' in p_parent_table)+1); EXECUTE 'DROP TRIGGER IF EXISTS '||v_tablename||'_part_trig ON '||p_parent_table; EXECUTE 'DROP FUNCTION IF EXISTS '||p_parent_table||'_part_trig_func()'; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Stopped partition creation process. Removed trigger & trigger function'); END IF; WHILE v_batch_loop_count < p_batch_count LOOP SELECT n.nspname||'.'||c.relname INTO v_child_table FROM pg_inherits i JOIN pg_class c ON i.inhrelid = c.oid JOIN pg_namespace n ON c.relnamespace = n.oid WHERE i.inhparent::regclass = p_parent_table::regclass ORDER BY i.inhrelid ASC; EXIT WHEN v_child_table IS NULL; EXECUTE 'SELECT count(*) FROM '||v_child_table INTO v_child_count; IF v_child_count = 0 THEN -- No rows left in this child table. Remove from partition set. EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; IF p_keep_table = false THEN EXECUTE 'DROP TABLE '||v_child_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table DROPPED. Moved '||coalesce(v_rowcount, 0)||' rows to parent'); END IF; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table UNINHERITED, not DROPPED. Copied '||coalesce(v_rowcount, 0)||' rows to parent'); END IF; END IF; v_undo_count := v_undo_count + 1; CONTINUE; END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing child partition: '||v_child_table); END IF; v_copy_sql := 'INSERT INTO '||p_parent_table||' SELECT * FROM '||v_child_table; EXECUTE v_copy_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total := v_total + v_rowcount; EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; IF p_keep_table = false THEN EXECUTE 'DROP TABLE '||v_child_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table DROPPED. Moved '||v_rowcount||' rows to parent'); END IF; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table UNINHERITED, not DROPPED. Copied '||v_rowcount||' rows to parent'); END IF; END IF; v_batch_loop_count := v_batch_loop_count + 1; v_undo_count := v_undo_count + 1; END LOOP; IF v_undo_count = 0 THEN -- FOR loop never ran, so there's no child tables left. DELETE FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing config from pg_partman (if it existed)'); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; RAISE NOTICE 'Copied % row(s) from % child table(s) to the parent: %', v_total, v_undo_count, p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Final stats'); PERFORM update_step(v_step_id, 'OK', 'Copied '||v_total||' row(s) from '||v_undo_count||' child table(s) to the parent'); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; PERFORM pg_advisory_unlock(hashtext('pg_partman undo_partition')); RETURN v_total; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN UNDO PARTITIONING: '||p_parent_table); v_step_id := add_step(v_job_id, 'Partition function maintenance for table '||p_parent_table||' failed'); ELSIF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'CRITICAL', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to undo id-based partitioning created by this extension */ CREATE OR REPLACE FUNCTION undo_partition_id(p_parent_table text, p_batch_count int DEFAULT 1, p_batch_interval bigint DEFAULT NULL, p_keep_table boolean DEFAULT true) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_batch_loop_count int := 0; v_child_loop_total bigint := 0; v_child_min bigint; v_child_table text; v_control text; v_inner_loop_count int; v_job_id bigint; v_jobmon_schema text; v_move_sql text; v_old_search_path text; v_part_interval bigint; v_row record; v_rowcount bigint; v_step_id bigint; v_tablename text; v_total bigint := 0; v_undo_count int := 0; BEGIN v_adv_lock := pg_try_advisory_lock(hashtext('pg_partman undo_id_partition')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'undo_id_partition already running.'; RETURN 0; END IF; SELECT part_interval::bigint , control INTO v_part_interval , v_control FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic'); IF v_part_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN UNDO PARTITIONING: '||p_parent_table); v_step_id := add_step(v_job_id, 'Undoing partitioning for table '||p_parent_table); END IF; IF p_batch_interval IS NULL THEN p_batch_interval := v_part_interval; END IF; -- Stops new time partitons 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; -- Stop data going into child tables and stop new id partitions from being made. v_tablename := substring(p_parent_table from position('.' in p_parent_table)+1); EXECUTE 'DROP TRIGGER IF EXISTS '||v_tablename||'_part_trig ON '||p_parent_table; EXECUTE 'DROP FUNCTION IF EXISTS '||p_parent_table||'_part_trig_func()'; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Stopped partition creation process. Removed trigger & trigger function'); END IF; <> WHILE v_batch_loop_count < p_batch_count LOOP SELECT n.nspname||'.'||c.relname INTO v_child_table FROM pg_inherits i JOIN pg_class c ON i.inhrelid = c.oid JOIN pg_namespace n ON c.relnamespace = n.oid WHERE i.inhparent::regclass = p_parent_table::regclass ORDER BY i.inhrelid ASC; EXIT WHEN v_child_table IS NULL; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing child partition: '||v_child_table); END IF; EXECUTE 'SELECT min('||v_control||') FROM '||v_child_table INTO v_child_min; IF v_child_min IS NULL THEN -- No rows left in this child table. Remove from partition set. EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; IF p_keep_table = false THEN EXECUTE 'DROP TABLE '||v_child_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table DROPPED. Moved '||v_child_loop_total||' rows to parent'); END IF; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table UNINHERITED, not DROPPED. Moved '||v_child_loop_total||' rows to parent'); END IF; END IF; v_undo_count := v_undo_count + 1; CONTINUE outer_child_loop; END IF; v_inner_loop_count := 1; v_child_loop_total := 0; <> LOOP -- Get everything from the current child minimum up to the multiples of the given interval v_move_sql := 'WITH move_data AS (DELETE FROM '||v_child_table|| ' WHERE '||v_control||' <= '||quote_literal(v_child_min + (p_batch_interval * v_inner_loop_count))||' RETURNING *) INSERT INTO '||p_parent_table||' SELECT * FROM move_data'; EXECUTE v_move_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total := v_total + v_rowcount; v_child_loop_total := v_child_loop_total + v_rowcount; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Moved '||v_child_loop_total||' rows to parent.'); END IF; EXIT inner_child_loop WHEN v_rowcount = 0; -- exit before loop incr if table is empty v_inner_loop_count := v_inner_loop_count + 1; v_batch_loop_count := v_batch_loop_count + 1; EXIT outer_child_loop WHEN v_batch_loop_count >= p_batch_count; -- Exit outer FOR loop if p_batch_count is reached END LOOP inner_child_loop; END LOOP outer_child_loop; IF v_batch_loop_count < p_batch_count THEN -- FOR loop never ran, so there's no child tables left. DELETE FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing config from pg_partman'); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; RAISE NOTICE 'Copied % row(s) to the parent. Removed % partitions.', v_total, v_undo_count; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Final stats'); PERFORM update_step(v_step_id, 'OK', 'Copied '||v_total||' row(s) to the parent. Removed '||v_undo_count||' partitions.'); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; PERFORM pg_advisory_unlock(hashtext('pg_partman undo_id_partition')); RETURN v_total; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN UNDO PARTITIONING: '||p_parent_table); v_step_id := add_step(v_job_id, 'Partition function maintenance for table '||p_parent_table||' failed'); ELSIF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'CRITICAL', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to undo time-based partitioning created by this extension */ CREATE OR REPLACE FUNCTION undo_partition_time(p_parent_table text, p_batch_count int DEFAULT 1, p_batch_interval interval DEFAULT NULL, p_keep_table boolean DEFAULT true) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_batch_loop_count int := 0; v_child_min timestamptz; v_child_loop_total bigint := 0; v_child_table text; v_control text; v_inner_loop_count int; v_job_id bigint; v_jobmon_schema text; v_move_sql text; v_old_search_path text; v_part_interval interval; v_row record; v_rowcount bigint; v_step_id bigint; v_tablename text; v_total bigint := 0; v_undo_count int := 0; BEGIN v_adv_lock := pg_try_advisory_lock(hashtext('pg_partman undo_time_partition')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'undo_time_partition already running.'; RETURN 0; END IF; SELECT part_interval::interval , control INTO v_part_interval , v_control FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic'); IF v_part_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN UNDO PARTITIONING: '||p_parent_table); v_step_id := add_step(v_job_id, 'Undoing partitioning for table '||p_parent_table); END IF; IF p_batch_interval IS NULL THEN p_batch_interval := v_part_interval; END IF; -- Stops new time partitons 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; -- Stop data going into child tables and stop new id partitions from being made. v_tablename := substring(p_parent_table from position('.' in p_parent_table)+1); EXECUTE 'DROP TRIGGER IF EXISTS '||v_tablename||'_part_trig ON '||p_parent_table; EXECUTE 'DROP FUNCTION IF EXISTS '||p_parent_table||'_part_trig_func()'; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Stopped partition creation process. Removed trigger & trigger function'); END IF; <> WHILE v_batch_loop_count < p_batch_count LOOP SELECT n.nspname||'.'||c.relname INTO v_child_table FROM pg_inherits i JOIN pg_class c ON i.inhrelid = c.oid JOIN pg_namespace n ON c.relnamespace = n.oid WHERE i.inhparent::regclass = p_parent_table::regclass ORDER BY i.inhrelid ASC; EXIT WHEN v_child_table IS NULL; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing child partition: '||v_child_table); END IF; EXECUTE 'SELECT min('||v_control||') FROM '||v_child_table INTO v_child_min; IF v_child_min IS NULL THEN -- No rows left in this child table. Remove from partition set. EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; IF p_keep_table = false THEN EXECUTE 'DROP TABLE '||v_child_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table DROPPED. Moved '||v_child_loop_total||' rows to parent'); END IF; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table UNINHERITED, not DROPPED. Moved '||v_child_loop_total||' rows to parent'); END IF; END IF; v_undo_count := v_undo_count + 1; CONTINUE outer_child_loop; END IF; v_inner_loop_count := 1; v_child_loop_total := 0; <> LOOP -- Get everything from the current child minimum up to the multiples of the given interval v_move_sql := 'WITH move_data AS (DELETE FROM '||v_child_table|| ' WHERE '||v_control||' <= '||quote_literal(v_child_min + (p_batch_interval * v_inner_loop_count))||' RETURNING *) INSERT INTO '||p_parent_table||' SELECT * FROM move_data'; EXECUTE v_move_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total := v_total + v_rowcount; v_child_loop_total := v_child_loop_total + v_rowcount; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Moved '||v_child_loop_total||' rows to parent.'); END IF; EXIT inner_child_loop WHEN v_rowcount = 0; -- exit before loop incr if table is empty v_inner_loop_count := v_inner_loop_count + 1; v_batch_loop_count := v_batch_loop_count + 1; EXIT outer_child_loop WHEN v_batch_loop_count >= p_batch_count; -- Exit outer FOR loop if p_batch_count is reached END LOOP inner_child_loop; END LOOP outer_child_loop; IF v_batch_loop_count < p_batch_count THEN -- FOR loop never ran, so there's no child tables left. DELETE FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing config from pg_partman'); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; RAISE NOTICE 'Copied % row(s) to the parent. Removed % partitions.', v_total, v_undo_count; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Final stats'); PERFORM update_step(v_step_id, 'OK', 'Copied '||v_total||' row(s) to the parent. Removed '||v_undo_count||' partitions.'); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; PERFORM pg_advisory_unlock(hashtext('pg_partman undo_time_partition')); RETURN v_total; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN UNDO PARTITIONING: '||p_parent_table); v_step_id := add_step(v_job_id, 'Partition function maintenance for table '||p_parent_table||' failed'); ELSIF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'CRITICAL', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to manage pre-creation of the next partitions in a time-based partition set. * Also manages dropping old partitions if the retention option is set. */ CREATE OR REPLACE FUNCTION run_maintenance() RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_create_count int := 0; v_current_partition_timestamp timestamp; v_datetime_string text; v_drop_count int := 0; v_job_id bigint; v_jobmon_schema text; v_last_partition_timestamp timestamp; v_old_search_path text; v_premade_count real; v_quarter text; v_step_id bigint; v_row record; v_year text; BEGIN v_adv_lock := pg_try_advisory_lock(hashtext('pg_partman run_maintenance')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'Partman maintenance already running.'; RETURN; END IF; SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN RUN MAINTENANCE'); v_step_id := add_step(v_job_id, 'Running maintenance loop'); END IF; FOR v_row IN SELECT parent_table , type , part_interval::interval , control , premake , datetime_string , last_partition , undo_in_progress FROM @extschema@.part_config WHERE type = 'time-static' OR type = 'time-dynamic' LOOP CONTINUE WHEN v_row.undo_in_progress; CASE WHEN v_row.part_interval = '15 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0); WHEN v_row.part_interval = '30 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0); WHEN v_row.part_interval = '1 hour' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP); WHEN v_row.part_interval = '1 day' THEN v_current_partition_timestamp := date_trunc('day', CURRENT_TIMESTAMP); WHEN v_row.part_interval = '1 week' THEN v_current_partition_timestamp := date_trunc('week', CURRENT_TIMESTAMP); WHEN v_row.part_interval = '1 month' THEN v_current_partition_timestamp := date_trunc('month', CURRENT_TIMESTAMP); WHEN v_row.part_interval = '3 months' THEN v_current_partition_timestamp := date_trunc('quarter', CURRENT_TIMESTAMP); WHEN v_row.part_interval = '1 year' THEN v_current_partition_timestamp := date_trunc('year', CURRENT_TIMESTAMP); END CASE; IF v_row.part_interval != '3 months' THEN v_last_partition_timestamp := to_timestamp(substring(v_row.last_partition from char_length(v_row.parent_table||'_p')+1), v_row.datetime_string); ELSE -- to_timestamp doesn't recognize 'Q' date string formater. Handle it v_year := split_part(substring(v_row.last_partition from char_length(v_row.parent_table||'_p')+1), 'q', 1); v_quarter := split_part(substring(v_row.last_partition from char_length(v_row.parent_table||'_p')+1), 'q', 2); CASE WHEN v_quarter = '1' THEN v_last_partition_timestamp := to_timestamp(v_year || '-01-01', 'YYYY-MM-DD'); WHEN v_quarter = '2' THEN v_last_partition_timestamp := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); WHEN v_quarter = '3' THEN v_last_partition_timestamp := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); WHEN v_quarter = '4' THEN v_last_partition_timestamp := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); END CASE; END IF; -- Check and see how many premade partitions there are. If it's less than premake in config table, make another v_premade_count = EXTRACT('epoch' FROM age(v_last_partition_timestamp, v_current_partition_timestamp)) / EXTRACT('epoch' FROM v_row.part_interval::interval); -- 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 EXECUTE 'SELECT @extschema@.create_next_time_partition('||quote_literal(v_row.parent_table)||')'; v_create_count := v_create_count + 1; IF v_row.type = 'time-static' THEN EXECUTE 'SELECT @extschema@.create_time_function('||quote_literal(v_row.parent_table)||')'; END IF; v_last_partition_timestamp := v_last_partition_timestamp + v_row.part_interval; v_premade_count = EXTRACT('epoch' FROM age(v_last_partition_timestamp, v_current_partition_timestamp)) / EXTRACT('epoch' FROM v_row.part_interval::interval); END LOOP; END LOOP; -- end of creation loop -- Manage dropping old partitions if retention option is set FOR v_row IN SELECT parent_table FROM @extschema@.part_config WHERE retention IS NOT NULL AND undo_in_progress = false AND (type = 'time-static' OR type = 'time-dynamic') LOOP v_drop_count := v_drop_count + @extschema@.drop_partition_time(v_row.parent_table); END LOOP; FOR v_row IN SELECT parent_table FROM @extschema@.part_config WHERE retention IS NOT NULL AND undo_in_progress = false AND (type = 'id-static' OR type = 'id-dynamic') LOOP v_drop_count := v_drop_count + @extschema@.drop_partition_id(v_row.parent_table); END LOOP; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Partition maintenance finished. '||v_create_count||' partitons made. '||v_drop_count||' partitions dropped.'); PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; PERFORM pg_advisory_unlock(hashtext('pg_partman run_maintenance')); EXCEPTION WHEN QUERY_CANCELED THEN PERFORM pg_advisory_unlock(hashtext('pg_partman run_maintenance')); RAISE EXCEPTION '%', SQLERRM; WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN RUN MAINTENANCE'); v_step_id := add_step(v_job_id, 'EXCEPTION before job logging started'); END IF; IF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'CRITICAL', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; PERFORM pg_advisory_unlock(hashtext('pg_partman run_maintenance')); RAISE EXCEPTION '%', SQLERRM; END $$; pg_partman-2.2.2/updates/pg_partman--1.2.0--1.3.0.sql000066400000000000000000001046341262146621700214250ustar00rootroot00000000000000-- 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. ALTER TABLE @extschema@.part_config ADD retention_schema text; CREATE TEMP TABLE pg_partman_preserve_privs_temp (statement text); INSERT INTO pg_partman_preserve_privs_temp SELECT 'GRANT EXECUTE ON FUNCTION @extschema@.drop_partition_id(text, bigint, boolean, boolean, text) TO '||array_to_string(array_agg(grantee::text), ',')||';' FROM information_schema.routine_privileges WHERE routine_schema = '@extschema@' AND routine_name = 'drop_partition_id'; INSERT INTO pg_partman_preserve_privs_temp SELECT 'GRANT EXECUTE ON FUNCTION @extschema@.drop_partition_time(text, interval, boolean, boolean, text) TO '||array_to_string(array_agg(grantee::text), ',')||';' FROM information_schema.routine_privileges WHERE routine_schema = '@extschema@' AND routine_name = 'drop_partition_time'; CREATE FUNCTION replay_preserved_privs() RETURNS void LANGUAGE plpgsql AS $$ DECLARE v_row record; BEGIN FOR v_row IN SELECT statement FROM pg_partman_preserve_privs_temp LOOP IF v_row.statement IS NOT NULL THEN EXECUTE v_row.statement; END IF; END LOOP; END $$; DROP FUNCTION drop_partition_id(text, bigint, boolean, boolean); DROP FUNCTION drop_partition_time(text, interval, boolean, boolean); /* * Function to list all child partitions in a set. * Will list all child tables in any inheritance set, * not just those managed by pg_partman. */ CREATE FUNCTION show_partitions (p_parent_table text) RETURNS SETOF text LANGUAGE plpgsql STABLE SECURITY DEFINER AS $$ BEGIN RETURN QUERY EXECUTE ' SELECT n.nspname::text ||''.''|| 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 = '||quote_literal(p_parent_table)||'::regclass ORDER BY c.relname'; END $$; /* * Function to create id partitions */ CREATE OR REPLACE FUNCTION create_id_partition (p_parent_table text, p_control text, p_interval bigint, p_partition_ids bigint[]) RETURNS text LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_all text[] := ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER']; v_grantees text[]; v_job_id bigint; v_jobmon_schema text; v_old_search_path text; v_parent_grant record; v_parent_owner text; v_partition_name text; v_revoke text[]; v_step_id bigint; v_tablename text; v_id bigint; BEGIN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; SELECT tableowner INTO v_parent_owner FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; FOREACH v_id IN ARRAY p_partition_ids LOOP v_partition_name := p_parent_table||'_p'||v_id; SELECT schemaname ||'.'|| tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_tablename IS NOT NULL THEN CONTINUE; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating new partition '||v_partition_name||' with interval from '||v_id||' to '||(v_id + p_interval)-1); END IF; IF position('.' in p_parent_table) > 0 THEN v_tablename := substring(v_partition_name from position('.' in v_partition_name)+1); END IF; EXECUTE 'CREATE TABLE '||v_partition_name||' (LIKE '||p_parent_table||' INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING STORAGE INCLUDING COMMENTS)'; EXECUTE 'ALTER TABLE '||v_partition_name||' ADD CONSTRAINT '||v_tablename||'_partition_check CHECK ('||p_control||'>='||quote_literal(v_id)||' AND '||p_control||'<'||quote_literal(v_id + p_interval)||')'; EXECUTE 'ALTER TABLE '||v_partition_name||' INHERIT '||p_parent_table; FOR v_parent_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = p_parent_table GROUP BY grantee LOOP EXECUTE 'GRANT '||array_to_string(v_parent_grant.types, ',')||' ON '||v_partition_name||' TO '||v_parent_grant.grantee; SELECT array_agg(r) INTO v_revoke FROM (SELECT unnest(v_all) AS r EXCEPT SELECT unnest(v_parent_grant.types)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE '||array_to_string(v_revoke, ',')||' ON '||v_partition_name||' FROM '||v_parent_grant.grantee||' CASCADE'; END IF; v_grantees := array_append(v_grantees, v_parent_grant.grantee::text); END LOOP; -- Revoke all privileges from roles that have none on the parent IF v_grantees IS NOT NULL THEN SELECT array_agg(r) INTO v_revoke FROM ( SELECT DISTINCT grantee::text AS r FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = v_partition_name EXCEPT SELECT unnest(v_grantees)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE ALL ON '||v_partition_name||' FROM '||array_to_string(v_revoke, ','); END IF; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' OWNER TO '||v_parent_owner; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); PERFORM close_job(v_job_id); END IF; END LOOP; IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_partition_name; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); v_step_id := add_step(v_job_id, 'Partition maintenance for table '||p_parent_table||' failed'); ELSIF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'BAD', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to create a child table in a time-based partition set */ CREATE OR REPLACE FUNCTION create_time_partition (p_parent_table text, p_control text, p_interval interval, p_datetime_string text, p_partition_times timestamp[]) RETURNS text LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_all text[] := ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER']; v_grantees text[]; v_job_id bigint; v_jobmon_schema text; v_old_search_path text; v_parent_grant record; v_parent_owner text; v_partition_name text; v_partition_timestamp_end timestamp; v_partition_timestamp_start timestamp; v_quarter text; v_revoke text[]; v_step_id bigint; v_tablename text; v_trunc_value text; v_time timestamp; v_year text; BEGIN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; SELECT tableowner INTO v_parent_owner FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; FOREACH v_time IN ARRAY p_partition_times LOOP v_partition_name := p_parent_table || '_p'; IF p_interval = '1 year' OR p_interval = '1 month' OR p_interval = '1 day' OR p_interval = '1 hour' OR p_interval = '30 mins' OR p_interval = '15 mins' THEN v_partition_name := v_partition_name || to_char(v_time, 'YYYY'); v_trunc_value := 'year'; IF p_interval = '1 month' OR p_interval = '1 day' OR p_interval = '1 hour' OR p_interval = '30 mins' OR p_interval = '15 mins' THEN v_partition_name := v_partition_name || '_' || to_char(v_time, 'MM'); v_trunc_value := 'month'; IF p_interval = '1 day' OR p_interval = '1 hour' OR p_interval = '30 mins' OR p_interval = '15 mins' THEN v_partition_name := v_partition_name || '_' || to_char(v_time, 'DD'); v_trunc_value := 'day'; IF p_interval = '1 hour' OR p_interval = '30 mins' OR p_interval = '15 mins' THEN v_partition_name := v_partition_name || '_' || to_char(v_time, 'HH24'); IF p_interval <> '30 mins' AND p_interval <> '15 mins' THEN v_partition_name := v_partition_name || '00'; v_trunc_value := 'hour'; ELSIF p_interval = '15 mins' THEN IF date_part('minute', v_time) < 15 THEN v_partition_name := v_partition_name || '00'; ELSIF date_part('minute', v_time) >= 15 AND date_part('minute', v_time) < 30 THEN v_partition_name := v_partition_name || '15'; ELSIF date_part('minute', v_time) >= 30 AND date_part('minute', v_time) < 45 THEN v_partition_name := v_partition_name || '30'; ELSE v_partition_name := v_partition_name || '45'; END IF; v_trunc_value := 'minute'; ELSIF p_interval = '30 mins' THEN IF date_part('minute', v_time) < 30 THEN v_partition_name := v_partition_name || '00'; ELSE v_partition_name := v_partition_name || '30'; END IF; v_trunc_value := 'minute'; END IF; END IF; -- end hour IF END IF; -- end day IF END IF; -- end month IF ELSIF p_interval = '1 week' THEN v_partition_name := v_partition_name || to_char(v_time, 'IYYY') || 'w' || to_char(v_time, 'IW'); v_trunc_value := 'week'; END IF; -- end year/week IF -- pull out datetime portion of last partition's tablename if it matched one of the above partitioning intervals IF v_trunc_value IS NOT NULL THEN v_partition_timestamp_start := date_trunc(v_trunc_value, to_timestamp(substring(v_partition_name from char_length(p_parent_table||'_p')+1), p_datetime_string)); v_partition_timestamp_end := date_trunc(v_trunc_value, to_timestamp(substring(v_partition_name from char_length(p_parent_table||'_p')+1), p_datetime_string) + p_interval); END IF; -- "Q" is ignored in to_timestamp, so handle special case IF p_interval = '3 months' THEN v_year := to_char(v_time, 'YYYY'); v_quarter := to_char(v_time, 'Q'); v_partition_name := v_partition_name || v_year || 'q' || v_quarter; v_trunc_value := 'quarter'; CASE WHEN v_quarter = '1' THEN v_partition_timestamp_start := date_trunc(v_trunc_value, to_timestamp(v_year || '-01-01', 'YYYY-MM-DD')); WHEN v_quarter = '2' THEN v_partition_timestamp_start := date_trunc(v_trunc_value, to_timestamp(v_year || '-04-01', 'YYYY-MM-DD')); WHEN v_quarter = '3' THEN v_partition_timestamp_start := date_trunc(v_trunc_value, to_timestamp(v_year || '-07-01', 'YYYY-MM-DD')); WHEN v_quarter = '4' THEN v_partition_timestamp_start := date_trunc(v_trunc_value, to_timestamp(v_year || '-10-01', 'YYYY-MM-DD')); END CASE; v_partition_timestamp_end := date_trunc(v_trunc_value, (v_partition_timestamp_start + p_interval)); END IF; SELECT schemaname ||'.'|| tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_tablename IS NOT NULL THEN CONTINUE; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating new partition '||v_partition_name||' with interval from '||v_partition_timestamp_start||' to '||(v_partition_timestamp_end-'1sec'::interval)); END IF; IF position('.' in p_parent_table) > 0 THEN v_tablename := substring(v_partition_name from position('.' in v_partition_name)+1); END IF; EXECUTE 'CREATE TABLE '||v_partition_name||' (LIKE '||p_parent_table||' INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING STORAGE INCLUDING COMMENTS)'; EXECUTE 'ALTER TABLE '||v_partition_name||' ADD CONSTRAINT '||v_tablename||'_partition_check CHECK ('||p_control||'>='||quote_literal(v_partition_timestamp_start)||' AND '||p_control||'<'||quote_literal(v_partition_timestamp_end)||')'; EXECUTE 'ALTER TABLE '||v_partition_name||' INHERIT '||p_parent_table; FOR v_parent_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = p_parent_table GROUP BY grantee LOOP EXECUTE 'GRANT '||array_to_string(v_parent_grant.types, ',')||' ON '||v_partition_name||' TO '||v_parent_grant.grantee; SELECT array_agg(r) INTO v_revoke FROM (SELECT unnest(v_all) AS r EXCEPT SELECT unnest(v_parent_grant.types)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE '||array_to_string(v_revoke, ',')||' ON '||v_partition_name||' FROM '||v_parent_grant.grantee||' CASCADE'; END IF; v_grantees := array_append(v_grantees, v_parent_grant.grantee::text); END LOOP; -- Revoke all privileges from roles that have none on the parent IF v_grantees IS NOT NULL THEN SELECT array_agg(r) INTO v_revoke FROM ( SELECT DISTINCT grantee::text AS r FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = v_partition_name EXCEPT SELECT unnest(v_grantees)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE ALL ON '||v_partition_name||' FROM '||array_to_string(v_revoke, ','); END IF; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' OWNER TO '||v_parent_owner; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); PERFORM close_job(v_job_id); END IF; END LOOP; IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_partition_name; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); v_step_id := add_step(v_job_id, 'Partition maintenance for table '||p_parent_table||' failed'); ELSIF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'CRITICAL', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to drop child tables from a time-based partition set. * Options to move table to different schema, drop only indexes or actually drop the table from the database. */ CREATE FUNCTION 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 SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_child_table text; v_control text; v_drop_count int := 0; v_index record; v_job_id bigint; v_jobmon_schema text; v_max bigint; v_old_search_path text; v_part_interval bigint; v_partition_id bigint; v_retention bigint; v_retention_keep_index boolean; v_retention_keep_table boolean; v_retention_schema text; v_step_id bigint; BEGIN v_adv_lock := pg_try_advisory_lock(hashtext('pg_partman drop_partition_id')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'drop_partition_id already running.'; RETURN 0; END IF; SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; -- Allow override of configuration options IF p_retention IS NULL THEN SELECT part_interval::bigint , control , retention::bigint , retention_keep_table , retention_keep_index , retention_schema INTO v_part_interval , v_control , v_retention , v_retention_keep_table , v_retention_keep_index , v_retention_schema FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic') AND retention IS NOT NULL; IF v_part_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table with a retention period not found: %', p_parent_table; END IF; ELSE SELECT part_interval::bigint , control , retention_keep_table , retention_keep_index , retention_schema INTO v_part_interval , v_control , v_retention_keep_table , v_retention_keep_index , v_retention_schema FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic'); v_retention := p_retention; IF v_part_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; END IF; IF p_keep_table IS NOT NULL THEN v_retention_keep_table = p_keep_table; END IF; IF p_keep_index IS NOT NULL THEN v_retention_keep_index = p_keep_index; END IF; IF p_retention_schema IS NOT NULL THEN v_retention_schema = p_retention_schema; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN DROP ID PARTITION: '|| p_parent_table); END IF; EXECUTE 'SELECT max('||v_control||') FROM '||p_parent_table INTO v_max; -- Loop through child tables of the given parent FOR v_child_table IN SELECT n.nspname||'.'||c.relname FROM pg_inherits i join pg_class c ON i.inhrelid = c.oid join pg_namespace n ON c.relnamespace = n.oid WHERE i.inhparent::regclass = p_parent_table::regclass ORDER BY i.inhrelid ASC LOOP v_partition_id := substring(v_child_table from char_length(p_parent_table||'_p')+1)::bigint; -- Add one interval since partition names contain the start of the constraint period IF v_retention <= (v_max - (v_partition_id + v_part_interval)) THEN IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Uninherit table '||v_child_table||' from '||p_parent_table); END IF; EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; 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, 'Drop table '||v_child_table); END IF; EXECUTE 'DROP TABLE '||v_child_table; 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 FOR v_index IN SELECT i.indexrelid::regclass AS name , c.conname FROM pg_catalog.pg_index i LEFT JOIN pg_catalog.pg_constraint c ON i.indexrelid = c.conindid WHERE i.indrelid = v_child_table::regclass LOOP IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Drop index '||v_index.name||' from '||v_child_table); END IF; IF v_index.conname IS NOT NULL THEN EXECUTE 'ALTER TABLE '||v_child_table||' DROP CONSTRAINT '||v_index.conname; ELSE EXECUTE 'DROP INDEX '||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; ELSE -- Move to new schema IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Moving table '||v_child_table||' to schema '||v_retention_schema); END IF; EXECUTE 'ALTER TABLE '||v_child_table||' SET SCHEMA '||v_retention_schema; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; -- End retention schema if v_drop_count := v_drop_count + 1; END IF; -- End retention check IF END LOOP; -- End child table loop IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Finished partition drop maintenance'); PERFORM update_step(v_step_id, 'OK', v_drop_count||' partitions dropped.'); PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; PERFORM pg_advisory_unlock(hashtext('pg_partman drop_partition_id')); RETURN v_drop_count; EXCEPTION WHEN QUERY_CANCELED THEN PERFORM pg_advisory_unlock(hashtext('pg_partman drop_partition_id')); RAISE EXCEPTION '%', SQLERRM; WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN DROP ID PARTITION'); v_step_id := add_step(v_job_id, 'EXCEPTION before job logging started'); END IF; IF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'CRITICAL', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; PERFORM pg_advisory_unlock(hashtext('pg_partman drop_partition_id')); RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to drop child tables from a time-based partition set. * Options to move table to different schema, drop only indexes or actually drop the table from the database. */ CREATE FUNCTION 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) RETURNS int LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_child_table text; v_datetime_string text; v_drop_count int := 0; v_index record; v_job_id bigint; v_jobmon_schema text; v_old_search_path text; v_part_interval interval; v_partition_timestamp timestamp; v_quarter text; v_retention interval; v_retention_keep_index boolean; v_retention_keep_table boolean; v_retention_schema text; v_step_id bigint; v_year text; BEGIN v_adv_lock := pg_try_advisory_lock(hashtext('pg_partman drop_partition_time')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'drop_partition_time already running.'; RETURN 0; END IF; SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; -- Allow override of configuration options IF p_retention IS NULL THEN SELECT part_interval::interval , retention::interval , retention_keep_table , retention_keep_index , datetime_string , retention_schema INTO v_part_interval , v_retention , v_retention_keep_table , v_retention_keep_index , v_datetime_string , v_retention_schema FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic') AND retention IS NOT NULL; IF v_part_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table with a retention period not found: %', p_parent_table; END IF; ELSE SELECT part_interval::interval , retention_keep_table , retention_keep_index , datetime_string , retention_schema INTO v_part_interval , v_retention_keep_table , v_retention_keep_index , v_datetime_string , v_retention_schema FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic'); v_retention := p_retention; IF v_part_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; END IF; IF p_keep_table IS NOT NULL THEN v_retention_keep_table = p_keep_table; END IF; IF p_keep_index IS NOT NULL THEN v_retention_keep_index = p_keep_index; END IF; IF p_retention_schema IS NOT NULL THEN v_retention_schema = p_retention_schema; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN DROP TIME PARTITION: '|| p_parent_table); END IF; -- Loop through child tables of the given parent FOR v_child_table IN SELECT n.nspname||'.'||c.relname FROM pg_inherits i join pg_class c ON i.inhrelid = c.oid join pg_namespace n ON c.relnamespace = n.oid WHERE i.inhparent::regclass = p_parent_table::regclass ORDER BY i.inhrelid ASC LOOP -- pull out datetime portion of last partition's tablename to make the next one IF v_part_interval != '3 months' THEN v_partition_timestamp := to_timestamp(substring(v_child_table from char_length(p_parent_table||'_p')+1), v_datetime_string); ELSE -- to_timestamp doesn't recognize 'Q' date string formater. Handle it v_year := split_part(substring(v_child_table from char_length(p_parent_table||'_p')+1), 'q', 1); v_quarter := split_part(substring(v_child_table from char_length(p_parent_table||'_p')+1), 'q', 2); CASE WHEN v_quarter = '1' THEN v_partition_timestamp := to_timestamp(v_year || '-01-01', 'YYYY-MM-DD'); WHEN v_quarter = '2' THEN v_partition_timestamp := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); WHEN v_quarter = '3' THEN v_partition_timestamp := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); WHEN v_quarter = '4' THEN v_partition_timestamp := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); END CASE; END IF; -- Add one interval since partition names contain the start of the constraint period IF v_retention < (CURRENT_TIMESTAMP - (v_partition_timestamp + v_part_interval)) THEN IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Uninherit table '||v_child_table||' from '||p_parent_table); END IF; EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; 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, 'Drop table '||v_child_table); END IF; EXECUTE 'DROP TABLE '||v_child_table; 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 FOR v_index IN SELECT i.indexrelid::regclass AS name , c.conname FROM pg_catalog.pg_index i LEFT JOIN pg_catalog.pg_constraint c ON i.indexrelid = c.conindid WHERE i.indrelid = v_child_table::regclass LOOP IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Drop index '||v_index.name||' from '||v_child_table); END IF; IF v_index.conname IS NOT NULL THEN EXECUTE 'ALTER TABLE '||v_child_table||' DROP CONSTRAINT '||v_index.conname; ELSE EXECUTE 'DROP INDEX '||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; ELSE -- Move to new schema IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Moving table '||v_child_table||' to schema '||v_retention_schema); END IF; EXECUTE 'ALTER TABLE '||v_child_table||' SET SCHEMA '||v_retention_schema; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; -- End retention schema if v_drop_count := v_drop_count + 1; END IF; -- End retention check IF END LOOP; -- End child table loop IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Finished partition drop maintenance'); PERFORM update_step(v_step_id, 'OK', v_drop_count||' partitions dropped.'); PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; PERFORM pg_advisory_unlock(hashtext('pg_partman drop_partition_time')); RETURN v_drop_count; EXCEPTION WHEN QUERY_CANCELED THEN PERFORM pg_advisory_unlock(hashtext('pg_partman drop_partition_time')); RAISE EXCEPTION '%', SQLERRM; WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN DROP TIME PARTITION'); v_step_id := add_step(v_job_id, 'EXCEPTION before job logging started'); END IF; IF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'CRITICAL', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; PERFORM pg_advisory_unlock(hashtext('pg_partman drop_partition_time')); RAISE EXCEPTION '%', SQLERRM; END $$; -- Restore original privileges to objects that were dropped SELECT @extschema@.replay_preserved_privs(); DROP FUNCTION @extschema@.replay_preserved_privs(); DROP TABLE pg_partman_preserve_privs_temp; pg_partman-2.2.2/updates/pg_partman--1.3.0--1.4.0.sql000066400000000000000000002563671262146621700214420ustar00rootroot00000000000000-- 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. /* * Truncate the name of the given object if it is greater than the postgres default max (63 characters). * Also appends given suffix and schema if given and truncates the name so that the entire suffix will fit. * Returns original name with schema given if it doesn't require truncation */ CREATE FUNCTION check_name_length (p_object_name text, p_object_schema text DEFAULT NULL, p_suffix text DEFAULT NULL, p_table_partition boolean DEFAULT FALSE) RETURNS text LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_new_length int; v_new_name text; BEGIN IF p_table_partition IS TRUE AND (p_suffix IS NULL OR p_object_schema IS NULL) THEN RAISE EXCEPTION 'Table partition name requires a schema and 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 := p_object_schema ||'.'|| substring(p_object_name from 1 for v_new_length) || '_p' || p_suffix; ELSE v_new_name := p_object_schema ||'.'||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 := COALESCE(p_object_schema ||'.', '') || substring(p_object_name from 1 for v_new_length) || COALESCE(p_suffix, ''); ELSE v_new_name := COALESCE(p_object_schema ||'.', '') || p_object_name||COALESCE(p_suffix, ''); END IF; END IF; RETURN v_new_name; END $$; CREATE TYPE check_unique_table AS (column_value text, count bigint); /* * Function to check uniqueness of a column in a partiton set. * First draft that runs within database in a single transaction. * Working on version that will dump data out to perform a quicker check with less impact on DB. */ CREATE FUNCTION check_unique_column(p_parent_table text, p_column text) RETURNS SETOF check_unique_table LANGUAGE plpgsql AS $$ DECLARE v_row record; v_sql text; v_trouble @extschema@.check_unique_table%rowtype; BEGIN v_sql := 'SELECT '||p_column||'::text AS column_value, count('||p_column||') AS count FROM '||p_parent_table||' GROUP BY '||p_column||' HAVING (count('||p_column||') > 1) ORDER BY '||p_column; RAISE NOTICE 'v_sql: %', v_sql; FOR v_row IN EXECUTE v_sql LOOP v_trouble.column_value := v_row.column_value; v_trouble.count := v_row.count; RETURN NEXT v_trouble; END LOOP; END $$; /* * Function to create id partitions */ CREATE OR REPLACE FUNCTION create_id_partition (p_parent_table text, p_control text, p_interval bigint, p_partition_ids bigint[]) RETURNS text LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_all text[] := ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER']; v_grantees text[]; v_job_id bigint; v_jobmon_schema text; v_old_search_path text; v_parent_grant record; v_parent_owner text; v_parent_schema text; v_parent_tablename text; v_partition_name text; v_revoke text[]; v_step_id bigint; v_tablename text; v_id bigint; BEGIN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; SELECT tableowner, schemaname, tablename INTO v_parent_owner, v_parent_schema, v_parent_tablename FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; FOREACH v_id IN ARRAY p_partition_ids LOOP v_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_id::text, TRUE); -- If child table already exists, skip creation SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_tablename IS NOT NULL THEN CONTINUE; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating new partition '||v_partition_name||' with interval from '||v_id||' to '||(v_id + p_interval)-1); END IF; EXECUTE 'CREATE TABLE '||v_partition_name||' (LIKE '||p_parent_table||' INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING STORAGE INCLUDING COMMENTS)'; SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; EXECUTE 'ALTER TABLE '||v_partition_name||' ADD CONSTRAINT '||v_tablename||'_partition_check CHECK ('||p_control||'>='||quote_literal(v_id)||' AND '||p_control||'<'||quote_literal(v_id + p_interval)||')'; EXECUTE 'ALTER TABLE '||v_partition_name||' INHERIT '||p_parent_table; FOR v_parent_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = p_parent_table GROUP BY grantee LOOP EXECUTE 'GRANT '||array_to_string(v_parent_grant.types, ',')||' ON '||v_partition_name||' TO '||v_parent_grant.grantee; SELECT array_agg(r) INTO v_revoke FROM (SELECT unnest(v_all) AS r EXCEPT SELECT unnest(v_parent_grant.types)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE '||array_to_string(v_revoke, ',')||' ON '||v_partition_name||' FROM '||v_parent_grant.grantee||' CASCADE'; END IF; v_grantees := array_append(v_grantees, v_parent_grant.grantee::text); END LOOP; -- Revoke all privileges from roles that have none on the parent IF v_grantees IS NOT NULL THEN SELECT array_agg(r) INTO v_revoke FROM ( SELECT DISTINCT grantee::text AS r FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = v_partition_name EXCEPT SELECT unnest(v_grantees)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE ALL ON '||v_partition_name||' FROM '||array_to_string(v_revoke, ','); END IF; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' OWNER TO '||v_parent_owner; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); PERFORM close_job(v_job_id); END IF; END LOOP; IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_partition_name; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); v_step_id := add_step(v_job_id, 'Partition maintenance for table '||p_parent_table||' failed'); ELSIF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'BAD', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to create a child table in a time-based partition set */ CREATE OR REPLACE FUNCTION create_time_partition (p_parent_table text, p_control text, p_interval interval, p_datetime_string text, p_partition_times timestamp[]) RETURNS text LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_all text[] := ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER']; v_grantees text[]; v_job_id bigint; v_jobmon_schema text; v_old_search_path text; v_parent_grant record; v_parent_owner text; v_parent_schema text; v_parent_tablename text; v_partition_name text; v_partition_suffix text; v_partition_timestamp_end timestamp; v_partition_timestamp_start timestamp; v_quarter text; v_revoke text[]; v_step_id bigint; v_tablename text; v_trunc_value text; v_time timestamp; v_year text; BEGIN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; SELECT tableowner, schemaname, tablename INTO v_parent_owner, v_parent_schema, v_parent_tablename FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; FOREACH v_time IN ARRAY p_partition_times LOOP IF p_interval <= '1 year' AND p_interval <> '3 months' AND p_interval <> '1 week' THEN v_partition_suffix := to_char(v_time, 'YYYY'); v_trunc_value := 'year'; IF p_interval <= '1 month' AND p_interval <> '1 week' THEN v_partition_suffix := v_partition_suffix ||'_'|| to_char(v_time, 'MM'); v_trunc_value := 'month'; IF p_interval <= '1 day' THEN v_partition_suffix := v_partition_suffix ||'_'|| to_char(v_time, 'DD'); v_trunc_value := 'day'; IF p_interval <= '1 hour' THEN v_partition_suffix := v_partition_suffix || '_' || to_char(v_time, 'HH24'); IF p_interval <> '30 mins' AND p_interval <> '15 mins' THEN v_partition_suffix := v_partition_suffix || '00'; v_trunc_value := 'hour'; ELSIF p_interval = '15 mins' THEN IF date_part('minute', v_time) < 15 THEN v_partition_suffix := v_partition_suffix || '00'; ELSIF date_part('minute', v_time) >= 15 AND date_part('minute', v_time) < 30 THEN v_partition_suffix := v_partition_suffix || '15'; ELSIF date_part('minute', v_time) >= 30 AND date_part('minute', v_time) < 45 THEN v_partition_suffix := v_partition_suffix || '30'; ELSE v_partition_suffix := v_partition_suffix || '45'; END IF; v_trunc_value := 'minute'; ELSIF p_interval = '30 mins' THEN IF date_part('minute', v_time) < 30 THEN v_partition_suffix := v_partition_suffix || '00'; ELSE v_partition_suffix := v_partition_suffix || '30'; END IF; v_trunc_value := 'minute'; END IF; END IF; -- end hour IF END IF; -- end day IF END IF; -- end month IF ELSIF p_interval = '1 week' THEN v_partition_suffix := to_char(v_time, 'IYYY') || 'w' || to_char(v_time, 'IW'); v_trunc_value := 'week'; END IF; -- end year/week IF -- "Q" is ignored in to_timestamp, so handle special case IF p_interval = '3 months' THEN v_year := to_char(v_time, 'YYYY'); v_quarter := to_char(v_time, 'Q'); v_partition_suffix := v_year || 'q' || v_quarter; v_trunc_value := 'quarter'; CASE WHEN v_quarter = '1' THEN v_partition_timestamp_start := date_trunc(v_trunc_value, to_timestamp(v_year || '-01-01', 'YYYY-MM-DD')); WHEN v_quarter = '2' THEN v_partition_timestamp_start := date_trunc(v_trunc_value, to_timestamp(v_year || '-04-01', 'YYYY-MM-DD')); WHEN v_quarter = '3' THEN v_partition_timestamp_start := date_trunc(v_trunc_value, to_timestamp(v_year || '-07-01', 'YYYY-MM-DD')); WHEN v_quarter = '4' THEN v_partition_timestamp_start := date_trunc(v_trunc_value, to_timestamp(v_year || '-10-01', 'YYYY-MM-DD')); END CASE; v_partition_timestamp_end := date_trunc(v_trunc_value, (v_partition_timestamp_start + p_interval)); END IF; v_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_partition_suffix, TRUE); -- pull out datetime portion of last partition's tablename if it matched anything except quarterly IF p_interval <> '3 months' AND v_trunc_value IS NOT NULL THEN v_partition_timestamp_start := date_trunc(v_trunc_value, to_timestamp(v_partition_suffix, p_datetime_string)); v_partition_timestamp_end := date_trunc(v_trunc_value, to_timestamp(v_partition_suffix, p_datetime_string) + p_interval); END IF; SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_tablename IS NOT NULL THEN CONTINUE; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating new partition '||v_partition_name||' with interval from '||v_partition_timestamp_start||' to '||(v_partition_timestamp_end-'1sec'::interval)); END IF; EXECUTE 'CREATE TABLE '||v_partition_name||' (LIKE '||p_parent_table||' INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING STORAGE INCLUDING COMMENTS)'; SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; EXECUTE 'ALTER TABLE '||v_partition_name||' ADD CONSTRAINT '||v_tablename||'_partition_check CHECK ('||p_control||'>='||quote_literal(v_partition_timestamp_start)||' AND '||p_control||'<'||quote_literal(v_partition_timestamp_end)||')'; EXECUTE 'ALTER TABLE '||v_partition_name||' INHERIT '||p_parent_table; FOR v_parent_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = p_parent_table GROUP BY grantee LOOP EXECUTE 'GRANT '||array_to_string(v_parent_grant.types, ',')||' ON '||v_partition_name||' TO '||v_parent_grant.grantee; SELECT array_agg(r) INTO v_revoke FROM (SELECT unnest(v_all) AS r EXCEPT SELECT unnest(v_parent_grant.types)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE '||array_to_string(v_revoke, ',')||' ON '||v_partition_name||' FROM '||v_parent_grant.grantee||' CASCADE'; END IF; v_grantees := array_append(v_grantees, v_parent_grant.grantee::text); END LOOP; -- Revoke all privileges from roles that have none on the parent IF v_grantees IS NOT NULL THEN SELECT array_agg(r) INTO v_revoke FROM ( SELECT DISTINCT grantee::text AS r FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = v_partition_name EXCEPT SELECT unnest(v_grantees)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE ALL ON '||v_partition_name||' FROM '||array_to_string(v_revoke, ','); END IF; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' OWNER TO '||v_parent_owner; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); PERFORM close_job(v_job_id); END IF; END LOOP; IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_partition_name; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); v_step_id := add_step(v_job_id, 'Partition maintenance for table '||p_parent_table||' failed'); ELSIF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'CRITICAL', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Create the trigger function for the parent table of an id-based partition set */ CREATE OR REPLACE FUNCTION create_id_function(p_parent_table text, p_current_id bigint) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_control text; v_count int; v_current_partition_name text; v_current_partition_id bigint; v_datetime_string text; v_final_partition_id bigint; v_function_name text; v_job_id bigint; v_jobmon_schema text; v_last_partition text; v_next_partition_id bigint; v_next_partition_name text; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_part_interval bigint; v_premake int; v_prev_partition_id bigint; v_prev_partition_name text; v_step_id bigint; v_trig_func text; v_type text; BEGIN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE FUNCTION: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating partition function for table '||p_parent_table); END IF; SELECT type , part_interval::bigint , control , premake , last_partition INTO v_type , v_part_interval , v_control , v_premake , v_last_partition FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; v_function_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, '_part_trig_func', FALSE); IF v_type = 'id-static' THEN v_current_partition_id := p_current_id - (p_current_id % v_part_interval); v_next_partition_id := v_current_partition_id + v_part_interval; v_current_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_current_partition_id::text, TRUE); v_trig_func := 'CREATE OR REPLACE FUNCTION '||v_function_name||'() RETURNS trigger LANGUAGE plpgsql AS $t$ DECLARE v_current_partition_id bigint; v_last_partition text := '||quote_literal(v_last_partition)||'; v_id_position int; v_next_partition_id bigint; v_next_partition_name text; BEGIN IF TG_OP = ''INSERT'' THEN IF NEW.'||v_control||' >= '||v_current_partition_id||' AND NEW.'||v_control||' < '||v_next_partition_id|| ' THEN INSERT INTO '||v_current_partition_name||' VALUES (NEW.*); '; FOR i IN 1..v_premake LOOP v_prev_partition_id := v_current_partition_id - (v_part_interval * i); v_next_partition_id := v_current_partition_id + (v_part_interval * i); v_final_partition_id := v_next_partition_id + v_part_interval; v_prev_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_prev_partition_id::text, TRUE); v_next_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_next_partition_id::text, TRUE); -- Check that child table exist before making a rule to insert to them. -- Handles edge case of changing premake immediately after running create_parent(). SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname ||'.'||tablename = v_prev_partition_name; IF v_count > 0 THEN -- Only handle previous partitions if they're starting above zero IF v_prev_partition_id >= 0 THEN v_trig_func := v_trig_func ||' ELSIF NEW.'||v_control||' >= '||v_prev_partition_id||' AND NEW.'||v_control||' < '||v_prev_partition_id + v_part_interval|| ' THEN INSERT INTO '||v_prev_partition_name||' VALUES (NEW.*); '; END IF; END IF; SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname ||'.'||tablename = v_next_partition_name; IF v_count > 0 THEN v_trig_func := v_trig_func ||' ELSIF NEW.'||v_control||' >= '||v_next_partition_id||' AND NEW.'||v_control||' < '||v_final_partition_id|| ' THEN INSERT INTO '||v_next_partition_name||' VALUES (NEW.*); '; END IF; END LOOP; v_trig_func := v_trig_func ||' ELSE RETURN NEW; END IF; v_current_partition_id := NEW.'||v_control||' - (NEW.'||v_control||' % '||v_part_interval||'); IF (NEW.'||v_control||' % '||v_part_interval||') > ('||v_part_interval||' / 2) THEN v_id_position := (length(v_last_partition) - position(''p_'' in reverse(v_last_partition))) + 2; v_next_partition_id := (substring(v_last_partition from v_id_position)::bigint) + '||v_part_interval||'; WHILE ((v_next_partition_id - v_current_partition_id) / '||v_part_interval||') <= '||v_premake||' LOOP v_next_partition_name := @extschema@.create_id_partition('||quote_literal(p_parent_table)||', '||quote_literal(v_control)||',' ||v_part_interval||', ARRAY[v_next_partition_id]); UPDATE @extschema@.part_config SET last_partition = v_next_partition_name WHERE parent_table = '||quote_literal(p_parent_table)||'; PERFORM @extschema@.create_id_function('||quote_literal(p_parent_table)||', NEW.'||v_control||'); v_next_partition_id := v_next_partition_id + '||v_part_interval||'; END LOOP; END IF; END IF; RETURN NULL; END $t$;'; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Added function for current id interval: '||v_current_partition_id||' to '||v_final_partition_id-1); END IF; ELSIF v_type = 'id-dynamic' THEN -- The return inside the partition creation check is there to keep really high ID values from creating new partitions. v_trig_func := 'CREATE OR REPLACE FUNCTION '||v_function_name||'() RETURNS trigger LANGUAGE plpgsql AS $t$ DECLARE v_count int; v_current_partition_id bigint; v_current_partition_name text; v_id_position int; v_last_partition text := '||quote_literal(v_last_partition)||'; v_last_partition_id bigint; v_next_partition_id bigint; v_next_partition_name text; BEGIN IF TG_OP = ''INSERT'' THEN v_current_partition_id := NEW.'||v_control||' - (NEW.'||v_control||' % '||v_part_interval||'); v_current_partition_name := @extschema@.check_name_length('''||v_parent_tablename||''', '''||v_parent_schema||''', v_current_partition_id::text, TRUE); IF (NEW.'||v_control||' % '||v_part_interval||') > ('||v_part_interval||' / 2) THEN v_id_position := (length(v_last_partition) - position(''p_'' in reverse(v_last_partition))) + 2; v_last_partition_id = substring(v_last_partition from v_id_position)::bigint; v_next_partition_id := v_last_partition_id + '||v_part_interval||'; IF NEW.'||v_control||' >= v_next_partition_id THEN RETURN NEW; END IF; WHILE ((v_next_partition_id - v_current_partition_id) / '||v_part_interval||') <= '||v_premake||' LOOP v_next_partition_name := @extschema@.create_id_partition('||quote_literal(p_parent_table)||', '||quote_literal(v_control)||',' ||quote_literal(v_part_interval)||', ARRAY[v_next_partition_id]); IF v_next_partition_name IS NOT NULL THEN UPDATE @extschema@.part_config SET last_partition = v_next_partition_name WHERE parent_table = '||quote_literal(p_parent_table)||'; PERFORM @extschema@.create_id_function('||quote_literal(p_parent_table)||', NEW.'||v_control||'); END IF; v_next_partition_id := v_next_partition_id + '||v_part_interval||'; END LOOP; END IF; SELECT count(*) INTO v_count FROM pg_tables WHERE schemaname ||''.''|| tablename = v_current_partition_name; IF v_count > 0 THEN EXECUTE ''INSERT INTO ''||v_current_partition_name||'' VALUES($1.*)'' USING NEW; ELSE RETURN NEW; END IF; END IF; RETURN NULL; END $t$;'; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Added function for dynamic id table: '||p_parent_table); END IF; ELSE RAISE EXCEPTION 'ERROR: Invalid id partitioning type given: %', v_type; END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN CREATE FUNCTION: '||p_parent_table); v_step_id := add_step(v_job_id, 'Partition function maintenance for table '||p_parent_table||' failed'); ELSIF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'CRITICAL', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Create the trigger function for the parent table of a time-based partition set */ CREATE OR REPLACE FUNCTION create_time_function(p_parent_table text) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_control text; v_count int; v_current_partition_name text; v_current_partition_timestamp timestamptz; v_datetime_string text; v_final_partition_timestamp timestamptz; v_function_name text; v_job_id bigint; v_jobmon_schema text; v_old_search_path text; v_new_length int; v_next_partition_name text; v_next_partition_timestamp timestamptz; v_parent_schema text; v_parent_tablename text; v_part_interval interval; v_premake int; v_prev_partition_name text; v_prev_partition_timestamp timestamptz; v_step_id bigint; v_trig_func text; v_type text; BEGIN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE FUNCTION: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating partition function for table '||p_parent_table); END IF; SELECT type , part_interval::interval , control , premake , datetime_string INTO v_type , v_part_interval , v_control , v_premake , v_datetime_string FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; v_function_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, '_part_trig_func', FALSE); IF v_type = 'time-static' THEN CASE WHEN v_part_interval = '15 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0); WHEN v_part_interval = '30 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0); WHEN v_part_interval = '1 hour' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP); WHEN v_part_interval = '1 day' THEN v_current_partition_timestamp := date_trunc('day', CURRENT_TIMESTAMP); WHEN v_part_interval = '1 week' THEN v_current_partition_timestamp := date_trunc('week', CURRENT_TIMESTAMP); WHEN v_part_interval = '1 month' THEN v_current_partition_timestamp := date_trunc('month', CURRENT_TIMESTAMP); WHEN v_part_interval = '3 months' THEN v_current_partition_timestamp := date_trunc('quarter', CURRENT_TIMESTAMP); WHEN v_part_interval = '1 year' THEN v_current_partition_timestamp := date_trunc('year', CURRENT_TIMESTAMP); END CASE; v_current_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, to_char(v_current_partition_timestamp, v_datetime_string), TRUE); v_next_partition_timestamp := v_current_partition_timestamp + v_part_interval::interval; v_trig_func := 'CREATE OR REPLACE FUNCTION '||v_function_name||'() RETURNS trigger LANGUAGE plpgsql AS $t$ BEGIN IF TG_OP = ''INSERT'' THEN IF NEW.'||v_control||' >= '||quote_literal(v_current_partition_timestamp)||' AND NEW.'||v_control||' < '||quote_literal(v_next_partition_timestamp)|| ' THEN INSERT INTO '||v_current_partition_name||' VALUES (NEW.*); '; FOR i IN 1..v_premake LOOP v_prev_partition_timestamp := v_current_partition_timestamp - (v_part_interval::interval * i); v_next_partition_timestamp := v_current_partition_timestamp + (v_part_interval::interval * i); v_final_partition_timestamp := v_next_partition_timestamp + (v_part_interval::interval); v_prev_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, to_char(v_prev_partition_timestamp, v_datetime_string), TRUE); v_next_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, to_char(v_next_partition_timestamp, v_datetime_string), TRUE); -- Check that child table exist before making a rule to insert to them. -- Handles edge case of changing premake immediately after running create_parent(). SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname ||'.'||tablename = v_prev_partition_name; IF v_count > 0 THEN v_trig_func := v_trig_func ||' ELSIF NEW.'||v_control||' >= '||quote_literal(v_prev_partition_timestamp)||' AND NEW.'||v_control||' < '|| quote_literal(v_prev_partition_timestamp + v_part_interval::interval)|| ' THEN INSERT INTO '||v_prev_partition_name||' VALUES (NEW.*);'; END IF; SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname ||'.'||tablename = v_next_partition_name; IF v_count > 0 THEN v_trig_func := v_trig_func ||' ELSIF NEW.'||v_control||' >= '||quote_literal(v_next_partition_timestamp)||' AND NEW.'||v_control||' < '|| quote_literal(v_final_partition_timestamp)|| ' THEN INSERT INTO '||v_next_partition_name||' VALUES (NEW.*);'; END IF; END LOOP; v_trig_func := v_trig_func ||' ELSE RETURN NEW; END IF; END IF; RETURN NULL; END $t$;'; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Added function for current time interval: '|| v_current_partition_timestamp||' to '||(v_final_partition_timestamp-'1sec'::interval)); END IF; ELSIF v_type = 'time-dynamic' THEN v_trig_func := 'CREATE OR REPLACE FUNCTION '||v_function_name||'() RETURNS trigger LANGUAGE plpgsql AS $t$ DECLARE v_count int; v_partition_name text; v_partition_timestamp timestamptz; BEGIN IF TG_OP = ''INSERT'' THEN '; CASE WHEN v_part_interval = '15 mins' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''hour'', NEW.'||v_control||') + ''15min''::interval * floor(date_part(''minute'', NEW.'||v_control||') / 15.0);'; WHEN v_part_interval = '30 mins' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''hour'', NEW.'||v_control||') + ''30min''::interval * floor(date_part(''minute'', NEW.'||v_control||') / 30.0);'; WHEN v_part_interval = '1 hour' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''hour'', NEW.'||v_control||');'; WHEN v_part_interval = '1 day' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''day'', NEW.'||v_control||');'; WHEN v_part_interval = '1 week' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''week'', NEW.'||v_control||');'; WHEN v_part_interval = '1 month' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''month'', NEW.'||v_control||');'; WHEN v_part_interval = '3 months' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''quarter'', NEW.'||v_control||');'; WHEN v_part_interval = '1 year' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''year'', NEW.'||v_control||');'; END CASE; v_trig_func := v_trig_func||' v_partition_name := @extschema@.check_name_length('''||v_parent_tablename||''', '''||v_parent_schema||''', to_char(v_partition_timestamp, '||quote_literal(v_datetime_string)||'), TRUE); SELECT count(*) INTO v_count FROM pg_tables WHERE schemaname ||''.''|| tablename = v_partition_name; IF v_count > 0 THEN EXECUTE ''INSERT INTO ''||v_partition_name||'' VALUES($1.*)'' USING NEW; ELSE RETURN NEW; END IF; END IF; RETURN NULL; END $t$;'; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Added function for dynamic time table: '||p_parent_table); END IF; ELSE RAISE EXCEPTION 'ERROR: Invalid time partitioning type given: %', v_type; END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN CREATE FUNCTION: '||p_parent_table); v_step_id := add_step(v_job_id, 'Partition function maintenance for table '||p_parent_table||' failed'); ELSIF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'CRITICAL', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; CREATE OR REPLACE FUNCTION create_trigger(p_parent_table text) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_function_name text; v_new_length int; v_parent_schema text; v_parent_tablename text; v_trig_name text; v_trig_sql text; BEGIN SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; v_trig_name := @extschema@.check_name_length(p_object_name := v_parent_tablename, p_suffix := '_part_trig'); -- Ensure function name matches the naming pattern v_function_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, '_part_trig_func', FALSE); v_trig_sql := 'CREATE TRIGGER '||v_trig_name||' BEFORE INSERT ON '||p_parent_table|| ' FOR EACH ROW EXECUTE PROCEDURE '||v_function_name||'()'; EXECUTE v_trig_sql; END $$; /* * Function to manage pre-creation of the next partitions in a time-based partition set. * Also manages dropping old partitions if the retention option is set. */ CREATE OR REPLACE FUNCTION run_maintenance() RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_create_count int := 0; v_current_partition_timestamp timestamp; v_datetime_string text; v_drop_count int := 0; v_job_id bigint; v_jobmon_schema text; v_last_partition_timestamp timestamp; v_old_search_path text; v_premade_count int; v_quarter text; v_step_id bigint; v_row record; v_time_position int; v_year text; BEGIN v_adv_lock := pg_try_advisory_lock(hashtext('pg_partman run_maintenance')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'Partman maintenance already running.'; RETURN; END IF; SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN RUN MAINTENANCE'); v_step_id := add_step(v_job_id, 'Running maintenance loop'); END IF; FOR v_row IN SELECT parent_table , type , part_interval::interval , control , premake , datetime_string , last_partition , undo_in_progress FROM @extschema@.part_config WHERE type = 'time-static' OR type = 'time-dynamic' LOOP CONTINUE WHEN v_row.undo_in_progress; CASE WHEN v_row.part_interval = '15 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0); WHEN v_row.part_interval = '30 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0); WHEN v_row.part_interval = '1 hour' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP); WHEN v_row.part_interval = '1 day' THEN v_current_partition_timestamp := date_trunc('day', CURRENT_TIMESTAMP); WHEN v_row.part_interval = '1 week' THEN v_current_partition_timestamp := date_trunc('week', CURRENT_TIMESTAMP); WHEN v_row.part_interval = '1 month' THEN v_current_partition_timestamp := date_trunc('month', CURRENT_TIMESTAMP); WHEN v_row.part_interval = '3 months' THEN v_current_partition_timestamp := date_trunc('quarter', CURRENT_TIMESTAMP); WHEN v_row.part_interval = '1 year' THEN v_current_partition_timestamp := date_trunc('year', CURRENT_TIMESTAMP); END CASE; -- Get position of last occurance of '_p' in child partition name v_time_position := (length(v_row.last_partition) - position('p_' in reverse(v_row.last_partition))) + 2; IF v_row.part_interval != '3 months' THEN v_last_partition_timestamp := to_timestamp(substring(v_row.last_partition from v_time_position), v_row.datetime_string); ELSE -- to_timestamp doesn't recognize 'Q' date string formater. Handle it v_year := split_part(substring(v_row.last_partition from v_time_position), 'q', 1); v_quarter := split_part(substring(v_row.last_partition from v_time_position), 'q', 2); CASE WHEN v_quarter = '1' THEN v_last_partition_timestamp := to_timestamp(v_year || '-01-01', 'YYYY-MM-DD'); WHEN v_quarter = '2' THEN v_last_partition_timestamp := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); WHEN v_quarter = '3' THEN v_last_partition_timestamp := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); WHEN v_quarter = '4' THEN v_last_partition_timestamp := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); END CASE; END IF; -- Check and see how many premade partitions there are. v_premade_count = round(EXTRACT('epoch' FROM age(v_last_partition_timestamp, v_current_partition_timestamp)) / EXTRACT('epoch' FROM v_row.part_interval::interval)); -- 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 EXECUTE 'SELECT @extschema@.create_next_time_partition('||quote_literal(v_row.parent_table)||')'; v_create_count := v_create_count + 1; IF v_row.type = 'time-static' THEN EXECUTE 'SELECT @extschema@.create_time_function('||quote_literal(v_row.parent_table)||')'; END IF; v_last_partition_timestamp := v_last_partition_timestamp + v_row.part_interval; v_premade_count = round(EXTRACT('epoch' FROM age(v_last_partition_timestamp, v_current_partition_timestamp)) / EXTRACT('epoch' FROM v_row.part_interval::interval)); END LOOP; END LOOP; -- end of creation loop -- Manage dropping old partitions if retention option is set FOR v_row IN SELECT parent_table FROM @extschema@.part_config WHERE retention IS NOT NULL AND undo_in_progress = false AND (type = 'time-static' OR type = 'time-dynamic') LOOP v_drop_count := v_drop_count + @extschema@.drop_partition_time(v_row.parent_table); END LOOP; FOR v_row IN SELECT parent_table FROM @extschema@.part_config WHERE retention IS NOT NULL AND undo_in_progress = false AND (type = 'id-static' OR type = 'id-dynamic') LOOP v_drop_count := v_drop_count + @extschema@.drop_partition_id(v_row.parent_table); END LOOP; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Partition maintenance finished. '||v_create_count||' partitons made. '||v_drop_count||' partitions dropped.'); PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; PERFORM pg_advisory_unlock(hashtext('pg_partman run_maintenance')); EXCEPTION WHEN QUERY_CANCELED THEN PERFORM pg_advisory_unlock(hashtext('pg_partman run_maintenance')); RAISE EXCEPTION '%', SQLERRM; WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN RUN MAINTENANCE'); v_step_id := add_step(v_job_id, 'EXCEPTION before job logging started'); END IF; IF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'CRITICAL', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; PERFORM pg_advisory_unlock(hashtext('pg_partman run_maintenance')); RAISE EXCEPTION '%', SQLERRM; END $$; /* * Create the next partition in sequence for a time-based partition set */ CREATE OR REPLACE FUNCTION create_next_time_partition (p_parent_table text) RETURNS void LANGUAGE plpgsql AS $$ DECLARE v_control text; v_datetime_string text; v_last_partition text; v_next_partition_timestamp timestamp; v_next_year text; v_part_interval interval; v_quarter text; v_tablename text; v_time_position int; v_type text; v_year text; BEGIN SELECT type , part_interval::interval , control , datetime_string , last_partition FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic') INTO v_type, v_part_interval, v_control, v_datetime_string, v_last_partition; IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; -- Double check that last created partition exists IF v_last_partition IS NOT NULL THEN SELECT tablename INTO v_tablename FROM pg_tables WHERE schemaname || '.' || tablename = v_last_partition; IF v_tablename IS NULL THEN RAISE EXCEPTION 'ERROR: previous partition table missing. Unable to determine next proper partition in sequence.'; END IF; ELSE RAISE EXCEPTION 'ERROR: last known partition missing from config table for parent table %.', p_parent_table; END IF; -- pull out datetime portion of last partition's tablename to make the next one v_time_position := (length(v_last_partition) - position('p_' in reverse(v_last_partition))) + 2; IF v_part_interval != '3 months' THEN v_next_partition_timestamp := to_timestamp(substring(v_last_partition from v_time_position), v_datetime_string) + v_part_interval; ELSE -- to_timestamp doesn't recognize 'Q' date string formater. Handle it v_year := split_part(substring(v_last_partition from v_time_position), 'q', 1); v_next_year := extract('year' from to_date(v_year, 'YYYY')+'1year'::interval); v_quarter := split_part(substring(v_last_partition from v_time_position), 'q', 2); CASE WHEN v_quarter = '1' THEN v_next_partition_timestamp := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); WHEN v_quarter = '2' THEN v_next_partition_timestamp := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); WHEN v_quarter = '3' THEN v_next_partition_timestamp := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); WHEN v_quarter = '4' THEN v_next_partition_timestamp := to_timestamp(v_next_year || '-01-01', 'YYYY-MM-DD'); END CASE; END IF; EXECUTE 'SELECT @extschema@.create_time_partition('||quote_literal(p_parent_table)||','||quote_literal(v_control)||','||quote_literal(v_part_interval)||',' ||quote_literal(v_datetime_string)||','||quote_literal(ARRAY[v_next_partition_timestamp])||')' INTO v_last_partition; IF v_last_partition IS NOT NULL THEN UPDATE @extschema@.part_config SET last_partition = v_last_partition WHERE parent_table = p_parent_table; END IF; END $$; /* * Function to undo time-based partitioning created by this extension */ CREATE OR REPLACE FUNCTION undo_partition_time(p_parent_table text, p_batch_count int DEFAULT 1, p_batch_interval interval DEFAULT NULL, p_keep_table boolean DEFAULT true) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_batch_loop_count int := 0; v_child_min timestamptz; v_child_loop_total bigint := 0; v_child_table text; v_control text; v_function_name text; v_inner_loop_count int; v_job_id bigint; v_jobmon_schema text; v_move_sql text; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_part_interval interval; v_row record; v_rowcount bigint; v_step_id bigint; v_total bigint := 0; v_trig_name text; v_undo_count int := 0; BEGIN v_adv_lock := pg_try_advisory_lock(hashtext('pg_partman undo_time_partition')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'undo_time_partition already running.'; RETURN 0; END IF; SELECT part_interval::interval , control INTO v_part_interval , v_control FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic'); IF v_part_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN UNDO PARTITIONING: '||p_parent_table); v_step_id := add_step(v_job_id, 'Undoing partitioning for table '||p_parent_table); END IF; IF p_batch_interval IS NULL THEN p_batch_interval := v_part_interval; END IF; -- Stops new time partitons 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; -- Stop data going into child tables. SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; 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, v_parent_schema, '_part_trig_func', FALSE); EXECUTE 'DROP TRIGGER IF EXISTS '||v_trig_name||' ON '||p_parent_table; EXECUTE 'DROP FUNCTION IF EXISTS '||v_function_name||'()'; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Stopped partition creation process. Removed trigger & trigger function'); END IF; <> WHILE v_batch_loop_count < p_batch_count LOOP SELECT n.nspname||'.'||c.relname INTO v_child_table FROM pg_inherits i JOIN pg_class c ON i.inhrelid = c.oid JOIN pg_namespace n ON c.relnamespace = n.oid WHERE i.inhparent::regclass = p_parent_table::regclass ORDER BY i.inhrelid ASC; EXIT WHEN v_child_table IS NULL; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing child partition: '||v_child_table); END IF; EXECUTE 'SELECT min('||v_control||') FROM '||v_child_table INTO v_child_min; IF v_child_min IS NULL THEN -- No rows left in this child table. Remove from partition set. EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; IF p_keep_table = false THEN EXECUTE 'DROP TABLE '||v_child_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table DROPPED. Moved '||v_child_loop_total||' rows to parent'); END IF; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table UNINHERITED, not DROPPED. Moved '||v_child_loop_total||' rows to parent'); END IF; END IF; v_undo_count := v_undo_count + 1; CONTINUE outer_child_loop; END IF; v_inner_loop_count := 1; v_child_loop_total := 0; <> LOOP -- Get everything from the current child minimum up to the multiples of the given interval v_move_sql := 'WITH move_data AS (DELETE FROM '||v_child_table|| ' WHERE '||v_control||' <= '||quote_literal(v_child_min + (p_batch_interval * v_inner_loop_count))||' RETURNING *) INSERT INTO '||p_parent_table||' SELECT * FROM move_data'; EXECUTE v_move_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total := v_total + v_rowcount; v_child_loop_total := v_child_loop_total + v_rowcount; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Moved '||v_child_loop_total||' rows to parent.'); END IF; EXIT inner_child_loop WHEN v_rowcount = 0; -- exit before loop incr if table is empty v_inner_loop_count := v_inner_loop_count + 1; v_batch_loop_count := v_batch_loop_count + 1; EXIT outer_child_loop WHEN v_batch_loop_count >= p_batch_count; -- Exit outer FOR loop if p_batch_count is reached END LOOP inner_child_loop; END LOOP outer_child_loop; IF v_batch_loop_count < p_batch_count THEN -- FOR loop never ran, so there's no child tables left. DELETE FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing config from pg_partman'); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; RAISE NOTICE 'Copied % row(s) to the parent. Removed % partitions.', v_total, v_undo_count; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Final stats'); PERFORM update_step(v_step_id, 'OK', 'Copied '||v_total||' row(s) to the parent. Removed '||v_undo_count||' partitions.'); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; PERFORM pg_advisory_unlock(hashtext('pg_partman undo_time_partition')); RETURN v_total; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN UNDO PARTITIONING: '||p_parent_table); v_step_id := add_step(v_job_id, 'Partition function maintenance for table '||p_parent_table||' failed'); ELSIF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'CRITICAL', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to undo id-based partitioning created by this extension */ CREATE OR REPLACE FUNCTION undo_partition_id(p_parent_table text, p_batch_count int DEFAULT 1, p_batch_interval bigint DEFAULT NULL, p_keep_table boolean DEFAULT true) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_batch_loop_count int := 0; v_child_loop_total bigint := 0; v_child_min bigint; v_child_table text; v_control text; v_function_name text; v_inner_loop_count int; v_job_id bigint; v_jobmon_schema text; v_move_sql text; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_part_interval bigint; v_row record; v_rowcount bigint; v_step_id bigint; v_trig_name text; v_total bigint := 0; v_undo_count int := 0; BEGIN v_adv_lock := pg_try_advisory_lock(hashtext('pg_partman undo_id_partition')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'undo_id_partition already running.'; RETURN 0; END IF; SELECT part_interval::bigint , control INTO v_part_interval , v_control FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic'); IF v_part_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN UNDO PARTITIONING: '||p_parent_table); v_step_id := add_step(v_job_id, 'Undoing partitioning for table '||p_parent_table); END IF; IF p_batch_interval IS NULL THEN p_batch_interval := v_part_interval; END IF; -- Stops new time partitons 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; -- Stop data going into child tables and stop new id partitions from being made. SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; 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, v_parent_schema, '_part_trig_func', FALSE); EXECUTE 'DROP TRIGGER IF EXISTS '||v_trig_name||' ON '||p_parent_table; EXECUTE 'DROP FUNCTION IF EXISTS '||v_function_name||'()'; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Stopped partition creation process. Removed trigger & trigger function'); END IF; <> WHILE v_batch_loop_count < p_batch_count LOOP SELECT n.nspname||'.'||c.relname INTO v_child_table FROM pg_inherits i JOIN pg_class c ON i.inhrelid = c.oid JOIN pg_namespace n ON c.relnamespace = n.oid WHERE i.inhparent::regclass = p_parent_table::regclass ORDER BY i.inhrelid ASC; EXIT WHEN v_child_table IS NULL; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing child partition: '||v_child_table); END IF; EXECUTE 'SELECT min('||v_control||') FROM '||v_child_table INTO v_child_min; IF v_child_min IS NULL THEN -- No rows left in this child table. Remove from partition set. EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; IF p_keep_table = false THEN EXECUTE 'DROP TABLE '||v_child_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table DROPPED. Moved '||v_child_loop_total||' rows to parent'); END IF; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table UNINHERITED, not DROPPED. Moved '||v_child_loop_total||' rows to parent'); END IF; END IF; v_undo_count := v_undo_count + 1; CONTINUE outer_child_loop; END IF; v_inner_loop_count := 1; v_child_loop_total := 0; <> LOOP -- Get everything from the current child minimum up to the multiples of the given interval v_move_sql := 'WITH move_data AS (DELETE FROM '||v_child_table|| ' WHERE '||v_control||' <= '||quote_literal(v_child_min + (p_batch_interval * v_inner_loop_count))||' RETURNING *) INSERT INTO '||p_parent_table||' SELECT * FROM move_data'; EXECUTE v_move_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total := v_total + v_rowcount; v_child_loop_total := v_child_loop_total + v_rowcount; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Moved '||v_child_loop_total||' rows to parent.'); END IF; EXIT inner_child_loop WHEN v_rowcount = 0; -- exit before loop incr if table is empty v_inner_loop_count := v_inner_loop_count + 1; v_batch_loop_count := v_batch_loop_count + 1; EXIT outer_child_loop WHEN v_batch_loop_count >= p_batch_count; -- Exit outer FOR loop if p_batch_count is reached END LOOP inner_child_loop; END LOOP outer_child_loop; IF v_batch_loop_count < p_batch_count THEN -- FOR loop never ran, so there's no child tables left. DELETE FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing config from pg_partman'); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; RAISE NOTICE 'Copied % row(s) to the parent. Removed % partitions.', v_total, v_undo_count; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Final stats'); PERFORM update_step(v_step_id, 'OK', 'Copied '||v_total||' row(s) to the parent. Removed '||v_undo_count||' partitions.'); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; PERFORM pg_advisory_unlock(hashtext('pg_partman undo_id_partition')); RETURN v_total; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN UNDO PARTITIONING: '||p_parent_table); v_step_id := add_step(v_job_id, 'Partition function maintenance for table '||p_parent_table||' failed'); ELSIF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'CRITICAL', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to undo partitioning. * Will actually work on any parent/child table set, not just ones created by pg_partman. */ CREATE OR REPLACE FUNCTION undo_partition(p_parent_table text, p_batch_count int DEFAULT 1, p_keep_table boolean DEFAULT true) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_batch_loop_count bigint := 0; v_child_count bigint; v_child_table text; v_copy_sql text; v_function_name text; v_job_id bigint; v_jobmon_schema text; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_part_interval interval; v_rowcount bigint; v_step_id bigint; v_total bigint := 0; v_trig_name text; v_undo_count int := 0; BEGIN v_adv_lock := pg_try_advisory_lock(hashtext('pg_partman undo_partition')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'undo_partition already running.'; RETURN 0; END IF; SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN UNDO PARTITIONING: '||p_parent_table); v_step_id := add_step(v_job_id, 'Undoing partitioning for table '||p_parent_table); END IF; -- Stops new time partitons 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; -- Stop data going into child tables and stop new id partitions from being made. SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; 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, v_parent_schema, '_part_trig_func', FALSE); EXECUTE 'DROP TRIGGER IF EXISTS '||v_trig_name||' ON '||p_parent_table; EXECUTE 'DROP FUNCTION IF EXISTS '||v_function_name||'()'; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Stopped partition creation process. Removed trigger & trigger function'); END IF; WHILE v_batch_loop_count < p_batch_count LOOP SELECT n.nspname||'.'||c.relname INTO v_child_table FROM pg_inherits i JOIN pg_class c ON i.inhrelid = c.oid JOIN pg_namespace n ON c.relnamespace = n.oid WHERE i.inhparent::regclass = p_parent_table::regclass ORDER BY i.inhrelid ASC; EXIT WHEN v_child_table IS NULL; EXECUTE 'SELECT count(*) FROM '||v_child_table INTO v_child_count; IF v_child_count = 0 THEN -- No rows left in this child table. Remove from partition set. EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; IF p_keep_table = false THEN EXECUTE 'DROP TABLE '||v_child_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table DROPPED. Moved '||coalesce(v_rowcount, 0)||' rows to parent'); END IF; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table UNINHERITED, not DROPPED. Copied '||coalesce(v_rowcount, 0)||' rows to parent'); END IF; END IF; v_undo_count := v_undo_count + 1; CONTINUE; END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing child partition: '||v_child_table); END IF; v_copy_sql := 'INSERT INTO '||p_parent_table||' SELECT * FROM '||v_child_table; EXECUTE v_copy_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total := v_total + v_rowcount; EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; IF p_keep_table = false THEN EXECUTE 'DROP TABLE '||v_child_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table DROPPED. Moved '||v_rowcount||' rows to parent'); END IF; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table UNINHERITED, not DROPPED. Copied '||v_rowcount||' rows to parent'); END IF; END IF; v_batch_loop_count := v_batch_loop_count + 1; v_undo_count := v_undo_count + 1; END LOOP; IF v_undo_count = 0 THEN -- FOR loop never ran, so there's no child tables left. DELETE FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing config from pg_partman (if it existed)'); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; RAISE NOTICE 'Copied % row(s) from % child table(s) to the parent: %', v_total, v_undo_count, p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Final stats'); PERFORM update_step(v_step_id, 'OK', 'Copied '||v_total||' row(s) from '||v_undo_count||' child table(s) to the parent'); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; PERFORM pg_advisory_unlock(hashtext('pg_partman undo_partition')); RETURN v_total; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN UNDO PARTITIONING: '||p_parent_table); v_step_id := add_step(v_job_id, 'Partition function maintenance for table '||p_parent_table||' failed'); ELSIF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'CRITICAL', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to drop child tables from a time-based partition set. * Options to move table to different schema, drop only indexes or actually drop the table from the database. */ CREATE OR REPLACE FUNCTION 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 SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_child_table text; v_control text; v_drop_count int := 0; v_id_position int; v_index record; v_job_id bigint; v_jobmon_schema text; v_max bigint; v_old_search_path text; v_part_interval bigint; v_partition_id bigint; v_retention bigint; v_retention_keep_index boolean; v_retention_keep_table boolean; v_retention_schema text; v_step_id bigint; BEGIN v_adv_lock := pg_try_advisory_lock(hashtext('pg_partman drop_partition_id')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'drop_partition_id already running.'; RETURN 0; END IF; SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; -- Allow override of configuration options IF p_retention IS NULL THEN SELECT part_interval::bigint , control , retention::bigint , retention_keep_table , retention_keep_index , retention_schema INTO v_part_interval , v_control , v_retention , v_retention_keep_table , v_retention_keep_index , v_retention_schema FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic') AND retention IS NOT NULL; IF v_part_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table with a retention period not found: %', p_parent_table; END IF; ELSE SELECT part_interval::bigint , control , retention_keep_table , retention_keep_index , retention_schema INTO v_part_interval , v_control , v_retention_keep_table , v_retention_keep_index , v_retention_schema FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic'); v_retention := p_retention; IF v_part_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; END IF; IF p_keep_table IS NOT NULL THEN v_retention_keep_table = p_keep_table; END IF; IF p_keep_index IS NOT NULL THEN v_retention_keep_index = p_keep_index; END IF; IF p_retention_schema IS NOT NULL THEN v_retention_schema = p_retention_schema; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN DROP ID PARTITION: '|| p_parent_table); END IF; EXECUTE 'SELECT max('||v_control||') FROM '||p_parent_table INTO v_max; -- Loop through child tables of the given parent FOR v_child_table IN SELECT n.nspname||'.'||c.relname FROM pg_inherits i join pg_class c ON i.inhrelid = c.oid join pg_namespace n ON c.relnamespace = n.oid WHERE i.inhparent::regclass = p_parent_table::regclass ORDER BY i.inhrelid ASC LOOP v_id_position := (length(v_child_table) - position('p_' in reverse(v_child_table))) + 2; v_partition_id := substring(v_child_table from v_id_position)::bigint; -- Add one interval since partition names contain the start of the constraint period IF v_retention <= (v_max - (v_partition_id + v_part_interval)) THEN IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Uninherit table '||v_child_table||' from '||p_parent_table); END IF; EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; 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, 'Drop table '||v_child_table); END IF; EXECUTE 'DROP TABLE '||v_child_table; 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 FOR v_index IN SELECT i.indexrelid::regclass AS name , c.conname FROM pg_catalog.pg_index i LEFT JOIN pg_catalog.pg_constraint c ON i.indexrelid = c.conindid WHERE i.indrelid = v_child_table::regclass LOOP IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Drop index '||v_index.name||' from '||v_child_table); END IF; IF v_index.conname IS NOT NULL THEN EXECUTE 'ALTER TABLE '||v_child_table||' DROP CONSTRAINT '||v_index.conname; ELSE EXECUTE 'DROP INDEX '||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; ELSE -- Move to new schema IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Moving table '||v_child_table||' to schema '||v_retention_schema); END IF; EXECUTE 'ALTER TABLE '||v_child_table||' SET SCHEMA '||v_retention_schema; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; -- End retention schema if v_drop_count := v_drop_count + 1; END IF; -- End retention check IF END LOOP; -- End child table loop IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Finished partition drop maintenance'); PERFORM update_step(v_step_id, 'OK', v_drop_count||' partitions dropped.'); PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; PERFORM pg_advisory_unlock(hashtext('pg_partman drop_partition_id')); RETURN v_drop_count; EXCEPTION WHEN QUERY_CANCELED THEN PERFORM pg_advisory_unlock(hashtext('pg_partman drop_partition_id')); RAISE EXCEPTION '%', SQLERRM; WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN DROP ID PARTITION'); v_step_id := add_step(v_job_id, 'EXCEPTION before job logging started'); END IF; IF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'CRITICAL', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; PERFORM pg_advisory_unlock(hashtext('pg_partman drop_partition_id')); RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to drop child tables from a time-based partition set. * Options to move table to different schema, drop only indexes or actually drop the table from the database. */ CREATE OR REPLACE FUNCTION 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) RETURNS int LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_child_table text; v_datetime_string text; v_drop_count int := 0; v_index record; v_job_id bigint; v_jobmon_schema text; v_old_search_path text; v_part_interval interval; v_partition_timestamp timestamp; v_quarter text; v_retention interval; v_retention_keep_index boolean; v_retention_keep_table boolean; v_retention_schema text; v_step_id bigint; v_time_position int; v_year text; BEGIN v_adv_lock := pg_try_advisory_lock(hashtext('pg_partman drop_partition_time')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'drop_partition_time already running.'; RETURN 0; END IF; SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; -- Allow override of configuration options IF p_retention IS NULL THEN SELECT part_interval::interval , retention::interval , retention_keep_table , retention_keep_index , datetime_string , retention_schema INTO v_part_interval , v_retention , v_retention_keep_table , v_retention_keep_index , v_datetime_string , v_retention_schema FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic') AND retention IS NOT NULL; IF v_part_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table with a retention period not found: %', p_parent_table; END IF; ELSE SELECT part_interval::interval , retention_keep_table , retention_keep_index , datetime_string , retention_schema INTO v_part_interval , v_retention_keep_table , v_retention_keep_index , v_datetime_string , v_retention_schema FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic'); v_retention := p_retention; IF v_part_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; END IF; IF p_keep_table IS NOT NULL THEN v_retention_keep_table = p_keep_table; END IF; IF p_keep_index IS NOT NULL THEN v_retention_keep_index = p_keep_index; END IF; IF p_retention_schema IS NOT NULL THEN v_retention_schema = p_retention_schema; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN DROP TIME PARTITION: '|| p_parent_table); END IF; -- Loop through child tables of the given parent FOR v_child_table IN SELECT n.nspname||'.'||c.relname FROM pg_inherits i join pg_class c ON i.inhrelid = c.oid join pg_namespace n ON c.relnamespace = n.oid WHERE i.inhparent::regclass = p_parent_table::regclass ORDER BY i.inhrelid ASC LOOP -- pull out datetime portion of last partition's tablename to make the next one v_time_position := (length(v_child_table) - position('p_' in reverse(v_child_table))) + 2; IF v_part_interval != '3 months' THEN v_partition_timestamp := to_timestamp(substring(v_child_table from v_time_position), v_datetime_string); ELSE -- to_timestamp doesn't recognize 'Q' date string formater. Handle it v_year := split_part(substring(v_child_table from v_time_position), 'q', 1); v_quarter := split_part(substring(v_child_table from v_time_position), 'q', 2); CASE WHEN v_quarter = '1' THEN v_partition_timestamp := to_timestamp(v_year || '-01-01', 'YYYY-MM-DD'); WHEN v_quarter = '2' THEN v_partition_timestamp := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); WHEN v_quarter = '3' THEN v_partition_timestamp := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); WHEN v_quarter = '4' THEN v_partition_timestamp := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); END CASE; END IF; -- Add one interval since partition names contain the start of the constraint period IF v_retention < (CURRENT_TIMESTAMP - (v_partition_timestamp + v_part_interval)) THEN IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Uninherit table '||v_child_table||' from '||p_parent_table); END IF; EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; 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, 'Drop table '||v_child_table); END IF; EXECUTE 'DROP TABLE '||v_child_table; 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 FOR v_index IN SELECT i.indexrelid::regclass AS name , c.conname FROM pg_catalog.pg_index i LEFT JOIN pg_catalog.pg_constraint c ON i.indexrelid = c.conindid WHERE i.indrelid = v_child_table::regclass LOOP IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Drop index '||v_index.name||' from '||v_child_table); END IF; IF v_index.conname IS NOT NULL THEN EXECUTE 'ALTER TABLE '||v_child_table||' DROP CONSTRAINT '||v_index.conname; ELSE EXECUTE 'DROP INDEX '||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; ELSE -- Move to new schema IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Moving table '||v_child_table||' to schema '||v_retention_schema); END IF; EXECUTE 'ALTER TABLE '||v_child_table||' SET SCHEMA '||v_retention_schema; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; -- End retention schema if v_drop_count := v_drop_count + 1; END IF; -- End retention check IF END LOOP; -- End child table loop IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Finished partition drop maintenance'); PERFORM update_step(v_step_id, 'OK', v_drop_count||' partitions dropped.'); PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; PERFORM pg_advisory_unlock(hashtext('pg_partman drop_partition_time')); RETURN v_drop_count; EXCEPTION WHEN QUERY_CANCELED THEN PERFORM pg_advisory_unlock(hashtext('pg_partman drop_partition_time')); RAISE EXCEPTION '%', SQLERRM; WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN DROP TIME PARTITION'); v_step_id := add_step(v_job_id, 'EXCEPTION before job logging started'); END IF; IF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'CRITICAL', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; PERFORM pg_advisory_unlock(hashtext('pg_partman drop_partition_time')); RAISE EXCEPTION '%', SQLERRM; END $$; pg_partman-2.2.2/updates/pg_partman--1.4.0--1.4.1.sql000066400000000000000000000355111262146621700214260ustar00rootroot00000000000000-- 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. /* * Function to create id partitions */ CREATE OR REPLACE FUNCTION create_id_partition (p_parent_table text, p_control text, p_interval bigint, p_partition_ids bigint[]) RETURNS text LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_all text[] := ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER']; v_grantees text[]; v_job_id bigint; v_jobmon_schema text; v_old_search_path text; v_parent_grant record; v_parent_owner text; v_parent_schema text; v_parent_tablename text; v_parent_tablespace text; v_partition_name text; v_revoke text[]; v_step_id bigint; v_tablename text; v_id bigint; BEGIN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; SELECT tableowner, schemaname, tablename, tablespace INTO v_parent_owner, v_parent_schema, v_parent_tablename, v_parent_tablespace FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; FOREACH v_id IN ARRAY p_partition_ids LOOP v_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_id::text, TRUE); -- If child table already exists, skip creation SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_tablename IS NOT NULL THEN CONTINUE; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating new partition '||v_partition_name||' with interval from '||v_id||' to '||(v_id + p_interval)-1); END IF; EXECUTE 'CREATE TABLE '||v_partition_name||' (LIKE '||p_parent_table||' INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING STORAGE INCLUDING COMMENTS)'; SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_parent_tablespace IS NOT NULL THEN EXECUTE 'ALTER TABLE '||v_partition_name||' SET TABLESPACE '||v_parent_tablespace; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' ADD CONSTRAINT '||v_tablename||'_partition_check CHECK ('||p_control||'>='||quote_literal(v_id)||' AND '||p_control||'<'||quote_literal(v_id + p_interval)||')'; EXECUTE 'ALTER TABLE '||v_partition_name||' INHERIT '||p_parent_table; FOR v_parent_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = p_parent_table GROUP BY grantee LOOP EXECUTE 'GRANT '||array_to_string(v_parent_grant.types, ',')||' ON '||v_partition_name||' TO '||v_parent_grant.grantee; SELECT array_agg(r) INTO v_revoke FROM (SELECT unnest(v_all) AS r EXCEPT SELECT unnest(v_parent_grant.types)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE '||array_to_string(v_revoke, ',')||' ON '||v_partition_name||' FROM '||v_parent_grant.grantee||' CASCADE'; END IF; v_grantees := array_append(v_grantees, v_parent_grant.grantee::text); END LOOP; -- Revoke all privileges from roles that have none on the parent IF v_grantees IS NOT NULL THEN SELECT array_agg(r) INTO v_revoke FROM ( SELECT DISTINCT grantee::text AS r FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = v_partition_name EXCEPT SELECT unnest(v_grantees)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE ALL ON '||v_partition_name||' FROM '||array_to_string(v_revoke, ','); END IF; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' OWNER TO '||v_parent_owner; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); PERFORM close_job(v_job_id); END IF; END LOOP; IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_partition_name; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); v_step_id := add_step(v_job_id, 'Partition maintenance for table '||p_parent_table||' failed'); ELSIF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'BAD', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to create a child table in a time-based partition set */ CREATE OR REPLACE FUNCTION create_time_partition (p_parent_table text, p_control text, p_interval interval, p_datetime_string text, p_partition_times timestamp[]) RETURNS text LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_all text[] := ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER']; v_grantees text[]; v_job_id bigint; v_jobmon_schema text; v_old_search_path text; v_parent_grant record; v_parent_owner text; v_parent_schema text; v_parent_tablename text; v_partition_name text; v_partition_suffix text; v_parent_tablespace text; v_partition_timestamp_end timestamp; v_partition_timestamp_start timestamp; v_quarter text; v_revoke text[]; v_step_id bigint; v_tablename text; v_trunc_value text; v_time timestamp; v_year text; BEGIN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; SELECT tableowner, schemaname, tablename, tablespace INTO v_parent_owner, v_parent_schema, v_parent_tablename, v_parent_tablespace FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; FOREACH v_time IN ARRAY p_partition_times LOOP IF p_interval <= '1 year' AND p_interval <> '3 months' AND p_interval <> '1 week' THEN v_partition_suffix := to_char(v_time, 'YYYY'); v_trunc_value := 'year'; IF p_interval <= '1 month' AND p_interval <> '1 week' THEN v_partition_suffix := v_partition_suffix ||'_'|| to_char(v_time, 'MM'); v_trunc_value := 'month'; IF p_interval <= '1 day' THEN v_partition_suffix := v_partition_suffix ||'_'|| to_char(v_time, 'DD'); v_trunc_value := 'day'; IF p_interval <= '1 hour' THEN v_partition_suffix := v_partition_suffix || '_' || to_char(v_time, 'HH24'); IF p_interval <> '30 mins' AND p_interval <> '15 mins' THEN v_partition_suffix := v_partition_suffix || '00'; v_trunc_value := 'hour'; ELSIF p_interval = '15 mins' THEN IF date_part('minute', v_time) < 15 THEN v_partition_suffix := v_partition_suffix || '00'; ELSIF date_part('minute', v_time) >= 15 AND date_part('minute', v_time) < 30 THEN v_partition_suffix := v_partition_suffix || '15'; ELSIF date_part('minute', v_time) >= 30 AND date_part('minute', v_time) < 45 THEN v_partition_suffix := v_partition_suffix || '30'; ELSE v_partition_suffix := v_partition_suffix || '45'; END IF; v_trunc_value := 'minute'; ELSIF p_interval = '30 mins' THEN IF date_part('minute', v_time) < 30 THEN v_partition_suffix := v_partition_suffix || '00'; ELSE v_partition_suffix := v_partition_suffix || '30'; END IF; v_trunc_value := 'minute'; END IF; END IF; -- end hour IF END IF; -- end day IF END IF; -- end month IF ELSIF p_interval = '1 week' THEN v_partition_suffix := to_char(v_time, 'IYYY') || 'w' || to_char(v_time, 'IW'); v_trunc_value := 'week'; END IF; -- end year/week IF -- "Q" is ignored in to_timestamp, so handle special case IF p_interval = '3 months' THEN v_year := to_char(v_time, 'YYYY'); v_quarter := to_char(v_time, 'Q'); v_partition_suffix := v_year || 'q' || v_quarter; v_trunc_value := 'quarter'; CASE WHEN v_quarter = '1' THEN v_partition_timestamp_start := date_trunc(v_trunc_value, to_timestamp(v_year || '-01-01', 'YYYY-MM-DD')); WHEN v_quarter = '2' THEN v_partition_timestamp_start := date_trunc(v_trunc_value, to_timestamp(v_year || '-04-01', 'YYYY-MM-DD')); WHEN v_quarter = '3' THEN v_partition_timestamp_start := date_trunc(v_trunc_value, to_timestamp(v_year || '-07-01', 'YYYY-MM-DD')); WHEN v_quarter = '4' THEN v_partition_timestamp_start := date_trunc(v_trunc_value, to_timestamp(v_year || '-10-01', 'YYYY-MM-DD')); END CASE; v_partition_timestamp_end := date_trunc(v_trunc_value, (v_partition_timestamp_start + p_interval)); END IF; v_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_partition_suffix, TRUE); -- pull out datetime portion of last partition's tablename if it matched anything except quarterly IF p_interval <> '3 months' AND v_trunc_value IS NOT NULL THEN v_partition_timestamp_start := date_trunc(v_trunc_value, to_timestamp(v_partition_suffix, p_datetime_string)); v_partition_timestamp_end := date_trunc(v_trunc_value, to_timestamp(v_partition_suffix, p_datetime_string) + p_interval); END IF; SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_tablename IS NOT NULL THEN CONTINUE; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating new partition '||v_partition_name||' with interval from '||v_partition_timestamp_start||' to '||(v_partition_timestamp_end-'1sec'::interval)); END IF; EXECUTE 'CREATE TABLE '||v_partition_name||' (LIKE '||p_parent_table||' INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING STORAGE INCLUDING COMMENTS)'; SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_parent_tablespace IS NOT NULL THEN EXECUTE 'ALTER TABLE '||v_partition_name||' SET TABLESPACE '||v_parent_tablespace; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' ADD CONSTRAINT '||v_tablename||'_partition_check CHECK ('||p_control||'>='||quote_literal(v_partition_timestamp_start)||' AND '||p_control||'<'||quote_literal(v_partition_timestamp_end)||')'; EXECUTE 'ALTER TABLE '||v_partition_name||' INHERIT '||p_parent_table; FOR v_parent_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = p_parent_table GROUP BY grantee LOOP EXECUTE 'GRANT '||array_to_string(v_parent_grant.types, ',')||' ON '||v_partition_name||' TO '||v_parent_grant.grantee; SELECT array_agg(r) INTO v_revoke FROM (SELECT unnest(v_all) AS r EXCEPT SELECT unnest(v_parent_grant.types)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE '||array_to_string(v_revoke, ',')||' ON '||v_partition_name||' FROM '||v_parent_grant.grantee||' CASCADE'; END IF; v_grantees := array_append(v_grantees, v_parent_grant.grantee::text); END LOOP; -- Revoke all privileges from roles that have none on the parent IF v_grantees IS NOT NULL THEN SELECT array_agg(r) INTO v_revoke FROM ( SELECT DISTINCT grantee::text AS r FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = v_partition_name EXCEPT SELECT unnest(v_grantees)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE ALL ON '||v_partition_name||' FROM '||array_to_string(v_revoke, ','); END IF; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' OWNER TO '||v_parent_owner; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); PERFORM close_job(v_job_id); END IF; END LOOP; IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_partition_name; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); v_step_id := add_step(v_job_id, 'Partition maintenance for table '||p_parent_table||' failed'); ELSIF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'CRITICAL', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; pg_partman-2.2.2/updates/pg_partman--1.4.1--1.4.2.sql000066400000000000000000000202761262146621700214320ustar00rootroot00000000000000DROP FUNCTION IF EXISTS partition_data_id(text, int, int); DROP FUNCTION IF EXISTS partition_data_time(text, int, interval); /* * Populate the child table(s) of an id-based partition set with old data from the original parent */ CREATE FUNCTION partition_data_id(p_parent_table text, p_batch_count int DEFAULT 1, p_batch_interval int DEFAULT NULL, p_lock_wait numeric DEFAULT 0) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_control text; v_last_partition_name text; v_max_partition_id bigint; v_min_control bigint; v_min_partition_id bigint; v_part_interval bigint; v_partition_id bigint[]; v_rowcount bigint; v_sql text; v_total_rows bigint := 0; v_lock_iter int := 1; v_lock_obtained boolean := FALSE; BEGIN SELECT part_interval::bigint, control INTO v_part_interval, v_control FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF p_batch_interval IS NULL OR p_batch_interval > v_part_interval THEN p_batch_interval := v_part_interval; END IF; FOR i IN 1..p_batch_count LOOP EXECUTE 'SELECT min('||v_control||') FROM ONLY '||p_parent_table INTO v_min_control; IF v_min_control IS NULL THEN RETURN 0; END IF; v_min_partition_id = v_min_control - (v_min_control % v_part_interval); v_partition_id := ARRAY[v_min_partition_id]; -- RAISE NOTICE 'v_partition_id: %',v_partition_id; IF (v_min_control + p_batch_interval) >= (v_min_partition_id + v_part_interval) THEN v_max_partition_id := v_min_partition_id + v_part_interval; ELSE v_max_partition_id := v_min_control + p_batch_interval; END IF; -- RAISE NOTICE 'v_max_partition_id: %',v_max_partition_id; -- do some locking with timeout, if required IF p_lock_wait > 0 THEN WHILE v_lock_iter <= 5 LOOP v_lock_iter := v_lock_iter + 1; BEGIN v_sql := 'SELECT * FROM ' || p_parent_table || ' WHERE '||v_control||' >= '||v_min_control|| ' AND '||v_control||' < '||v_max_partition_id ||' FOR UPDATE NOWAIT'; EXECUTE v_sql; 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 RETURN -1; END IF; END IF; v_sql := 'SELECT @extschema@.create_id_partition('||quote_literal(p_parent_table)||','||quote_literal(v_control)||',' ||v_part_interval||','||quote_literal(v_partition_id)||')'; -- RAISE NOTICE 'v_sql: %', v_sql; EXECUTE v_sql INTO v_last_partition_name; v_sql := 'WITH partition_data AS ( DELETE FROM ONLY '||p_parent_table||' WHERE '||v_control||' >= '||v_min_control|| ' AND '||v_control||' < '||v_max_partition_id||' RETURNING *) INSERT INTO '||v_last_partition_name||' SELECT * FROM partition_data'; -- RAISE NOTICE 'v_sql: %', v_sql; EXECUTE v_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total_rows := v_total_rows + v_rowcount; IF v_rowcount = 0 THEN EXIT; END IF; END LOOP; RETURN v_total_rows; END $$; /* * Populate the child table(s) of a time-based partition set with old data from the original parent */ CREATE FUNCTION 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) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_control text; v_datetime_string text; v_last_partition_name text; v_max_partition_timestamp timestamp; v_min_control timestamp; v_min_partition_timestamp timestamp; v_part_interval interval; v_partition_timestamp timestamp[]; v_rowcount bigint; v_sql text; v_total_rows bigint := 0; v_lock_iter int := 1; v_lock_obtained boolean := FALSE; BEGIN SELECT part_interval::interval, control, datetime_string INTO v_part_interval, v_control, v_datetime_string FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF p_batch_interval IS NULL OR p_batch_interval > v_part_interval THEN p_batch_interval := v_part_interval; END IF; FOR i IN 1..p_batch_count LOOP EXECUTE 'SELECT min('||v_control||') FROM ONLY '||p_parent_table INTO v_min_control; IF v_min_control IS NULL THEN RETURN 0; END IF; CASE WHEN v_part_interval = '15 mins' THEN v_min_partition_timestamp := date_trunc('hour', v_min_control) + '15min'::interval * floor(date_part('minute', v_min_control) / 15.0); WHEN v_part_interval = '30 mins' THEN v_min_partition_timestamp := date_trunc('hour', v_min_control) + '30min'::interval * floor(date_part('minute', v_min_control) / 30.0); WHEN v_part_interval = '1 hour' THEN v_min_partition_timestamp := date_trunc('hour', v_min_control); WHEN v_part_interval = '1 day' THEN v_min_partition_timestamp := date_trunc('day', v_min_control); WHEN v_part_interval = '1 week' THEN v_min_partition_timestamp := date_trunc('week', v_min_control); WHEN v_part_interval = '1 month' THEN v_min_partition_timestamp := date_trunc('month', v_min_control); WHEN v_part_interval = '3 months' THEN v_min_partition_timestamp := date_trunc('quarter', v_min_control); WHEN v_part_interval = '1 year' THEN v_min_partition_timestamp := date_trunc('year', v_min_control); END CASE; v_partition_timestamp := ARRAY[v_min_partition_timestamp]; -- RAISE NOTICE 'v_partition_timestamp: %',v_partition_timestamp; IF (v_min_control + p_batch_interval) >= (v_min_partition_timestamp + v_part_interval) THEN v_max_partition_timestamp := v_min_partition_timestamp + v_part_interval; ELSE v_max_partition_timestamp := v_min_control + p_batch_interval; END IF; -- RAISE NOTICE 'v_max_partition_timestamp: %',v_max_partition_timestamp; -- do some locking with timeout, if required IF p_lock_wait > 0 THEN WHILE v_lock_iter <= 5 LOOP v_lock_iter := v_lock_iter + 1; BEGIN v_sql := 'SELECT * FROM ' || p_parent_table || ' WHERE '||v_control||' >= '||v_min_control|| ' AND '||v_control||' < '||v_max_partition_id ||' FOR UPDATE NOWAIT'; EXECUTE v_sql; 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 RETURN -1; END IF; END IF; v_sql := 'SELECT @extschema@.create_time_partition('||quote_literal(p_parent_table)||','||quote_literal(v_control)||',' ||quote_literal(v_part_interval)||','||quote_literal(v_datetime_string)||','||quote_literal(v_partition_timestamp)||')'; -- RAISE NOTICE 'v_sql: %', v_sql; EXECUTE v_sql INTO v_last_partition_name; v_sql := 'WITH partition_data AS ( DELETE FROM ONLY '||p_parent_table||' WHERE '||v_control||' >= '||quote_literal(v_min_control)|| ' AND '||v_control||' < '||quote_literal(v_max_partition_timestamp)||' RETURNING *) INSERT INTO '||v_last_partition_name||' SELECT * FROM partition_data'; -- RAISE NOTICE 'v_sql: %', v_sql; EXECUTE v_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total_rows := v_total_rows + v_rowcount; IF v_rowcount = 0 THEN EXIT; END IF; END LOOP; RETURN v_total_rows; END $$; pg_partman-2.2.2/updates/pg_partman--1.4.2--1.4.3.sql000066400000000000000000000011601262146621700214230ustar00rootroot00000000000000-- 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. -- No code changes to core extension, however this file is required to update to pg_partman 1.4.3 and higher. pg_partman-2.2.2/updates/pg_partman--1.4.3--1.4.4.sql000066400000000000000000000204661262146621700214370ustar00rootroot00000000000000-- Bug fix: Typos in partition_time_data/id() functions. Only ran into this if a lockwait was hit while trying to partition data. /* * Populate the child table(s) of a time-based partition set with old data from the original parent */ CREATE OR REPLACE FUNCTION 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) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_control text; v_datetime_string text; v_last_partition_name text; v_max_partition_timestamp timestamp; v_min_control timestamp; v_min_partition_timestamp timestamp; v_part_interval interval; v_partition_timestamp timestamp[]; v_rowcount bigint; v_sql text; v_total_rows bigint := 0; v_lock_iter int := 1; v_lock_obtained boolean := FALSE; BEGIN SELECT part_interval::interval, control, datetime_string INTO v_part_interval, v_control, v_datetime_string FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF p_batch_interval IS NULL OR p_batch_interval > v_part_interval THEN p_batch_interval := v_part_interval; END IF; FOR i IN 1..p_batch_count LOOP EXECUTE 'SELECT min('||v_control||') FROM ONLY '||p_parent_table INTO v_min_control; IF v_min_control IS NULL THEN RETURN 0; END IF; CASE WHEN v_part_interval = '15 mins' THEN v_min_partition_timestamp := date_trunc('hour', v_min_control) + '15min'::interval * floor(date_part('minute', v_min_control) / 15.0); WHEN v_part_interval = '30 mins' THEN v_min_partition_timestamp := date_trunc('hour', v_min_control) + '30min'::interval * floor(date_part('minute', v_min_control) / 30.0); WHEN v_part_interval = '1 hour' THEN v_min_partition_timestamp := date_trunc('hour', v_min_control); WHEN v_part_interval = '1 day' THEN v_min_partition_timestamp := date_trunc('day', v_min_control); WHEN v_part_interval = '1 week' THEN v_min_partition_timestamp := date_trunc('week', v_min_control); WHEN v_part_interval = '1 month' THEN v_min_partition_timestamp := date_trunc('month', v_min_control); WHEN v_part_interval = '3 months' THEN v_min_partition_timestamp := date_trunc('quarter', v_min_control); WHEN v_part_interval = '1 year' THEN v_min_partition_timestamp := date_trunc('year', v_min_control); END CASE; v_partition_timestamp := ARRAY[v_min_partition_timestamp]; -- RAISE NOTICE 'v_partition_timestamp: %',v_partition_timestamp; IF (v_min_control + p_batch_interval) >= (v_min_partition_timestamp + v_part_interval) THEN v_max_partition_timestamp := v_min_partition_timestamp + v_part_interval; ELSE v_max_partition_timestamp := v_min_control + p_batch_interval; END IF; -- RAISE NOTICE 'v_max_partition_timestamp: %',v_max_partition_timestamp; -- do some locking with timeout, if required IF p_lock_wait > 0 THEN WHILE v_lock_iter <= 5 LOOP v_lock_iter := v_lock_iter + 1; BEGIN v_sql := 'SELECT * FROM ONLY ' || p_parent_table || ' WHERE '||v_control||' >= '||quote_literal(v_min_control)|| ' AND '||v_control||' < '||quote_literal(v_max_partition_timestamp) ||' FOR UPDATE NOWAIT'; EXECUTE v_sql; 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 RETURN -1; END IF; END IF; v_sql := 'SELECT @extschema@.create_time_partition('||quote_literal(p_parent_table)||','||quote_literal(v_control)||',' ||quote_literal(v_part_interval)||','||quote_literal(v_datetime_string)||','||quote_literal(v_partition_timestamp)||')'; -- RAISE NOTICE 'v_sql: %', v_sql; EXECUTE v_sql INTO v_last_partition_name; v_sql := 'WITH partition_data AS ( DELETE FROM ONLY '||p_parent_table||' WHERE '||v_control||' >= '||quote_literal(v_min_control)|| ' AND '||v_control||' < '||quote_literal(v_max_partition_timestamp)||' RETURNING *) INSERT INTO '||v_last_partition_name||' SELECT * FROM partition_data'; -- RAISE NOTICE 'v_sql: %', v_sql; EXECUTE v_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total_rows := v_total_rows + v_rowcount; IF v_rowcount = 0 THEN EXIT; END IF; END LOOP; RETURN v_total_rows; END $$; /* * Populate the child table(s) of an id-based partition set with old data from the original parent */ CREATE OR REPLACE FUNCTION partition_data_id(p_parent_table text, p_batch_count int DEFAULT 1, p_batch_interval int DEFAULT NULL, p_lock_wait numeric DEFAULT 0) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_control text; v_last_partition_name text; v_max_partition_id bigint; v_min_control bigint; v_min_partition_id bigint; v_part_interval bigint; v_partition_id bigint[]; v_rowcount bigint; v_sql text; v_total_rows bigint := 0; v_lock_iter int := 1; v_lock_obtained boolean := FALSE; BEGIN SELECT part_interval::bigint, control INTO v_part_interval, v_control FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF p_batch_interval IS NULL OR p_batch_interval > v_part_interval THEN p_batch_interval := v_part_interval; END IF; FOR i IN 1..p_batch_count LOOP EXECUTE 'SELECT min('||v_control||') FROM ONLY '||p_parent_table INTO v_min_control; IF v_min_control IS NULL THEN RETURN 0; END IF; v_min_partition_id = v_min_control - (v_min_control % v_part_interval); v_partition_id := ARRAY[v_min_partition_id]; -- RAISE NOTICE 'v_partition_id: %',v_partition_id; IF (v_min_control + p_batch_interval) >= (v_min_partition_id + v_part_interval) THEN v_max_partition_id := v_min_partition_id + v_part_interval; ELSE v_max_partition_id := v_min_control + p_batch_interval; END IF; -- RAISE NOTICE 'v_max_partition_id: %',v_max_partition_id; -- do some locking with timeout, if required IF p_lock_wait > 0 THEN WHILE v_lock_iter <= 5 LOOP v_lock_iter := v_lock_iter + 1; BEGIN v_sql := 'SELECT * FROM ONLY ' || p_parent_table || ' WHERE '||v_control||' >= '||quote_literal(v_min_control)|| ' AND '||v_control||' < '||quote_literal(v_max_partition_id) ||' FOR UPDATE NOWAIT'; EXECUTE v_sql; 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 RETURN -1; END IF; END IF; v_sql := 'SELECT @extschema@.create_id_partition('||quote_literal(p_parent_table)||','||quote_literal(v_control)||',' ||v_part_interval||','||quote_literal(v_partition_id)||')'; -- RAISE NOTICE 'v_sql: %', v_sql; EXECUTE v_sql INTO v_last_partition_name; v_sql := 'WITH partition_data AS ( DELETE FROM ONLY '||p_parent_table||' WHERE '||v_control||' >= '||v_min_control|| ' AND '||v_control||' < '||v_max_partition_id||' RETURNING *) INSERT INTO '||v_last_partition_name||' SELECT * FROM partition_data'; -- RAISE NOTICE 'v_sql: %', v_sql; EXECUTE v_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total_rows := v_total_rows + v_rowcount; IF v_rowcount = 0 THEN EXIT; END IF; END LOOP; RETURN v_total_rows; END $$; pg_partman-2.2.2/updates/pg_partman--1.4.4--1.4.5.sql000066400000000000000000000011261262146621700214310ustar00rootroot00000000000000-- 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. -- No code changes to core extension, however this file is required to update to pg_partman 1.4.5 and higher. pg_partman-2.2.2/updates/pg_partman--1.4.5--1.5.0.sql000066400000000000000000001234261262146621700214360ustar00rootroot00000000000000-- 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. ALTER TABLE @extschema@.part_config ADD constraint_cols text[]; CREATE TEMP TABLE partman_preserve_privs_temp (statement text); INSERT INTO partman_preserve_privs_temp SELECT 'GRANT EXECUTE ON FUNCTION @extschema@.create_parent(text, text, text, text, text[], int, boolean) TO '||array_to_string(array_agg(grantee::text), ',')||';' FROM information_schema.routine_privileges WHERE routine_schema = '@extschema@' AND routine_name = 'create_parent'; INSERT INTO partman_preserve_privs_temp SELECT 'GRANT EXECUTE ON FUNCTION @extschema@.show_partitions(text, text) TO '||array_to_string(array_agg(grantee::text), ',')||';' FROM information_schema.routine_privileges WHERE routine_schema = '@extschema@' AND routine_name = 'show_partitions'; CREATE FUNCTION replay_preserved_privs() RETURNS void LANGUAGE plpgsql AS $$ DECLARE v_row record; BEGIN FOR v_row IN SELECT statement FROM partman_preserve_privs_temp LOOP IF v_row.statement IS NOT NULL THEN EXECUTE v_row.statement; END IF; END LOOP; END $$; DROP FUNCTION @extschema@.create_parent(text, text, text, text, int, boolean); DROP FUNCTION @extschema@.show_partitions(text); /* * Apply constraints managed by partman extension */ CREATE FUNCTION apply_constraints(p_parent_table text, p_child_table text DEFAULT NULL, p_debug BOOLEAN DEFAULT FALSE) RETURNS void LANGUAGE plpgsql AS $$ DECLARE v_child_table text; v_child_tablename text; v_col text; v_constraint_cols text[]; v_constraint_col_type text; v_constraint_name text; v_datetime_string text; v_existing_constraint_name text; v_job_id bigint; v_jobmon_schema text; v_last_partition text; v_last_partition_id int; v_last_partition_timestamp timestamp; v_constraint_values record; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_part_interval text; v_partition_suffix text; v_premake int; v_sql text; v_step_id bigint; v_suffix_position int; v_type text; BEGIN SELECT type , part_interval , premake , datetime_string , last_partition , constraint_cols INTO v_type , v_part_interval , v_premake , v_datetime_string , v_last_partition , v_constraint_cols FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_constraint_cols IS NULL THEN IF p_debug THEN RAISE NOTICE 'Given parent table (%) not set up for constraint management (constraint_cols is NULL)', p_parent_table; END IF; -- Returns silently to allow this function to be simply called by maintenance processes without having to check if config options are set. RETURN; END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; -- If p_child_table is null, figure out the partition that is the one right before the premake value backwards. IF p_child_table IS NULL THEN v_suffix_position := (length(v_last_partition) - position('p_' in reverse(v_last_partition))) + 2; IF v_type IN ('time-static', 'time-dynamic') THEN v_last_partition_timestamp := to_timestamp(substring(v_last_partition from v_suffix_position), v_datetime_string); v_partition_suffix := to_char(v_last_partition_timestamp - (v_part_interval::interval * ((v_premake * 2)+1) ), v_datetime_string); ELSIF v_type IN ('id-static', 'id-dynamic') THEN v_last_partition_id := substring(v_last_partition from v_suffix_position)::int; v_partition_suffix := (v_last_partition_id - (v_part_interval::int * ((v_premake * 2)+1) ))::text; END IF; v_child_table := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_partition_suffix, TRUE); ELSE v_child_table := p_child_table; END IF; SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE CONSTRAINT: '||p_parent_table); v_step_id := add_step(v_job_id, 'Checking if target child table exists'); END IF; SELECT tablename INTO v_child_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_child_table; IF v_child_tablename IS NULL THEN IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'NOTICE', 'Target child table ('||v_child_table||') does not exist. Skipping constraint creation.'); PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; IF p_debug THEN RAISE NOTICE 'Target child table (%) does not exist. Skipping constraint creation.', v_child_table; END IF; RETURN; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; FOREACH v_col IN ARRAY v_constraint_cols LOOP SELECT c.conname INTO v_existing_constraint_name FROM pg_catalog.pg_constraint c JOIN pg_catalog.pg_attribute a ON c.conrelid = a.attrelid WHERE conrelid = v_child_table::regclass AND c.conname LIKE 'partmanconstr_%' AND c.contype = 'c' AND a.attname = v_col AND ARRAY[a.attnum] <@ c.conkey AND a.attisdropped = false; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Applying new constraint on column: '||v_col); END IF; IF v_existing_constraint_name IS NOT NULL THEN IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'NOTICE', 'Partman managed constraint already exists on this table ('||v_child_table||') and column ('||v_col||'). Skipping creation.'); END IF; RAISE WARNING 'Partman managed constraint already exists on this table (%) and column (%). Skipping creation.', v_child_table, v_col ; CONTINUE; END IF; -- 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 'SELECT min('||v_col||')::text AS min, max('||v_col||')::text AS max FROM '||v_child_table INTO v_constraint_values; IF v_constraint_values IS NOT NULL THEN v_sql := concat('ALTER TABLE ', v_child_table, ' ADD CONSTRAINT ', v_constraint_name , ' CHECK (', v_col, ' >= ', quote_literal(v_constraint_values.min), ' AND ' , v_col, ' <= ', quote_literal(v_constraint_values.max), ')' ); IF p_debug THEN RAISE NOTICE 'Constraint creation query: %', v_sql; END IF; EXECUTE v_sql; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'New constraint created: '||v_sql); END IF; ELSE IF p_debug THEN RAISE NOTICE 'Given column (%) contains all NULLs. No constraint created', v_col; END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'NOTICE', 'Given column ('||v_col||') contains all NULLs. No constraint created'); END IF; END IF; END LOOP; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN CREATE CONSTRAINT: '||p_parent_table||')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''Partition constraint maintenance for table '||p_parent_table||' failed'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Drop constraints managed by pg_partman */ CREATE FUNCTION drop_constraints(p_parent_table text, p_child_table text, p_debug boolean DEFAULT false) RETURNS void LANGUAGE plpgsql AS $$ DECLARE v_col text; v_constraint_cols text[]; v_existing_constraint_name text; v_exists boolean := FALSE; v_job_id bigint; v_jobmon_schema text; v_old_search_path text; v_sql text; v_step_id bigint; BEGIN SELECT constraint_cols INTO v_constraint_cols FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_constraint_cols IS NULL THEN RAISE EXCEPTION 'Given parent table (%) not set up for constraint management (constraint_cols is NULL)', p_parent_table; END IF; SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN DROP CONSTRAINT: '||p_parent_table); v_step_id := add_step(v_job_id, 'Entering constraint drop loop'); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; FOREACH v_col IN ARRAY v_constraint_cols LOOP SELECT c.conname INTO v_existing_constraint_name FROM pg_catalog.pg_constraint c JOIN pg_catalog.pg_attribute a ON c.conrelid = a.attrelid WHERE conrelid = p_child_table::regclass AND c.conname LIKE 'partmanconstr_%' AND c.contype = 'c' AND a.attname = v_col AND ARRAY[a.attnum] <@ c.conkey AND a.attisdropped = false; IF v_existing_constraint_name IS NOT NULL THEN v_exists := TRUE; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Dropping constraint on column: '||v_col); END IF; v_sql := 'ALTER TABLE '||p_child_table||' DROP CONSTRAINT '||v_existing_constraint_name; IF p_debug THEN RAISE NOTICE 'Constraint drop query: %', v_sql; END IF; EXECUTE v_sql; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Drop constraint query: '||v_sql); END IF; END IF; END LOOP; IF v_jobmon_schema IS NOT NULL AND v_exists IS FALSE THEN v_step_id := add_step(v_job_id, 'No constraints found to drop on child table: '||p_child_table); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN DROP CONSTRAINT: '||p_parent_table||')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''Partition constraint maintenance for table '||p_parent_table||' failed'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Create the trigger function for the parent table of an id-based partition set */ CREATE OR REPLACE FUNCTION create_id_function(p_parent_table text, p_current_id bigint) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_control text; v_count int; v_current_partition_name text; v_current_partition_id bigint; v_datetime_string text; v_final_partition_id bigint; v_function_name text; v_job_id bigint; v_jobmon_schema text; v_last_partition text; v_next_partition_id bigint; v_next_partition_name text; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_part_interval bigint; v_premake int; v_prev_partition_id bigint; v_prev_partition_name text; v_step_id bigint; v_trig_func text; v_type text; BEGIN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE FUNCTION: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating partition function for table '||p_parent_table); END IF; SELECT type , part_interval::bigint , control , premake , last_partition INTO v_type , v_part_interval , v_control , v_premake , v_last_partition FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; v_function_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, '_part_trig_func', FALSE); IF v_type = 'id-static' THEN v_current_partition_id := p_current_id - (p_current_id % v_part_interval); v_next_partition_id := v_current_partition_id + v_part_interval; v_current_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_current_partition_id::text, TRUE); v_trig_func := 'CREATE OR REPLACE FUNCTION '||v_function_name||'() RETURNS trigger LANGUAGE plpgsql AS $t$ DECLARE v_current_partition_id bigint; v_last_partition text := '||quote_literal(v_last_partition)||'; v_id_position int; v_next_partition_id bigint; v_next_partition_name text; BEGIN IF TG_OP = ''INSERT'' THEN IF NEW.'||v_control||' >= '||v_current_partition_id||' AND NEW.'||v_control||' < '||v_next_partition_id|| ' THEN INSERT INTO '||v_current_partition_name||' VALUES (NEW.*); '; FOR i IN 1..v_premake LOOP v_prev_partition_id := v_current_partition_id - (v_part_interval * i); v_next_partition_id := v_current_partition_id + (v_part_interval * i); v_final_partition_id := v_next_partition_id + v_part_interval; v_prev_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_prev_partition_id::text, TRUE); v_next_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_next_partition_id::text, TRUE); -- Check that child table exist before making a rule to insert to them. -- Handles edge case of changing premake immediately after running create_parent(). SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname ||'.'||tablename = v_prev_partition_name; IF v_count > 0 THEN -- Only handle previous partitions if they're starting above zero IF v_prev_partition_id >= 0 THEN v_trig_func := v_trig_func ||' ELSIF NEW.'||v_control||' >= '||v_prev_partition_id||' AND NEW.'||v_control||' < '||v_prev_partition_id + v_part_interval|| ' THEN INSERT INTO '||v_prev_partition_name||' VALUES (NEW.*); '; END IF; END IF; SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname ||'.'||tablename = v_next_partition_name; IF v_count > 0 THEN v_trig_func := v_trig_func ||' ELSIF NEW.'||v_control||' >= '||v_next_partition_id||' AND NEW.'||v_control||' < '||v_final_partition_id|| ' THEN INSERT INTO '||v_next_partition_name||' VALUES (NEW.*); '; END IF; END LOOP; v_trig_func := v_trig_func ||' ELSE RETURN NEW; END IF; v_current_partition_id := NEW.'||v_control||' - (NEW.'||v_control||' % '||v_part_interval||'); IF (NEW.'||v_control||' % '||v_part_interval||') > ('||v_part_interval||' / 2) THEN v_id_position := (length(v_last_partition) - position(''p_'' in reverse(v_last_partition))) + 2; v_next_partition_id := (substring(v_last_partition from v_id_position)::bigint) + '||v_part_interval||'; WHILE ((v_next_partition_id - v_current_partition_id) / '||v_part_interval||') <= '||v_premake||' LOOP v_next_partition_name := @extschema@.create_id_partition('||quote_literal(p_parent_table)||', '||quote_literal(v_control)||',' ||v_part_interval||', ARRAY[v_next_partition_id]); UPDATE @extschema@.part_config SET last_partition = v_next_partition_name WHERE parent_table = '||quote_literal(p_parent_table)||'; PERFORM @extschema@.create_id_function('||quote_literal(p_parent_table)||', NEW.'||v_control||'); PERFORM @extschema@.apply_constraints('||quote_literal(p_parent_table)||'); v_next_partition_id := v_next_partition_id + '||v_part_interval||'; END LOOP; END IF; END IF; RETURN NULL; END $t$;'; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Added function for current id interval: '||v_current_partition_id||' to '||v_final_partition_id-1); END IF; ELSIF v_type = 'id-dynamic' THEN -- The return inside the partition creation check is there to keep really high ID values from creating new partitions. v_trig_func := 'CREATE OR REPLACE FUNCTION '||v_function_name||'() RETURNS trigger LANGUAGE plpgsql AS $t$ DECLARE v_count int; v_current_partition_id bigint; v_current_partition_name text; v_id_position int; v_last_partition text := '||quote_literal(v_last_partition)||'; v_last_partition_id bigint; v_next_partition_id bigint; v_next_partition_name text; BEGIN IF TG_OP = ''INSERT'' THEN v_current_partition_id := NEW.'||v_control||' - (NEW.'||v_control||' % '||v_part_interval||'); v_current_partition_name := @extschema@.check_name_length('''||v_parent_tablename||''', '''||v_parent_schema||''', v_current_partition_id::text, TRUE); IF (NEW.'||v_control||' % '||v_part_interval||') > ('||v_part_interval||' / 2) THEN v_id_position := (length(v_last_partition) - position(''p_'' in reverse(v_last_partition))) + 2; v_last_partition_id = substring(v_last_partition from v_id_position)::bigint; v_next_partition_id := v_last_partition_id + '||v_part_interval||'; IF NEW.'||v_control||' >= v_next_partition_id THEN RETURN NEW; END IF; WHILE ((v_next_partition_id - v_current_partition_id) / '||v_part_interval||') <= '||v_premake||' LOOP v_next_partition_name := @extschema@.create_id_partition('||quote_literal(p_parent_table)||', '||quote_literal(v_control)||',' ||quote_literal(v_part_interval)||', ARRAY[v_next_partition_id]); IF v_next_partition_name IS NOT NULL THEN UPDATE @extschema@.part_config SET last_partition = v_next_partition_name WHERE parent_table = '||quote_literal(p_parent_table)||'; PERFORM @extschema@.create_id_function('||quote_literal(p_parent_table)||', NEW.'||v_control||'); PERFORM @extschema@.apply_constraints('||quote_literal(p_parent_table)||'); END IF; v_next_partition_id := v_next_partition_id + '||v_part_interval||'; END LOOP; END IF; SELECT count(*) INTO v_count FROM pg_tables WHERE schemaname ||''.''|| tablename = v_current_partition_name; IF v_count > 0 THEN EXECUTE ''INSERT INTO ''||v_current_partition_name||'' VALUES($1.*)'' USING NEW; ELSE RETURN NEW; END IF; END IF; RETURN NULL; END $t$;'; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Added function for dynamic id table: '||p_parent_table); END IF; ELSE RAISE EXCEPTION 'ERROR: Invalid id partitioning type given: %', v_type; END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN CREATE FUNCTION: '||p_parent_table||')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''Partition function maintenance for table '||p_parent_table||' failed'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to manage pre-creation of the next partitions in a time-based partition set. * Also manages dropping old partitions if the retention option is set. */ CREATE OR REPLACE FUNCTION run_maintenance() RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_create_count int := 0; v_current_partition_timestamp timestamp; v_datetime_string text; v_drop_count int := 0; v_job_id bigint; v_jobmon_schema text; v_last_partition_timestamp timestamp; v_old_search_path text; v_premade_count int; v_quarter text; v_step_id bigint; v_row record; v_time_position int; v_year text; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman run_maintenance')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'Partman maintenance already running.'; RETURN; END IF; SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN RUN MAINTENANCE'); v_step_id := add_step(v_job_id, 'Running maintenance loop'); END IF; FOR v_row IN SELECT parent_table , type , part_interval::interval , control , premake , datetime_string , last_partition , undo_in_progress FROM @extschema@.part_config WHERE type = 'time-static' OR type = 'time-dynamic' LOOP CONTINUE WHEN v_row.undo_in_progress; CASE WHEN v_row.part_interval = '15 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0); WHEN v_row.part_interval = '30 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0); WHEN v_row.part_interval = '1 hour' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP); WHEN v_row.part_interval = '1 day' THEN v_current_partition_timestamp := date_trunc('day', CURRENT_TIMESTAMP); WHEN v_row.part_interval = '1 week' THEN v_current_partition_timestamp := date_trunc('week', CURRENT_TIMESTAMP); WHEN v_row.part_interval = '1 month' THEN v_current_partition_timestamp := date_trunc('month', CURRENT_TIMESTAMP); WHEN v_row.part_interval = '3 months' THEN v_current_partition_timestamp := date_trunc('quarter', CURRENT_TIMESTAMP); WHEN v_row.part_interval = '1 year' THEN v_current_partition_timestamp := date_trunc('year', CURRENT_TIMESTAMP); END CASE; -- Get position of last occurance of '_p' in child partition name v_time_position := (length(v_row.last_partition) - position('p_' in reverse(v_row.last_partition))) + 2; IF v_row.part_interval != '3 months' THEN v_last_partition_timestamp := to_timestamp(substring(v_row.last_partition from v_time_position), v_row.datetime_string); ELSE -- to_timestamp doesn't recognize 'Q' date string formater. Handle it v_year := split_part(substring(v_row.last_partition from v_time_position), 'q', 1); v_quarter := split_part(substring(v_row.last_partition from v_time_position), 'q', 2); CASE WHEN v_quarter = '1' THEN v_last_partition_timestamp := to_timestamp(v_year || '-01-01', 'YYYY-MM-DD'); WHEN v_quarter = '2' THEN v_last_partition_timestamp := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); WHEN v_quarter = '3' THEN v_last_partition_timestamp := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); WHEN v_quarter = '4' THEN v_last_partition_timestamp := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); END CASE; END IF; -- Check and see how many premade partitions there are. v_premade_count = round(EXTRACT('epoch' FROM age(v_last_partition_timestamp, v_current_partition_timestamp)) / EXTRACT('epoch' FROM v_row.part_interval::interval)); -- 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 EXECUTE 'SELECT @extschema@.create_next_time_partition('||quote_literal(v_row.parent_table)||')'; v_create_count := v_create_count + 1; IF v_row.type = 'time-static' THEN EXECUTE 'SELECT @extschema@.create_time_function('||quote_literal(v_row.parent_table)||')'; END IF; -- Manage additonal constraints if set PERFORM @extschema@.apply_constraints(v_row.parent_table); v_last_partition_timestamp := v_last_partition_timestamp + v_row.part_interval; v_premade_count = round(EXTRACT('epoch' FROM age(v_last_partition_timestamp, v_current_partition_timestamp)) / EXTRACT('epoch' FROM v_row.part_interval::interval)); END LOOP; END LOOP; -- end of creation loop -- Manage dropping old partitions if retention option is set FOR v_row IN SELECT parent_table FROM @extschema@.part_config WHERE retention IS NOT NULL AND undo_in_progress = false AND (type = 'time-static' OR type = 'time-dynamic') LOOP v_drop_count := v_drop_count + @extschema@.drop_partition_time(v_row.parent_table); END LOOP; FOR v_row IN SELECT parent_table FROM @extschema@.part_config WHERE retention IS NOT NULL AND undo_in_progress = false AND (type = 'id-static' OR type = 'id-dynamic') LOOP v_drop_count := v_drop_count + @extschema@.drop_partition_id(v_row.parent_table); END LOOP; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Partition maintenance finished. '||v_create_count||' partitons made. '||v_drop_count||' partitions dropped.'); PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN RUN MAINTENANCE'')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to turn a table into the parent of a partition set */ CREATE FUNCTION create_parent( p_parent_table text , p_control text , p_type text , p_interval text , p_constraint_cols text[] DEFAULT NULL , p_premake int DEFAULT 4 , p_debug boolean DEFAULT false) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_current_id bigint; v_datetime_string text; v_id_interval bigint; v_job_id bigint; v_jobmon_schema text; v_last_partition_name text; v_old_search_path text; v_partition_time timestamp[]; v_partition_id bigint[]; v_max bigint; v_notnull boolean; v_starting_partition_id bigint; v_step_id bigint; v_tablename text; v_time_interval interval; v_valid_types text[] := '{"time-static", "time-dynamic", "id-static", "id-dynamic"}'; BEGIN IF position('.' in p_parent_table) = 0 THEN RAISE EXCEPTION 'Parent table must be schema qualified'; END IF; SELECT tablename INTO v_tablename FROM pg_tables WHERE schemaname || '.' || tablename = p_parent_table; IF v_tablename IS NULL THEN RAISE EXCEPTION 'Please create given parent table first: %', p_parent_table; END IF; SELECT attnotnull INTO v_notnull FROM pg_attribute WHERE attrelid = p_parent_table::regclass AND attname = p_control; IF v_notnull = false OR v_notnull IS NULL THEN RAISE EXCEPTION 'Control column (%) for parent table (%) must be NOT NULL', p_control, p_parent_table; END IF; IF NOT (string_to_array(p_type, '') <@ v_valid_types) THEN RAISE EXCEPTION '% is not a valid partitioning type (%)', p_type, array_to_string(v_valid_types, ', '); END IF; EXECUTE 'LOCK TABLE '||p_parent_table||' IN ACCESS EXCLUSIVE MODE'; SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN SETUP PARENT: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating initial partitions on new parent table: '||p_parent_table); END IF; CASE WHEN p_interval = 'yearly' THEN v_time_interval = '1 year'; v_datetime_string := 'YYYY'; WHEN p_interval = 'quarterly' THEN v_time_interval = '3 months'; v_datetime_string = 'YYYY"q"Q'; WHEN p_interval = 'monthly' THEN v_time_interval = '1 month'; v_datetime_string := 'YYYY_MM'; WHEN p_interval = 'weekly' THEN v_time_interval = '1 week'; v_datetime_string := 'IYYY"w"IW'; WHEN p_interval = 'daily' THEN v_time_interval = '1 day'; v_datetime_string := 'YYYY_MM_DD'; WHEN p_interval = 'hourly' THEN v_time_interval = '1 hour'; v_datetime_string := 'YYYY_MM_DD_HH24MI'; WHEN p_interval = 'half-hour' THEN v_time_interval = '30 mins'; v_datetime_string := 'YYYY_MM_DD_HH24MI'; WHEN p_interval = 'quarter-hour' THEN v_time_interval = '15 mins'; v_datetime_string := 'YYYY_MM_DD_HH24MI'; ELSE IF p_type = 'id-static' OR p_type = 'id-dynamic' THEN v_id_interval := p_interval::bigint; IF v_id_interval <= 0 THEN RAISE EXCEPTION 'Interval for serial partitioning must be greater than zero'; END IF; ELSE RAISE EXCEPTION 'Invalid interval for time based partitioning: %', p_interval; END IF; END CASE; IF p_type = 'time-static' OR p_type = 'time-dynamic' THEN FOR i IN 0..p_premake LOOP IF i > 0 THEN -- also create previous partitions equal to premake, but avoid duplicating current v_partition_time := array_append(v_partition_time, quote_literal(CURRENT_TIMESTAMP - (v_time_interval*i))::timestamp); END IF; v_partition_time := array_append(v_partition_time, quote_literal(CURRENT_TIMESTAMP + (v_time_interval*i))::timestamp); END LOOP; INSERT INTO @extschema@.part_config (parent_table, type, part_interval, control, premake, datetime_string) VALUES (p_parent_table, p_type, v_time_interval, p_control, p_premake, v_datetime_string); EXECUTE 'SELECT @extschema@.create_time_partition('||quote_literal(p_parent_table)||','||quote_literal(p_control)||',' ||quote_literal(v_time_interval)||','||quote_literal(v_datetime_string)||','||quote_literal(v_partition_time)||')' INTO v_last_partition_name; -- Doing separate update because create function needs parent table in config table for apply_grants() UPDATE @extschema@.part_config SET last_partition = v_last_partition_name WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Time partitions premade: '||p_premake); END IF; END IF; IF p_type = 'id-static' OR p_type = 'id-dynamic' THEN -- If there is already data, start partitioning with the highest current value EXECUTE 'SELECT COALESCE(max('||p_control||')::bigint, 0) FROM '||p_parent_table||' LIMIT 1' INTO v_max; 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 IF (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_append(v_partition_id, (v_starting_partition_id - v_id_interval*i)); END IF; v_partition_id = array_append(v_partition_id, (v_id_interval*i) + v_starting_partition_id); END LOOP; INSERT INTO @extschema@.part_config (parent_table, type, part_interval, control, premake, constraint_cols) VALUES (p_parent_table, p_type, v_id_interval, p_control, p_premake, p_constraint_cols); EXECUTE 'SELECT @extschema@.create_id_partition('||quote_literal(p_parent_table)||','||quote_literal(p_control)||',' ||v_id_interval||','||quote_literal(v_partition_id)||')' INTO v_last_partition_name; -- Doing separate update because create function needs parent table in config table for apply_grants() UPDATE @extschema@.part_config SET last_partition = v_last_partition_name WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'ID partitions premade: '||p_premake); END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Creating partition function'); END IF; IF p_type = 'time-static' OR p_type = 'time-dynamic' THEN EXECUTE 'SELECT @extschema@.create_time_function('||quote_literal(p_parent_table)||')'; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Time function created'); END IF; ELSIF p_type = 'id-static' OR p_type = 'id-dynamic' THEN v_current_id := COALESCE(v_max, 0); EXECUTE 'SELECT @extschema@.create_id_function('||quote_literal(p_parent_table)||','||v_current_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; EXECUTE 'SELECT @extschema@.create_trigger('||quote_literal(p_parent_table)||')'; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN CREATE PARENT: '||p_parent_table||')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''Partition creation for table '||p_parent_table||' failed'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to list all child partitions in a set. * Will list all child tables in any inheritance set, * not just those managed by pg_partman. */ CREATE FUNCTION show_partitions (p_parent_table text, p_order text DEFAULT 'ASC') RETURNS SETOF text LANGUAGE plpgsql STABLE SECURITY DEFINER AS $$ DECLARE v_datetime_string text; v_part_interval text; v_type text; BEGIN IF p_order NOT IN ('ASC', 'DESC') THEN RAISE EXCEPTION 'p_order paramter must be one of the following values: ASC, DESC'; END IF; SELECT type , part_interval , datetime_string INTO v_type , v_part_interval , v_datetime_string FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_type IN ('time-static', 'time-dynamic') THEN RETURN QUERY EXECUTE ' SELECT n.nspname::text ||''.''|| 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 = '||quote_literal(p_parent_table)||'::regclass ORDER BY to_timestamp(substring(c.relname from ((length(c.relname) - position(''p_'' in reverse(c.relname))) + 2) ), '||quote_literal(v_datetime_string)||') ' || p_order; ELSIF v_type IN ('id-static', 'id-dynamic') THEN RETURN QUERY EXECUTE ' SELECT n.nspname::text ||''.''|| 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 = '||quote_literal(p_parent_table)||'::regclass ORDER BY substring(c.relname from ((length(c.relname) - position(''p_'' in reverse(c.relname))) + 2) )::int ' || p_order; END IF; END $$; SELECT @extschema@.replay_preserved_privs(); DROP FUNCTION @extschema@.replay_preserved_privs(); DROP TABLE partman_preserve_privs_temp; pg_partman-2.2.2/updates/pg_partman--1.5.0--1.5.1.sql000066400000000000000000000213711262146621700214270ustar00rootroot00000000000000-- 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. /* * Function to turn a table into the parent of a partition set */ CREATE OR REPLACE FUNCTION create_parent( p_parent_table text , p_control text , p_type text , p_interval text , p_constraint_cols text[] DEFAULT NULL , p_premake int DEFAULT 4 , p_debug boolean DEFAULT false) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_current_id bigint; v_datetime_string text; v_id_interval bigint; v_job_id bigint; v_jobmon_schema text; v_last_partition_name text; v_old_search_path text; v_partition_time timestamp[]; v_partition_id bigint[]; v_max bigint; v_notnull boolean; v_starting_partition_id bigint; v_step_id bigint; v_tablename text; v_time_interval interval; v_valid_types text[] := '{"time-static", "time-dynamic", "id-static", "id-dynamic"}'; BEGIN IF position('.' in p_parent_table) = 0 THEN RAISE EXCEPTION 'Parent table must be schema qualified'; END IF; SELECT tablename INTO v_tablename FROM pg_tables WHERE schemaname || '.' || tablename = p_parent_table; IF v_tablename IS NULL THEN RAISE EXCEPTION 'Please create given parent table first: %', p_parent_table; END IF; SELECT attnotnull INTO v_notnull FROM pg_attribute WHERE attrelid = p_parent_table::regclass AND attname = p_control; IF v_notnull = false OR v_notnull IS NULL THEN RAISE EXCEPTION 'Control column (%) for parent table (%) must be NOT NULL', p_control, p_parent_table; END IF; IF NOT (string_to_array(p_type, '') <@ v_valid_types) THEN RAISE EXCEPTION '% is not a valid partitioning type (%)', p_type, array_to_string(v_valid_types, ', '); END IF; EXECUTE 'LOCK TABLE '||p_parent_table||' IN ACCESS EXCLUSIVE MODE'; SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN SETUP PARENT: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating initial partitions on new parent table: '||p_parent_table); END IF; CASE WHEN p_interval = 'yearly' THEN v_time_interval = '1 year'; v_datetime_string := 'YYYY'; WHEN p_interval = 'quarterly' THEN v_time_interval = '3 months'; v_datetime_string = 'YYYY"q"Q'; WHEN p_interval = 'monthly' THEN v_time_interval = '1 month'; v_datetime_string := 'YYYY_MM'; WHEN p_interval = 'weekly' THEN v_time_interval = '1 week'; v_datetime_string := 'IYYY"w"IW'; WHEN p_interval = 'daily' THEN v_time_interval = '1 day'; v_datetime_string := 'YYYY_MM_DD'; WHEN p_interval = 'hourly' THEN v_time_interval = '1 hour'; v_datetime_string := 'YYYY_MM_DD_HH24MI'; WHEN p_interval = 'half-hour' THEN v_time_interval = '30 mins'; v_datetime_string := 'YYYY_MM_DD_HH24MI'; WHEN p_interval = 'quarter-hour' THEN v_time_interval = '15 mins'; v_datetime_string := 'YYYY_MM_DD_HH24MI'; ELSE IF p_type = 'id-static' OR p_type = 'id-dynamic' THEN v_id_interval := p_interval::bigint; IF v_id_interval <= 0 THEN RAISE EXCEPTION 'Interval for serial partitioning must be greater than zero'; END IF; ELSE RAISE EXCEPTION 'Invalid interval for time based partitioning: %', p_interval; END IF; END CASE; IF p_type = 'time-static' OR p_type = 'time-dynamic' THEN FOR i IN 0..p_premake LOOP IF i > 0 THEN -- also create previous partitions equal to premake, but avoid duplicating current v_partition_time := array_append(v_partition_time, quote_literal(CURRENT_TIMESTAMP - (v_time_interval*i))::timestamp); END IF; v_partition_time := array_append(v_partition_time, quote_literal(CURRENT_TIMESTAMP + (v_time_interval*i))::timestamp); END LOOP; INSERT INTO @extschema@.part_config (parent_table, type, part_interval, control, premake, constraint_cols, datetime_string) VALUES (p_parent_table, p_type, v_time_interval, p_control, p_premake, p_constraint_cols, v_datetime_string); EXECUTE 'SELECT @extschema@.create_time_partition('||quote_literal(p_parent_table)||','||quote_literal(p_control)||',' ||quote_literal(v_time_interval)||','||quote_literal(v_datetime_string)||','||quote_literal(v_partition_time)||')' INTO v_last_partition_name; -- Doing separate update because create function needs parent table in config table for apply_grants() UPDATE @extschema@.part_config SET last_partition = v_last_partition_name WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Time partitions premade: '||p_premake); END IF; END IF; IF p_type = 'id-static' OR p_type = 'id-dynamic' THEN -- If there is already data, start partitioning with the highest current value EXECUTE 'SELECT COALESCE(max('||p_control||')::bigint, 0) FROM '||p_parent_table||' LIMIT 1' INTO v_max; 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 IF (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_append(v_partition_id, (v_starting_partition_id - v_id_interval*i)); END IF; v_partition_id = array_append(v_partition_id, (v_id_interval*i) + v_starting_partition_id); END LOOP; INSERT INTO @extschema@.part_config (parent_table, type, part_interval, control, premake, constraint_cols) VALUES (p_parent_table, p_type, v_id_interval, p_control, p_premake, p_constraint_cols); EXECUTE 'SELECT @extschema@.create_id_partition('||quote_literal(p_parent_table)||','||quote_literal(p_control)||',' ||v_id_interval||','||quote_literal(v_partition_id)||')' INTO v_last_partition_name; -- Doing separate update because create function needs parent table in config table for apply_grants() UPDATE @extschema@.part_config SET last_partition = v_last_partition_name WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'ID partitions premade: '||p_premake); END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Creating partition function'); END IF; IF p_type = 'time-static' OR p_type = 'time-dynamic' THEN EXECUTE 'SELECT @extschema@.create_time_function('||quote_literal(p_parent_table)||')'; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Time function created'); END IF; ELSIF p_type = 'id-static' OR p_type = 'id-dynamic' THEN v_current_id := COALESCE(v_max, 0); EXECUTE 'SELECT @extschema@.create_id_function('||quote_literal(p_parent_table)||','||v_current_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; EXECUTE 'SELECT @extschema@.create_trigger('||quote_literal(p_parent_table)||')'; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN CREATE PARENT: '||p_parent_table||')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''Partition creation for table '||p_parent_table||' failed'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; pg_partman-2.2.2/updates/pg_partman--1.5.1--1.6.0.sql000066400000000000000000004016651262146621700214400ustar00rootroot00000000000000-- 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. ALTER TABLE @extschema@.part_config ADD jobmon boolean DEFAULT true; CREATE TEMP TABLE partman_preserve_privs_temp (statement text); INSERT INTO partman_preserve_privs_temp SELECT 'GRANT EXECUTE ON FUNCTION @extschema@.create_parent(text, text, text, text, text[], int, text, boolean, boolean) TO '||array_to_string(array_agg(grantee::text), ',')||';' FROM information_schema.routine_privileges WHERE routine_schema = '@extschema@' AND routine_name = 'create_parent'; INSERT INTO partman_preserve_privs_temp SELECT 'GRANT EXECUTE ON FUNCTION @extschema@.create_time_partition (text, timestamp[]) TO '||array_to_string(array_agg(grantee::text), ',')||';' FROM information_schema.routine_privileges WHERE routine_schema = '@extschema@' AND routine_name = 'create_time_partition'; INSERT INTO partman_preserve_privs_temp SELECT 'GRANT EXECUTE ON FUNCTION @extschema@.create_id_partition (text, bigint[]) TO '||array_to_string(array_agg(grantee::text), ',')||';' FROM information_schema.routine_privileges WHERE routine_schema = '@extschema@' AND routine_name = 'create_id_partition'; INSERT INTO partman_preserve_privs_temp SELECT 'GRANT EXECUTE ON FUNCTION @extschema@.undo_partition(text, int, boolean, boolean) TO '||array_to_string(array_agg(grantee::text), ',')||';' FROM information_schema.routine_privileges WHERE routine_schema = '@extschema@' AND routine_name = 'undo_partition'; INSERT INTO partman_preserve_privs_temp SELECT 'GRANT EXECUTE ON FUNCTION @extschema@.create_id_function(text) TO '||array_to_string(array_agg(grantee::text), ',')||';' FROM information_schema.routine_privileges WHERE routine_schema = '@extschema@' AND routine_name = 'create_id_function'; INSERT INTO partman_preserve_privs_temp SELECT 'GRANT EXECUTE ON FUNCTION @extschema@.run_maintenance(boolean) TO '||array_to_string(array_agg(grantee::text), ',')||';' FROM information_schema.routine_privileges WHERE routine_schema = '@extschema@' AND routine_name = 'run_maintenance'; DROP FUNCTION @extschema@.create_parent(text, text, text, text, text[], int , boolean); DROP FUNCTION @extschema@.create_time_partition (text, text, interval, text, timestamp[]); DROP FUNCTION @extschema@.create_id_partition (text, text, bigint, bigint[]); DROP FUNCTION @extschema@.undo_partition(text, int, boolean); DROP FUNCTION @extschema@.create_id_function(text, bigint); DROP FUNCTION @extschema@.run_maintenance(); DROP FUNCTION @extschema@.create_next_time_partition(text); /* * Check PostgreSQL version number. Parameter must be full 3 point version. * Returns true if current version is greater than or equal to the parameter given. */ CREATE FUNCTION check_version(p_check_version text) RETURNS boolean LANGUAGE plpgsql STABLE AS $$ DECLARE v_check_version text[]; v_current_version text[] := string_to_array(current_setting('server_version'), '.'); BEGIN v_check_version := string_to_array(p_check_version, '.'); IF v_current_version[1]::int > v_check_version[1]::int THEN RETURN true; END IF; IF v_current_version[1]::int = v_check_version[1]::int THEN IF v_current_version[2]::int > v_check_version[2]::int THEN RETURN true; END IF; IF v_current_version[2]::int = v_check_version[2]::int THEN IF v_current_version[3]::int >= v_check_version[3]::int THEN RETURN true; END IF; -- 0.0.x END IF; -- 0.x.0 END IF; -- x.0.0 RETURN false; END $$; /********* 9.2+ stuff ********/ DO $$ BEGIN IF @extschema@.check_version('9.2.0') THEN CREATE TABLE 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 custom_time_partitions USING gist (partition_range); END IF; END $$; /********* end 9.2+ stuff ********/ /* * Check function for config table partition types */ CREATE OR REPLACE FUNCTION check_partition_type (p_type text) RETURNS boolean LANGUAGE plpgsql IMMUTABLE SECURITY DEFINER AS $$ DECLARE v_result boolean; BEGIN SELECT p_type IN ('time-static', 'time-dynamic', 'time-custom', 'id-static', 'id-dynamic') INTO v_result; RETURN v_result; END $$; /* * Function to turn a table into the parent of a partition set */ CREATE FUNCTION create_parent( p_parent_table text , p_control text , p_type text , p_interval text , p_constraint_cols text[] DEFAULT NULL , p_premake int DEFAULT 4 , p_start_partition text DEFAULT NULL , p_jobmon boolean DEFAULT true , p_debug boolean DEFAULT false) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_base_timestamp timestamp; v_count int := 1; v_datetime_string text; v_id_interval bigint; v_job_id bigint; v_jobmon_schema text; v_last_partition_name text; v_old_search_path text; v_partition_time timestamp; v_partition_time_array timestamp[]; v_partition_id bigint[]; v_max bigint; v_notnull boolean; v_start_time timestamp; v_starting_partition_id bigint; v_step_id bigint; v_step_overflow_id bigint; v_tablename text; v_time_interval interval; BEGIN IF position('.' in p_parent_table) = 0 THEN RAISE EXCEPTION 'Parent table must be schema qualified'; END IF; SELECT tablename INTO v_tablename FROM pg_tables WHERE schemaname || '.' || tablename = p_parent_table; IF v_tablename IS NULL THEN RAISE EXCEPTION 'Please create given parent table first: %', p_parent_table; END IF; SELECT attnotnull INTO v_notnull FROM pg_attribute WHERE attrelid = p_parent_table::regclass AND attname = p_control; IF v_notnull = false OR v_notnull IS NULL THEN RAISE EXCEPTION 'Control column (%) for parent table (%) must be NOT NULL', p_control, p_parent_table; END IF; IF NOT @extschema@.check_partition_type(p_type) THEN RAISE EXCEPTION '% is not a valid partitioning type', p_type; END IF; IF p_type = 'time-custom' AND @extschema@.check_version('9.2.0') IS FALSE THEN RAISE EXCEPTION 'The "time-custom" type requires a minimum PostgreSQL version of 9.2.0'; END IF; EXECUTE 'LOCK TABLE '||p_parent_table||' IN ACCESS EXCLUSIVE MODE'; IF p_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN SETUP PARENT: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating initial partitions on new parent table: '||p_parent_table); END IF; IF p_type = 'time-static' OR p_type = 'time-dynamic' OR p_type = 'time-custom' 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 <> 'time-custom' THEN RAISE EXCEPTION 'Must use a predefined time interval if not using type "time-custom". See documentation.'; 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; -- First partition is either the min premake or p_start_partition v_start_time := COALESCE(p_start_partition::timestamp, CURRENT_TIMESTAMP - (v_time_interval * p_premake)); 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 v_datetime_string := 'YYYY'; 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 v_partition_time_array := array_append(v_partition_time_array, v_base_timestamp); LOOP -- If current loop value is less than or equal to the value of the max premake, add time to array. IF (v_base_timestamp + (v_time_interval * v_count)) < (CURRENT_TIMESTAMP + (v_time_interval * p_premake)) THEN BEGIN v_partition_time := (v_base_timestamp + (v_time_interval * v_count))::timestamp; 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. 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'); CONTINUE; END; ELSE EXIT; -- all needed partitions added to array. Exit the loop. END IF; v_count := v_count + 1; END LOOP; INSERT INTO @extschema@.part_config (parent_table, type, part_interval, control, premake, constraint_cols, datetime_string, jobmon) VALUES (p_parent_table, p_type, v_time_interval, p_control, p_premake, p_constraint_cols, v_datetime_string, p_jobmon); v_last_partition_name := @extschema@.create_time_partition(p_parent_table, v_partition_time_array); -- Doing separate update because create function requires in config table last_partition to be set UPDATE @extschema@.part_config SET last_partition = v_last_partition_name WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Time partitions premade: '||p_premake); END IF; END IF; IF p_type = 'id-static' OR p_type = 'id-dynamic' THEN v_id_interval := p_interval::bigint; IF v_id_interval <= 1 THEN RAISE EXCEPTION 'Interval for serial partitioning must be greater than 1'; END IF; -- If custom start partition is set, use that. -- If custom start is not set and there is already data, start partitioning with the highest current value EXECUTE 'SELECT COALESCE('||quote_nullable(p_start_partition::bigint)||', max('||p_control||')::bigint, 0) FROM '||p_parent_table||' LIMIT 1' INTO v_max; 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 THEN v_partition_id = array_append(v_partition_id, (v_starting_partition_id - v_id_interval*i)); END IF; v_partition_id = array_append(v_partition_id, (v_id_interval*i) + v_starting_partition_id); END LOOP; INSERT INTO @extschema@.part_config (parent_table, type, part_interval, control, premake, constraint_cols, jobmon) VALUES (p_parent_table, p_type, v_id_interval, p_control, p_premake, p_constraint_cols, p_jobmon); v_last_partition_name := @extschema@.create_id_partition(p_parent_table, v_partition_id); -- Doing separate update because create function needs parent table in config table for apply_grants() UPDATE @extschema@.part_config SET last_partition = v_last_partition_name WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'ID partitions premade: '||p_premake); END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Creating partition function'); END IF; IF p_type = 'time-static' OR p_type = 'time-dynamic' OR p_type = 'time-custom' THEN PERFORM @extschema@.create_time_function(p_parent_table); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Time function created'); END IF; ELSIF p_type = 'id-static' OR p_type = 'id-dynamic' THEN PERFORM @extschema@.create_id_function(p_parent_table); 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); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); IF v_step_overflow_id IS NOT NULL THEN PERFORM fail_job(v_job_id); ELSE PERFORM close_job(v_job_id); END IF; EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN CREATE PARENT: '||p_parent_table||')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''Partition creation for table '||p_parent_table||' failed'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to create a child table in a time-based partition set */ CREATE FUNCTION create_time_partition (p_parent_table text, p_partition_times timestamp[]) RETURNS text LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_all text[] := ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER']; v_control text; v_grantees text[]; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_parent_grant record; v_parent_owner text; v_parent_schema text; v_parent_tablename text; v_partition_name text; v_partition_suffix text; v_parent_tablespace text; v_part_interval interval; v_partition_timestamp_end timestamp; v_partition_timestamp_start timestamp; v_quarter text; v_revoke text[]; v_step_id bigint; v_step_overflow_id bigint; v_tablename text; v_trunc_value text; v_time timestamp; v_type text; v_year text; BEGIN SELECT type , control , part_interval , jobmon INTO v_type , v_control , v_part_interval , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic' OR type = 'time-custom'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; SELECT tableowner, schemaname, tablename, tablespace INTO v_parent_owner, v_parent_schema, v_parent_tablename, v_parent_tablespace FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; FOREACH v_time IN ARRAY p_partition_times LOOP v_partition_suffix := to_char(v_time, 'YYYY'); IF v_part_interval < '1 year' AND v_part_interval <> '1 week' THEN v_partition_suffix := v_partition_suffix ||'_'|| to_char(v_time, 'MM'); IF v_part_interval < '1 month' AND v_part_interval <> '1 week' THEN v_partition_suffix := v_partition_suffix ||'_'|| to_char(v_time, 'DD'); IF v_part_interval < '1 day' THEN v_partition_suffix := v_partition_suffix || '_' || to_char(v_time, 'HH24MI'); IF v_part_interval < '1 minute' THEN v_partition_suffix := v_partition_suffix || to_char(v_time, 'SS'); END IF; -- end < minute IF END IF; -- end < day IF END IF; -- end < month IF END IF; -- end < year IF v_partition_timestamp_start := v_time; BEGIN v_partition_timestamp_end := v_time + v_part_interval; EXCEPTION WHEN datetime_field_overflow THEN 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'); CONTINUE; END; IF v_part_interval = '1 week' THEN v_partition_suffix := to_char(v_time, 'IYYY') || 'w' || to_char(v_time, 'IW'); END IF; -- "Q" is ignored in to_timestamp, so handle special case IF v_part_interval = '3 months' AND (v_type = 'time-static' OR v_type = 'time-dynamic') THEN v_year := to_char(v_time, 'YYYY'); v_quarter := to_char(v_time, 'Q'); v_partition_suffix := v_year || 'q' || v_quarter; END IF; v_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_partition_suffix, TRUE); SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_tablename IS NOT NULL THEN CONTINUE; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating new partition '||v_partition_name||' with interval from '||v_partition_timestamp_start||' to '||(v_partition_timestamp_end-'1sec'::interval)); END IF; EXECUTE 'CREATE TABLE '||v_partition_name||' (LIKE '||p_parent_table||' INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING STORAGE INCLUDING COMMENTS)'; SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_parent_tablespace IS NOT NULL THEN EXECUTE 'ALTER TABLE '||v_partition_name||' SET TABLESPACE '||v_parent_tablespace; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' ADD CONSTRAINT '||v_tablename||'_partition_check CHECK ('||v_control||'>='||quote_literal(v_partition_timestamp_start)||' AND '||v_control||'<'||quote_literal(v_partition_timestamp_end)||')'; EXECUTE 'ALTER TABLE '||v_partition_name||' INHERIT '||p_parent_table; -- If custom time, set extra config options. IF v_type = 'time-custom' THEN INSERT INTO @extschema@.custom_time_partitions (parent_table, child_table, partition_range) VALUES ( p_parent_table, v_partition_name, tstzrange(v_partition_timestamp_start, v_partition_timestamp_end, '[)') ); END IF; FOR v_parent_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = p_parent_table GROUP BY grantee LOOP EXECUTE 'GRANT '||array_to_string(v_parent_grant.types, ',')||' ON '||v_partition_name||' TO '||v_parent_grant.grantee; SELECT array_agg(r) INTO v_revoke FROM (SELECT unnest(v_all) AS r EXCEPT SELECT unnest(v_parent_grant.types)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE '||array_to_string(v_revoke, ',')||' ON '||v_partition_name||' FROM '||v_parent_grant.grantee||' CASCADE'; END IF; v_grantees := array_append(v_grantees, v_parent_grant.grantee::text); END LOOP; -- Revoke all privileges from roles that have none on the parent IF v_grantees IS NOT NULL THEN SELECT array_agg(r) INTO v_revoke FROM ( SELECT DISTINCT grantee::text AS r FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = v_partition_name EXCEPT SELECT unnest(v_grantees)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE ALL ON '||v_partition_name||' FROM '||array_to_string(v_revoke, ','); END IF; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' OWNER TO '||v_parent_owner; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); IF v_step_overflow_id IS NOT NULL THEN PERFORM fail_job(v_job_id); ELSE PERFORM close_job(v_job_id); END IF; END IF; END LOOP; IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_partition_name; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN CREATE TABLE: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Create the trigger function for the parent table of a time-based partition set */ CREATE OR REPLACE FUNCTION create_time_function(p_parent_table text) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_control text; v_count int; v_current_partition_name text; v_current_partition_timestamp timestamptz; v_datetime_string text; v_final_partition_timestamp timestamptz; v_function_name text; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_new_length int; v_next_partition_name text; v_next_partition_timestamp timestamptz; v_parent_schema text; v_parent_tablename text; v_part_interval interval; v_premake int; v_prev_partition_name text; v_prev_partition_timestamp timestamptz; v_step_id bigint; v_trig_func text; v_type text; BEGIN SELECT type , part_interval::interval , control , premake , datetime_string , jobmon INTO v_type , v_part_interval , v_control , v_premake , v_datetime_string , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic' OR type = 'time-custom'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE FUNCTION: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating partition function for table '||p_parent_table); END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; v_function_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, '_part_trig_func', FALSE); IF v_type = 'time-static' THEN CASE WHEN v_part_interval = '15 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0); WHEN v_part_interval = '30 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0); WHEN v_part_interval = '1 hour' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP); WHEN v_part_interval = '1 day' THEN v_current_partition_timestamp := date_trunc('day', CURRENT_TIMESTAMP); WHEN v_part_interval = '1 week' THEN v_current_partition_timestamp := date_trunc('week', CURRENT_TIMESTAMP); WHEN v_part_interval = '1 month' THEN v_current_partition_timestamp := date_trunc('month', CURRENT_TIMESTAMP); -- Type time-static plus this interval is the special quarterly interval WHEN v_part_interval = '3 months' THEN v_current_partition_timestamp := date_trunc('quarter', CURRENT_TIMESTAMP); WHEN v_part_interval = '1 year' THEN v_current_partition_timestamp := date_trunc('year', CURRENT_TIMESTAMP); END CASE; v_current_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, to_char(v_current_partition_timestamp, v_datetime_string), TRUE); v_next_partition_timestamp := v_current_partition_timestamp + v_part_interval::interval; v_trig_func := 'CREATE OR REPLACE FUNCTION '||v_function_name||'() RETURNS trigger LANGUAGE plpgsql AS $t$ BEGIN IF TG_OP = ''INSERT'' THEN IF NEW.'||v_control||' >= '||quote_literal(v_current_partition_timestamp)||' AND NEW.'||v_control||' < '||quote_literal(v_next_partition_timestamp)|| ' THEN INSERT INTO '||v_current_partition_name||' VALUES (NEW.*); '; FOR i IN 1..v_premake LOOP v_prev_partition_timestamp := v_current_partition_timestamp - (v_part_interval::interval * i); v_next_partition_timestamp := v_current_partition_timestamp + (v_part_interval::interval * i); v_final_partition_timestamp := v_next_partition_timestamp + (v_part_interval::interval); v_prev_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, to_char(v_prev_partition_timestamp, v_datetime_string), TRUE); v_next_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, to_char(v_next_partition_timestamp, v_datetime_string), TRUE); -- Check that child table exist before making a rule to insert to them. -- Handles edge case of changing premake immediately after running create_parent(). SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname ||'.'||tablename = v_prev_partition_name; IF v_count > 0 THEN v_trig_func := v_trig_func ||' ELSIF NEW.'||v_control||' >= '||quote_literal(v_prev_partition_timestamp)||' AND NEW.'||v_control||' < '|| quote_literal(v_prev_partition_timestamp + v_part_interval::interval)|| ' THEN INSERT INTO '||v_prev_partition_name||' VALUES (NEW.*);'; END IF; SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname ||'.'||tablename = v_next_partition_name; IF v_count > 0 THEN v_trig_func := v_trig_func ||' ELSIF NEW.'||v_control||' >= '||quote_literal(v_next_partition_timestamp)||' AND NEW.'||v_control||' < '|| quote_literal(v_final_partition_timestamp)|| ' THEN INSERT INTO '||v_next_partition_name||' VALUES (NEW.*);'; END IF; END LOOP; v_trig_func := v_trig_func ||' ELSE RETURN NEW; END IF; END IF; RETURN NULL; END $t$;'; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Added function for current time interval: '|| v_current_partition_timestamp||' to '||(v_final_partition_timestamp-'1sec'::interval)); END IF; ELSIF v_type = 'time-dynamic' THEN v_trig_func := 'CREATE OR REPLACE FUNCTION '||v_function_name||'() RETURNS trigger LANGUAGE plpgsql AS $t$ DECLARE v_count int; v_partition_name text; v_partition_timestamp timestamptz; BEGIN IF TG_OP = ''INSERT'' THEN '; CASE WHEN v_part_interval = '15 mins' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''hour'', NEW.'||v_control||') + ''15min''::interval * floor(date_part(''minute'', NEW.'||v_control||') / 15.0);'; WHEN v_part_interval = '30 mins' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''hour'', NEW.'||v_control||') + ''30min''::interval * floor(date_part(''minute'', NEW.'||v_control||') / 30.0);'; WHEN v_part_interval = '1 hour' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''hour'', NEW.'||v_control||');'; WHEN v_part_interval = '1 day' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''day'', NEW.'||v_control||');'; WHEN v_part_interval = '1 week' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''week'', NEW.'||v_control||');'; WHEN v_part_interval = '1 month' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''month'', NEW.'||v_control||');'; WHEN v_part_interval = '3 months' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''quarter'', NEW.'||v_control||');'; WHEN v_part_interval = '1 year' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''year'', NEW.'||v_control||');'; END CASE; v_trig_func := v_trig_func||' v_partition_name := @extschema@.check_name_length('''||v_parent_tablename||''', '''||v_parent_schema||''', to_char(v_partition_timestamp, '||quote_literal(v_datetime_string)||'), TRUE); SELECT count(*) INTO v_count FROM pg_tables WHERE schemaname ||''.''|| tablename = v_partition_name; IF v_count > 0 THEN EXECUTE ''INSERT INTO ''||v_partition_name||'' VALUES($1.*)'' USING NEW; ELSE RETURN NEW; END IF; END IF; RETURN NULL; END $t$;'; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Added function for dynamic time table: '||p_parent_table); END IF; ELSIF v_type = 'time-custom' THEN v_trig_func := 'CREATE OR REPLACE FUNCTION '||v_function_name||'() RETURNS trigger LANGUAGE plpgsql AS $t$ DECLARE v_child_table text; v_count int; BEGIN SELECT child_table INTO v_child_table FROM @extschema@.custom_time_partitions WHERE partition_range @> NEW.'||v_control||' AND parent_table = '||quote_literal(p_parent_table)||'; SELECT count(*) INTO v_count FROM pg_tables WHERE schemaname ||''.''|| tablename = v_child_table; IF v_count > 0 THEN EXECUTE ''INSERT INTO ''||v_child_table||'' VALUES ($1.*)'' USING NEW; ELSE RETURN NEW; END IF; RETURN NULL; END $t$;'; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Added function for custom time table: '||p_parent_table); END IF; ELSE RAISE EXCEPTION 'ERROR: Invalid time partitioning type given: %', v_type; END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN CREATE FUNCTION: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Populate the child table(s) of a time-based partition set with old data from the original parent */ CREATE OR REPLACE FUNCTION 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) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_control text; v_datetime_string text; v_last_partition_name text; v_max_partition_timestamp timestamp; v_last_partition text; v_lock_iter int := 1; v_lock_obtained boolean := FALSE; v_min_control timestamp; v_min_partition_timestamp timestamp; v_part_interval interval; v_partition_timestamp timestamp[]; v_rowcount bigint; v_sql text; v_time_position int; v_total_rows bigint := 0; v_type text; BEGIN SELECT type , part_interval::interval , control , datetime_string , last_partition INTO v_type , v_part_interval , v_control , v_datetime_string , v_last_partition FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic' OR type = 'time-custom'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF p_batch_interval IS NULL OR p_batch_interval > v_part_interval THEN p_batch_interval := v_part_interval; END IF; FOR i IN 1..p_batch_count LOOP EXECUTE 'SELECT min('||v_control||') FROM ONLY '||p_parent_table INTO v_min_control; IF v_min_control IS NULL THEN RETURN 0; END IF; IF v_type = 'time-static' OR v_type = 'time-dynamic' THEN CASE WHEN v_part_interval = '15 mins' THEN v_min_partition_timestamp := date_trunc('hour', v_min_control) + '15min'::interval * floor(date_part('minute', v_min_control) / 15.0); WHEN v_part_interval = '30 mins' THEN v_min_partition_timestamp := date_trunc('hour', v_min_control) + '30min'::interval * floor(date_part('minute', v_min_control) / 30.0); WHEN v_part_interval = '1 hour' THEN v_min_partition_timestamp := date_trunc('hour', v_min_control); WHEN v_part_interval = '1 day' THEN v_min_partition_timestamp := date_trunc('day', v_min_control); WHEN v_part_interval = '1 week' THEN v_min_partition_timestamp := date_trunc('week', v_min_control); WHEN v_part_interval = '1 month' THEN v_min_partition_timestamp := date_trunc('month', v_min_control); WHEN v_part_interval = '3 months' THEN v_min_partition_timestamp := date_trunc('quarter', v_min_control); WHEN v_part_interval = '1 year' THEN v_min_partition_timestamp := date_trunc('year', v_min_control); END CASE; ELSIF v_type = 'time-custom' THEN -- Keep going backwards, checking if the time interval encompases the current v_min_control value v_time_position := (length(v_last_partition) - position('p_' in reverse(v_last_partition))) + 2; v_min_partition_timestamp := to_timestamp(substring(v_last_partition from v_time_position), v_datetime_string); v_max_partition_timestamp := v_min_partition_timestamp + v_part_interval; LOOP IF v_min_control >= v_min_partition_timestamp AND v_min_control < v_max_partition_timestamp THEN EXIT; ELSE v_max_partition_timestamp := v_min_partition_timestamp; BEGIN v_min_partition_timestamp := v_min_partition_timestamp - v_part_interval; 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_interval; END; END IF; END LOOP; END IF; v_partition_timestamp := ARRAY[v_min_partition_timestamp]; IF (v_min_control + p_batch_interval) >= (v_min_partition_timestamp + v_part_interval) THEN v_max_partition_timestamp := v_min_partition_timestamp + v_part_interval; ELSE v_max_partition_timestamp := v_min_control + p_batch_interval; END IF; -- do some locking with timeout, if required IF p_lock_wait > 0 THEN v_lock_iter := 0; WHILE v_lock_iter <= 5 LOOP v_lock_iter := v_lock_iter + 1; BEGIN v_sql := 'SELECT * FROM ONLY ' || p_parent_table || ' WHERE '||v_control||' >= '||quote_literal(v_min_control)|| ' AND '||v_control||' < '||quote_literal(v_max_partition_timestamp) ||' FOR UPDATE NOWAIT'; EXECUTE v_sql; 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 RETURN -1; END IF; END IF; v_last_partition_name := @extschema@.create_time_partition(p_parent_table, v_partition_timestamp); EXECUTE 'WITH partition_data AS ( DELETE FROM ONLY '||p_parent_table||' WHERE '||v_control||' >= '||quote_literal(v_min_control)|| ' AND '||v_control||' < '||quote_literal(v_max_partition_timestamp)||' RETURNING *) INSERT INTO '||v_last_partition_name||' SELECT * FROM partition_data'; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total_rows := v_total_rows + v_rowcount; IF v_rowcount = 0 THEN EXIT; END IF; END LOOP; IF v_type = 'time-static' THEN PERFORM @extschema@.create_time_function(p_parent_table); END IF; RETURN v_total_rows; END $$; /* * Function to manage pre-creation of the next partitions in a time-based partition set. * Also manages dropping old partitions if the retention option is set. */ CREATE FUNCTION run_maintenance(p_jobmon BOOLEAN DEFAULT true) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_create_count int := 0; v_current_partition text; v_current_partition_timestamp timestamp; v_datetime_string text; v_drop_count int := 0; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_last_partition text; v_last_partition_timestamp timestamp; v_next_partition_timestamp timestamp; v_old_search_path text; v_premade_count int; v_quarter text; v_step_id bigint; v_step_overflow_id bigint; v_row record; v_tablename text; v_time_position int; v_year text; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman run_maintenance')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'Partman maintenance already running.'; RETURN; END IF; IF p_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN RUN MAINTENANCE'); v_step_id := add_step(v_job_id, 'Running maintenance loop'); END IF; FOR v_row IN SELECT parent_table , type , part_interval::interval , control , premake , datetime_string , last_partition , undo_in_progress FROM @extschema@.part_config WHERE type = 'time-static' OR type = 'time-dynamic' OR type = 'time-custom' LOOP CONTINUE WHEN v_row.undo_in_progress; -- Double check that last created partition exists IF v_row.last_partition IS NOT NULL THEN SELECT tablename INTO v_tablename FROM pg_tables WHERE schemaname || '.' || tablename = v_row.last_partition; IF v_tablename IS NULL THEN RAISE EXCEPTION 'ERROR: Last known partition table missing for parent table %. Unable to determine next partition in sequence.', v_row.parent_table; END IF; ELSE RAISE EXCEPTION 'ERROR: Last known partition missing from config table for parent table %.', p_parent_table; END IF; IF v_row.type = 'time-static' OR v_row.type = 'time-dynamic' THEN CASE WHEN v_row.part_interval = '15 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0); WHEN v_row.part_interval = '30 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0); WHEN v_row.part_interval = '1 hour' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP); WHEN v_row.part_interval = '1 day' THEN v_current_partition_timestamp := date_trunc('day', CURRENT_TIMESTAMP); WHEN v_row.part_interval = '1 week' THEN v_current_partition_timestamp := date_trunc('week', CURRENT_TIMESTAMP); WHEN v_row.part_interval = '1 month' THEN v_current_partition_timestamp := date_trunc('month', CURRENT_TIMESTAMP); WHEN v_row.part_interval = '3 months' THEN v_current_partition_timestamp := date_trunc('quarter', CURRENT_TIMESTAMP); WHEN v_row.part_interval = '1 year' THEN v_current_partition_timestamp := date_trunc('year', CURRENT_TIMESTAMP); END CASE; ELSIF v_row.type = 'time-custom' THEN SELECT child_table INTO v_current_partition FROM @extschema@.custom_time_partitions WHERE parent_table = v_row.parent_table AND partition_range @> CURRENT_TIMESTAMP; IF v_current_partition IS NULL THEN RAISE EXCEPTION 'Current time partition missing from custom_time_partitions config table for table % and timestamp %', CURRENT_TIMESTAMP, v_row.parent_table; END IF; v_time_position := (length(v_current_partition) - position('p_' in reverse(v_current_partition))) + 2; v_current_partition_timestamp := to_timestamp(substring(v_current_partition from v_time_position), v_row.datetime_string); END IF; v_time_position := (length(v_row.last_partition) - position('p_' in reverse(v_row.last_partition))) + 2; IF v_row.part_interval <> '3 months' OR (v_row.part_interval = '3 months' AND v_row.type = 'time-custom') THEN v_last_partition_timestamp := to_timestamp(substring(v_row.last_partition from v_time_position), v_row.datetime_string); ELSE -- to_timestamp doesn't recognize 'Q' date string formater. Handle it v_year := split_part(substring(v_row.last_partition from v_time_position), 'q', 1); v_quarter := split_part(substring(v_row.last_partition from v_time_position), 'q', 2); CASE WHEN v_quarter = '1' THEN v_last_partition_timestamp := to_timestamp(v_year || '-01-01', 'YYYY-MM-DD'); WHEN v_quarter = '2' THEN v_last_partition_timestamp := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); WHEN v_quarter = '3' THEN v_last_partition_timestamp := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); WHEN v_quarter = '4' THEN v_last_partition_timestamp := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); END CASE; END IF; -- Check and see how many premade partitions there are. v_premade_count = round(EXTRACT('epoch' FROM age(v_last_partition_timestamp, v_current_partition_timestamp)) / EXTRACT('epoch' FROM v_row.part_interval::interval)); v_next_partition_timestamp := v_last_partition_timestamp; -- 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 BEGIN v_next_partition_timestamp := v_next_partition_timestamp + v_row.part_interval; EXCEPTION WHEN datetime_field_overflow THEN RAISE WARNING 'Attempted partition time interval is outside PostgreSQL''s supported time range. Child partition creation skipped for parent table %', v_row.parent_table; v_premade_count = v_row.premake; -- do this so it can exit the premake check loop and continue in the outer for loop 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 skippd for parent table '||v_partition_time); CONTINUE; END; v_last_partition := @extschema@.create_time_partition(v_row.parent_table, ARRAY[v_next_partition_timestamp]); IF v_last_partition IS NOT NULL THEN UPDATE @extschema@.part_config SET last_partition = v_last_partition WHERE parent_table = v_row.parent_table; END IF; v_create_count := v_create_count + 1; IF v_row.type = 'time-static' THEN PERFORM @extschema@.create_time_function(v_row.parent_table); END IF; -- Manage additonal constraints if set PERFORM @extschema@.apply_constraints(v_row.parent_table); v_premade_count = round(EXTRACT('epoch' FROM age(v_next_partition_timestamp, v_current_partition_timestamp)) / EXTRACT('epoch' FROM v_row.part_interval::interval)); END LOOP; END LOOP; -- end of creation loop -- Manage dropping old partitions if retention option is set FOR v_row IN SELECT parent_table FROM @extschema@.part_config WHERE retention IS NOT NULL AND undo_in_progress = false AND (type = 'time-static' OR type = 'time-dynamic' OR type = 'time-custom') LOOP v_drop_count := v_drop_count + @extschema@.drop_partition_time(v_row.parent_table); END LOOP; FOR v_row IN SELECT parent_table FROM @extschema@.part_config WHERE retention IS NOT NULL AND undo_in_progress = false AND (type = 'id-static' OR type = 'id-dynamic') LOOP v_drop_count := v_drop_count + @extschema@.drop_partition_id(v_row.parent_table); END LOOP; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Partition maintenance finished. '||v_create_count||' partitons made. '||v_drop_count||' partitions dropped.'); IF v_step_overflow_id IS NOT NULL THEN PERFORM fail_job(v_job_id); ELSE PERFORM close_job(v_job_id); END IF; EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN RUN MAINTENANCE'')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to drop child tables from a time-based partition set. * Options to move table to different schema, drop only indexes or actually drop the table from the database. */ CREATE OR REPLACE FUNCTION 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) RETURNS int LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_child_table text; v_datetime_string text; v_drop_count int := 0; v_index record; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_part_interval interval; v_partition_timestamp timestamp; v_quarter text; v_retention interval; v_retention_keep_index boolean; v_retention_keep_table boolean; v_retention_schema text; v_step_id bigint; v_time_position int; v_type text; v_year text; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman drop_partition_time')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'drop_partition_time already running.'; RETURN 0; END IF; -- Allow override of configuration options IF p_retention IS NULL THEN SELECT type , part_interval::interval , retention::interval , retention_keep_table , retention_keep_index , datetime_string , retention_schema , jobmon INTO v_type , v_part_interval , v_retention , v_retention_keep_table , v_retention_keep_index , v_datetime_string , v_retention_schema , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic' OR type = 'time-custom') AND retention IS NOT NULL; IF v_part_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table with a retention period not found: %', p_parent_table; END IF; ELSE SELECT type , part_interval::interval , retention_keep_table , retention_keep_index , datetime_string , retention_schema , jobmon INTO v_type , v_part_interval , v_retention_keep_table , v_retention_keep_index , v_datetime_string , v_retention_schema , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic' OR type = 'time-custom'); v_retention := p_retention; IF v_part_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF p_keep_table IS NOT NULL THEN v_retention_keep_table = p_keep_table; END IF; IF p_keep_index IS NOT NULL THEN v_retention_keep_index = p_keep_index; END IF; IF p_retention_schema IS NOT NULL THEN v_retention_schema = p_retention_schema; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN DROP TIME PARTITION: '|| p_parent_table); END IF; -- Loop through child tables of the given parent FOR v_child_table IN SELECT n.nspname||'.'||c.relname FROM pg_inherits i join pg_class c ON i.inhrelid = c.oid join pg_namespace n ON c.relnamespace = n.oid WHERE i.inhparent::regclass = p_parent_table::regclass ORDER BY i.inhrelid ASC LOOP -- pull out datetime portion of partition's tablename to make the next one v_time_position := (length(v_child_table) - position('p_' in reverse(v_child_table))) + 2; IF v_part_interval <> '3 months' OR (v_part_interval = '3 months' AND v_type = 'time-custom') THEN v_partition_timestamp := to_timestamp(substring(v_child_table from v_time_position), v_datetime_string); ELSE -- to_timestamp doesn't recognize 'Q' date string formater. Handle it v_year := split_part(substring(v_child_table from v_time_position), 'q', 1); v_quarter := split_part(substring(v_child_table from v_time_position), 'q', 2); CASE WHEN v_quarter = '1' THEN v_partition_timestamp := to_timestamp(v_year || '-01-01', 'YYYY-MM-DD'); WHEN v_quarter = '2' THEN v_partition_timestamp := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); WHEN v_quarter = '3' THEN v_partition_timestamp := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); WHEN v_quarter = '4' THEN v_partition_timestamp := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); END CASE; END IF; -- Add one interval since partition names contain the start of the constraint period IF v_retention < (CURRENT_TIMESTAMP - (v_partition_timestamp + v_part_interval)) THEN IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Uninherit table '||v_child_table||' from '||p_parent_table); END IF; EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; 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, 'Drop table '||v_child_table); END IF; EXECUTE 'DROP TABLE '||v_child_table; 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 FOR v_index IN SELECT i.indexrelid::regclass AS name , c.conname FROM pg_catalog.pg_index i LEFT JOIN pg_catalog.pg_constraint c ON i.indexrelid = c.conindid WHERE i.indrelid = v_child_table::regclass LOOP IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Drop index '||v_index.name||' from '||v_child_table); END IF; IF v_index.conname IS NOT NULL THEN EXECUTE 'ALTER TABLE '||v_child_table||' DROP CONSTRAINT '||v_index.conname; ELSE EXECUTE 'DROP INDEX '||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; ELSE -- Move to new schema IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Moving table '||v_child_table||' to schema '||v_retention_schema); END IF; EXECUTE 'ALTER TABLE '||v_child_table||' SET SCHEMA '||v_retention_schema; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; -- End retention schema if v_drop_count := v_drop_count + 1; END IF; -- End retention check IF END LOOP; -- End child table loop IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Finished partition drop maintenance'); PERFORM update_step(v_step_id, 'OK', v_drop_count||' partitions dropped.'); PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_drop_count; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN DROP TIME PARTITION: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to undo time-based partitioning created by this extension */ CREATE OR REPLACE FUNCTION undo_partition_time(p_parent_table text, p_batch_count int DEFAULT 1, p_batch_interval interval DEFAULT NULL, p_keep_table boolean DEFAULT true) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_batch_loop_count int := 0; v_child_min timestamptz; v_child_loop_total bigint := 0; v_child_table text; v_control text; v_function_name text; v_inner_loop_count int; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_move_sql text; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_part_interval interval; v_row record; v_rowcount bigint; v_step_id bigint; v_total bigint := 0; v_trig_name text; v_undo_count int := 0; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman undo_time_partition')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'undo_time_partition already running.'; RETURN 0; END IF; SELECT part_interval::interval , control , jobmon INTO v_part_interval , v_control , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic' OR type = 'time-custom'); IF v_part_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN UNDO PARTITIONING: '||p_parent_table); v_step_id := add_step(v_job_id, 'Undoing partitioning for table '||p_parent_table); END IF; IF p_batch_interval IS NULL THEN p_batch_interval := v_part_interval; END IF; -- Stops new time partitons 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; -- Stop data going into child tables. SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; 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, v_parent_schema, '_part_trig_func', FALSE); EXECUTE 'DROP TRIGGER IF EXISTS '||v_trig_name||' ON '||p_parent_table; EXECUTE 'DROP FUNCTION IF EXISTS '||v_function_name||'()'; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Stopped partition creation process. Removed trigger & trigger function'); END IF; <> WHILE v_batch_loop_count < p_batch_count LOOP SELECT n.nspname||'.'||c.relname INTO v_child_table FROM pg_inherits i JOIN pg_class c ON i.inhrelid = c.oid JOIN pg_namespace n ON c.relnamespace = n.oid WHERE i.inhparent::regclass = p_parent_table::regclass ORDER BY i.inhrelid ASC; EXIT WHEN v_child_table IS NULL; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing child partition: '||v_child_table); END IF; EXECUTE 'SELECT min('||v_control||') FROM '||v_child_table INTO v_child_min; IF v_child_min IS NULL THEN -- No rows left in this child table. Remove from partition set. EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; IF p_keep_table = false THEN EXECUTE 'DROP TABLE '||v_child_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table DROPPED. Moved '||v_child_loop_total||' rows to parent'); END IF; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table UNINHERITED, not DROPPED. Moved '||v_child_loop_total||' rows to parent'); END IF; END IF; v_undo_count := v_undo_count + 1; CONTINUE outer_child_loop; END IF; v_inner_loop_count := 1; v_child_loop_total := 0; <> LOOP -- Get everything from the current child minimum up to the multiples of the given interval v_move_sql := 'WITH move_data AS (DELETE FROM '||v_child_table|| ' WHERE '||v_control||' <= '||quote_literal(v_child_min + (p_batch_interval * v_inner_loop_count))||' RETURNING *) INSERT INTO '||p_parent_table||' SELECT * FROM move_data'; EXECUTE v_move_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total := v_total + v_rowcount; v_child_loop_total := v_child_loop_total + v_rowcount; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Moved '||v_child_loop_total||' rows to parent.'); END IF; EXIT inner_child_loop WHEN v_rowcount = 0; -- exit before loop incr if table is empty v_inner_loop_count := v_inner_loop_count + 1; v_batch_loop_count := v_batch_loop_count + 1; EXIT outer_child_loop WHEN v_batch_loop_count >= p_batch_count; -- Exit outer FOR loop if p_batch_count is reached END LOOP inner_child_loop; END LOOP outer_child_loop; IF v_batch_loop_count < p_batch_count THEN -- FOR loop never ran, so there's no child tables left. DELETE FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing config from pg_partman'); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; RAISE NOTICE 'Copied % row(s) to the parent. Removed % partitions.', v_total, v_undo_count; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Final stats'); PERFORM update_step(v_step_id, 'OK', 'Copied '||v_total||' row(s) to the parent. Removed '||v_undo_count||' partitions.'); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_total; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN UNDO PARTITIONING: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to create id partitions */ CREATE FUNCTION create_id_partition (p_parent_table text, p_partition_ids bigint[]) RETURNS text LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_all text[] := ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER']; v_control text; v_grantees text[]; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_parent_grant record; v_parent_owner text; v_parent_schema text; v_parent_tablename text; v_parent_tablespace text; v_part_interval bigint; v_partition_name text; v_revoke text[]; v_step_id bigint; v_tablename text; v_id bigint; BEGIN SELECT control , part_interval , jobmon INTO v_control , v_part_interval , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; SELECT tableowner, schemaname, tablename, tablespace INTO v_parent_owner, v_parent_schema, v_parent_tablename, v_parent_tablespace FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; FOREACH v_id IN ARRAY p_partition_ids LOOP v_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_id::text, TRUE); -- If child table already exists, skip creation SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_tablename IS NOT NULL THEN CONTINUE; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating new partition '||v_partition_name||' with interval from '||v_id||' to '||(v_id + v_part_interval)-1); END IF; EXECUTE 'CREATE TABLE '||v_partition_name||' (LIKE '||p_parent_table||' INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING STORAGE INCLUDING COMMENTS)'; SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_parent_tablespace IS NOT NULL THEN EXECUTE 'ALTER TABLE '||v_partition_name||' SET TABLESPACE '||v_parent_tablespace; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' ADD CONSTRAINT '||v_tablename||'_partition_check CHECK ('||v_control||'>='||quote_literal(v_id)||' AND '||v_control||'<'||quote_literal(v_id + v_part_interval)||')'; EXECUTE 'ALTER TABLE '||v_partition_name||' INHERIT '||p_parent_table; FOR v_parent_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = p_parent_table GROUP BY grantee LOOP EXECUTE 'GRANT '||array_to_string(v_parent_grant.types, ',')||' ON '||v_partition_name||' TO '||v_parent_grant.grantee; SELECT array_agg(r) INTO v_revoke FROM (SELECT unnest(v_all) AS r EXCEPT SELECT unnest(v_parent_grant.types)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE '||array_to_string(v_revoke, ',')||' ON '||v_partition_name||' FROM '||v_parent_grant.grantee||' CASCADE'; END IF; v_grantees := array_append(v_grantees, v_parent_grant.grantee::text); END LOOP; -- Revoke all privileges from roles that have none on the parent IF v_grantees IS NOT NULL THEN SELECT array_agg(r) INTO v_revoke FROM ( SELECT DISTINCT grantee::text AS r FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = v_partition_name EXCEPT SELECT unnest(v_grantees)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE ALL ON '||v_partition_name||' FROM '||array_to_string(v_revoke, ','); END IF; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' OWNER TO '||v_parent_owner; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); PERFORM close_job(v_job_id); END IF; END LOOP; IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_partition_name; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN CREATE TABLE: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Create the trigger function for the parent table of an id-based partition set */ CREATE OR REPLACE FUNCTION create_id_function(p_parent_table text) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_control text; v_count int; v_current_partition_name text; v_current_partition_id bigint; v_datetime_string text; v_final_partition_id bigint; v_function_name text; v_job_id bigint; v_jobmon text; v_jobmon_schema text; v_last_partition text; v_max bigint; v_next_partition_id bigint; v_next_partition_name text; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_part_interval bigint; v_premake int; v_prev_partition_id bigint; v_prev_partition_name text; v_step_id bigint; v_trig_func text; v_type text; BEGIN SELECT type , part_interval::bigint , control , premake , last_partition , jobmon INTO v_type , v_part_interval , v_control , v_premake , v_last_partition , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE FUNCTION: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating partition function for table '||p_parent_table); END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; v_function_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, '_part_trig_func', FALSE); IF v_type = 'id-static' THEN EXECUTE 'SELECT COALESCE(max('||v_control||'), 0) FROM '||p_parent_table INTO v_max; v_current_partition_id = v_max - (v_max % v_part_interval); v_next_partition_id := v_current_partition_id + v_part_interval; v_current_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_current_partition_id::text, TRUE); v_trig_func := 'CREATE OR REPLACE FUNCTION '||v_function_name||'() RETURNS trigger LANGUAGE plpgsql AS $t$ DECLARE v_current_partition_id bigint; v_last_partition text := '||quote_literal(v_last_partition)||'; v_id_position int; v_next_partition_id bigint; v_next_partition_name text; BEGIN IF TG_OP = ''INSERT'' THEN IF NEW.'||v_control||' >= '||v_current_partition_id||' AND NEW.'||v_control||' < '||v_next_partition_id|| ' THEN INSERT INTO '||v_current_partition_name||' VALUES (NEW.*); '; FOR i IN 1..v_premake LOOP v_prev_partition_id := v_current_partition_id - (v_part_interval * i); v_next_partition_id := v_current_partition_id + (v_part_interval * i); v_final_partition_id := v_next_partition_id + v_part_interval; v_prev_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_prev_partition_id::text, TRUE); v_next_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_next_partition_id::text, TRUE); -- Check that child table exist before making a rule to insert to them. -- Handles edge case of changing premake immediately after running create_parent(). SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname ||'.'||tablename = v_prev_partition_name; IF v_count > 0 THEN -- Only handle previous partitions if they're starting above zero IF v_prev_partition_id >= 0 THEN v_trig_func := v_trig_func ||' ELSIF NEW.'||v_control||' >= '||v_prev_partition_id||' AND NEW.'||v_control||' < '||v_prev_partition_id + v_part_interval|| ' THEN INSERT INTO '||v_prev_partition_name||' VALUES (NEW.*); '; END IF; END IF; SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname ||'.'||tablename = v_next_partition_name; IF v_count > 0 THEN v_trig_func := v_trig_func ||' ELSIF NEW.'||v_control||' >= '||v_next_partition_id||' AND NEW.'||v_control||' < '||v_final_partition_id|| ' THEN INSERT INTO '||v_next_partition_name||' VALUES (NEW.*); '; END IF; END LOOP; v_trig_func := v_trig_func ||' ELSE RETURN NEW; END IF; v_current_partition_id := NEW.'||v_control||' - (NEW.'||v_control||' % '||v_part_interval||'); IF (NEW.'||v_control||' % '||v_part_interval||') > ('||v_part_interval||' / 2) THEN v_id_position := (length(v_last_partition) - position(''p_'' in reverse(v_last_partition))) + 2; v_next_partition_id := (substring(v_last_partition from v_id_position)::bigint) + '||v_part_interval||'; WHILE ((v_next_partition_id - v_current_partition_id) / '||v_part_interval||') <= '||v_premake||' LOOP v_next_partition_name := @extschema@.create_id_partition('||quote_literal(p_parent_table)||', ARRAY[v_next_partition_id]); UPDATE @extschema@.part_config SET last_partition = v_next_partition_name WHERE parent_table = '||quote_literal(p_parent_table)||'; PERFORM @extschema@.create_id_function('||quote_literal(p_parent_table)||'); PERFORM @extschema@.apply_constraints('||quote_literal(p_parent_table)||'); v_next_partition_id := v_next_partition_id + '||v_part_interval||'; END LOOP; END IF; END IF; RETURN NULL; END $t$;'; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Added function for current id interval: '||v_current_partition_id||' to '||v_final_partition_id-1); END IF; ELSIF v_type = 'id-dynamic' THEN -- The return inside the partition creation check is there to keep really high ID values from creating new partitions. v_trig_func := 'CREATE OR REPLACE FUNCTION '||v_function_name||'() RETURNS trigger LANGUAGE plpgsql AS $t$ DECLARE v_count int; v_current_partition_id bigint; v_current_partition_name text; v_id_position int; v_last_partition text := '||quote_literal(v_last_partition)||'; v_last_partition_id bigint; v_next_partition_id bigint; v_next_partition_name text; BEGIN IF TG_OP = ''INSERT'' THEN v_current_partition_id := NEW.'||v_control||' - (NEW.'||v_control||' % '||v_part_interval||'); v_current_partition_name := @extschema@.check_name_length('''||v_parent_tablename||''', '''||v_parent_schema||''', v_current_partition_id::text, TRUE); IF (NEW.'||v_control||' % '||v_part_interval||') > ('||v_part_interval||' / 2) THEN v_id_position := (length(v_last_partition) - position(''p_'' in reverse(v_last_partition))) + 2; v_last_partition_id = substring(v_last_partition from v_id_position)::bigint; v_next_partition_id := v_last_partition_id + '||v_part_interval||'; IF NEW.'||v_control||' >= v_next_partition_id THEN RETURN NEW; END IF; WHILE ((v_next_partition_id - v_current_partition_id) / '||v_part_interval||') <= '||v_premake||' LOOP v_next_partition_name := @extschema@.create_id_partition('||quote_literal(p_parent_table)||', ARRAY[v_next_partition_id]); IF v_next_partition_name IS NOT NULL THEN UPDATE @extschema@.part_config SET last_partition = v_next_partition_name WHERE parent_table = '||quote_literal(p_parent_table)||'; PERFORM @extschema@.create_id_function('||quote_literal(p_parent_table)||'); PERFORM @extschema@.apply_constraints('||quote_literal(p_parent_table)||'); END IF; v_next_partition_id := v_next_partition_id + '||v_part_interval||'; END LOOP; END IF; SELECT count(*) INTO v_count FROM pg_tables WHERE schemaname ||''.''|| tablename = v_current_partition_name; IF v_count > 0 THEN EXECUTE ''INSERT INTO ''||v_current_partition_name||'' VALUES($1.*)'' USING NEW; ELSE RETURN NEW; END IF; END IF; RETURN NULL; END $t$;'; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Added function for dynamic id table: '||p_parent_table); END IF; ELSE RAISE EXCEPTION 'ERROR: Invalid id partitioning type given: %', v_type; END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN CREATE FUNCTION: '||p_parent_table||')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''Partition function maintenance for table '||p_parent_table||' failed'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Populate the child table(s) of an id-based partition set with old data from the original parent */ CREATE OR REPLACE FUNCTION partition_data_id(p_parent_table text, p_batch_count int DEFAULT 1, p_batch_interval int DEFAULT NULL, p_lock_wait numeric DEFAULT 0) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_control text; v_last_partition_name text; v_lock_iter int := 1; v_lock_obtained boolean := FALSE; v_max_partition_id bigint; v_min_control bigint; v_min_partition_id bigint; v_part_interval bigint; v_partition_id bigint[]; v_rowcount bigint; v_sql text; v_total_rows bigint := 0; v_type text; BEGIN SELECT type , part_interval::bigint , control INTO v_type , v_part_interval , v_control FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF p_batch_interval IS NULL OR p_batch_interval > v_part_interval THEN p_batch_interval := v_part_interval; END IF; FOR i IN 1..p_batch_count LOOP EXECUTE 'SELECT min('||v_control||') FROM ONLY '||p_parent_table INTO v_min_control; IF v_min_control IS NULL THEN RETURN 0; END IF; v_min_partition_id = v_min_control - (v_min_control % v_part_interval); v_partition_id := ARRAY[v_min_partition_id]; -- RAISE NOTICE 'v_partition_id: %',v_partition_id; IF (v_min_control + p_batch_interval) >= (v_min_partition_id + v_part_interval) THEN v_max_partition_id := v_min_partition_id + v_part_interval; ELSE v_max_partition_id := v_min_control + p_batch_interval; END IF; -- RAISE NOTICE 'v_max_partition_id: %',v_max_partition_id; -- do some locking with timeout, if required IF p_lock_wait > 0 THEN v_lock_iter := 0; WHILE v_lock_iter <= 5 LOOP v_lock_iter := v_lock_iter + 1; BEGIN v_sql := 'SELECT * FROM ONLY ' || p_parent_table || ' WHERE '||v_control||' >= '||quote_literal(v_min_control)|| ' AND '||v_control||' < '||quote_literal(v_max_partition_id) ||' FOR UPDATE NOWAIT'; EXECUTE v_sql; 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 RETURN -1; END IF; END IF; v_last_partition_name := @extschema@.create_id_partition(p_parent_table, v_partition_id); EXECUTE 'WITH partition_data AS ( DELETE FROM ONLY '||p_parent_table||' WHERE '||v_control||' >= '||v_min_control|| ' AND '||v_control||' < '||v_max_partition_id||' RETURNING *) INSERT INTO '||v_last_partition_name||' SELECT * FROM partition_data'; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total_rows := v_total_rows + v_rowcount; IF v_rowcount = 0 THEN EXIT; END IF; END LOOP; IF v_type = 'id-static' THEN PERFORM @extschema@.create_id_function(p_parent_table); END IF; RETURN v_total_rows; END $$; /* * 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. */ CREATE OR REPLACE FUNCTION 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 SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_child_table text; v_control text; v_drop_count int := 0; v_id_position int; v_index record; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_max bigint; v_old_search_path text; v_part_interval bigint; v_partition_id bigint; v_retention bigint; v_retention_keep_index boolean; v_retention_keep_table boolean; v_retention_schema text; v_step_id bigint; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman drop_partition_id')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'drop_partition_id already running.'; RETURN 0; END IF; -- Allow override of configuration options IF p_retention IS NULL THEN SELECT part_interval::bigint , control , retention::bigint , retention_keep_table , retention_keep_index , retention_schema , jobmon INTO v_part_interval , v_control , v_retention , v_retention_keep_table , v_retention_keep_index , v_retention_schema , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic') AND retention IS NOT NULL; IF v_part_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table with a retention period not found: %', p_parent_table; END IF; ELSE SELECT part_interval::bigint , control , retention_keep_table , retention_keep_index , retention_schema , jobmon INTO v_part_interval , v_control , v_retention_keep_table , v_retention_keep_index , v_retention_schema , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic'); v_retention := p_retention; IF v_part_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF p_keep_table IS NOT NULL THEN v_retention_keep_table = p_keep_table; END IF; IF p_keep_index IS NOT NULL THEN v_retention_keep_index = p_keep_index; END IF; IF p_retention_schema IS NOT NULL THEN v_retention_schema = p_retention_schema; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN DROP ID PARTITION: '|| p_parent_table); END IF; EXECUTE 'SELECT max('||v_control||') FROM '||p_parent_table INTO v_max; -- Loop through child tables of the given parent FOR v_child_table IN SELECT n.nspname||'.'||c.relname FROM pg_inherits i join pg_class c ON i.inhrelid = c.oid join pg_namespace n ON c.relnamespace = n.oid WHERE i.inhparent::regclass = p_parent_table::regclass ORDER BY i.inhrelid ASC LOOP v_id_position := (length(v_child_table) - position('p_' in reverse(v_child_table))) + 2; v_partition_id := substring(v_child_table from v_id_position)::bigint; -- Add one interval since partition names contain the start of the constraint period IF v_retention <= (v_max - (v_partition_id + v_part_interval)) THEN IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Uninherit table '||v_child_table||' from '||p_parent_table); END IF; EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; 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, 'Drop table '||v_child_table); END IF; EXECUTE 'DROP TABLE '||v_child_table; 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 FOR v_index IN SELECT i.indexrelid::regclass AS name , c.conname FROM pg_catalog.pg_index i LEFT JOIN pg_catalog.pg_constraint c ON i.indexrelid = c.conindid WHERE i.indrelid = v_child_table::regclass LOOP IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Drop index '||v_index.name||' from '||v_child_table); END IF; IF v_index.conname IS NOT NULL THEN EXECUTE 'ALTER TABLE '||v_child_table||' DROP CONSTRAINT '||v_index.conname; ELSE EXECUTE 'DROP INDEX '||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; ELSE -- Move to new schema IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Moving table '||v_child_table||' to schema '||v_retention_schema); END IF; EXECUTE 'ALTER TABLE '||v_child_table||' SET SCHEMA '||v_retention_schema; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; -- End retention schema if v_drop_count := v_drop_count + 1; END IF; -- End retention check IF END LOOP; -- End child table loop IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Finished partition drop maintenance'); PERFORM update_step(v_step_id, 'OK', v_drop_count||' partitions dropped.'); PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_drop_count; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN DROP ID PARTITION: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Apply constraints managed by partman extension */ CREATE OR REPLACE FUNCTION apply_constraints(p_parent_table text, p_child_table text DEFAULT NULL, p_debug BOOLEAN DEFAULT FALSE) RETURNS void LANGUAGE plpgsql AS $$ DECLARE v_child_table text; v_child_tablename text; v_col text; v_constraint_cols text[]; v_constraint_col_type text; v_constraint_name text; v_datetime_string text; v_existing_constraint_name text; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_last_partition text; v_last_partition_id int; v_last_partition_timestamp timestamp; v_constraint_values record; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_part_interval text; v_partition_suffix text; v_premake int; v_sql text; v_step_id bigint; v_suffix_position int; v_type text; BEGIN SELECT type , part_interval , premake , datetime_string , last_partition , constraint_cols , jobmon INTO v_type , v_part_interval , v_premake , v_datetime_string , v_last_partition , v_constraint_cols , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_constraint_cols IS NULL THEN IF p_debug THEN RAISE NOTICE 'Given parent table (%) not set up for constraint management (constraint_cols is NULL)', p_parent_table; END IF; -- Returns silently to allow this function to be simply called by maintenance processes without having to check if config options are set. RETURN; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE CONSTRAINT: '||p_parent_table); END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; -- If p_child_table is null, figure out the partition that is the one right before the premake value backwards. IF p_child_table IS NULL THEN IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Automatically determining most recent child on which to apply constraints'); END IF; v_suffix_position := (length(v_last_partition) - position('p_' in reverse(v_last_partition))) + 2; IF v_type IN ('time-static', 'time-dynamic') THEN v_last_partition_timestamp := to_timestamp(substring(v_last_partition from v_suffix_position), v_datetime_string); v_partition_suffix := to_char(v_last_partition_timestamp - (v_part_interval::interval * ((v_premake * 2)+1) ), v_datetime_string); ELSIF v_type IN ('id-static', 'id-dynamic') THEN v_last_partition_id := substring(v_last_partition from v_suffix_position)::int; v_partition_suffix := (v_last_partition_id - (v_part_interval::int * ((v_premake * 2)+1) ))::text; END IF; v_child_table := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_partition_suffix, TRUE); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Target child table: '||v_child_table); END IF; ELSE v_child_table := p_child_table; END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Checking if target child table exists'); END IF; SELECT tablename INTO v_child_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_child_table; IF v_child_tablename IS NULL THEN IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'NOTICE', 'Target child table ('||v_child_table||') does not exist. Skipping constraint creation.'); PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; IF p_debug THEN RAISE NOTICE 'Target child table (%) does not exist. Skipping constraint creation.', v_child_table; END IF; RETURN; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; FOREACH v_col IN ARRAY v_constraint_cols LOOP SELECT c.conname INTO v_existing_constraint_name FROM pg_catalog.pg_constraint c JOIN pg_catalog.pg_attribute a ON c.conrelid = a.attrelid WHERE conrelid = v_child_table::regclass AND c.conname LIKE 'partmanconstr_%' AND c.contype = 'c' AND a.attname = v_col AND ARRAY[a.attnum] <@ c.conkey AND a.attisdropped = false; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Applying new constraint on column: '||v_col); END IF; IF v_existing_constraint_name IS NOT NULL THEN IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'NOTICE', 'Partman managed constraint already exists on this table ('||v_child_table||') and column ('||v_col||'). Skipping creation.'); END IF; RAISE WARNING 'Partman managed constraint already exists on this table (%) and column (%). Skipping creation.', v_child_table, v_col ; CONTINUE; END IF; -- 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 'SELECT min('||v_col||')::text AS min, max('||v_col||')::text AS max FROM '||v_child_table INTO v_constraint_values; IF v_constraint_values IS NOT NULL THEN v_sql := concat('ALTER TABLE ', v_child_table, ' ADD CONSTRAINT ', v_constraint_name , ' CHECK (', v_col, ' >= ', quote_literal(v_constraint_values.min), ' AND ' , v_col, ' <= ', quote_literal(v_constraint_values.max), ')' ); IF p_debug THEN RAISE NOTICE 'Constraint creation query: %', v_sql; END IF; EXECUTE v_sql; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'New constraint created: '||v_sql); END IF; ELSE IF p_debug THEN RAISE NOTICE 'Given column (%) contains all NULLs. No constraint created', v_col; END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'NOTICE', 'Given column ('||v_col||') contains all NULLs. No constraint created'); END IF; END IF; END LOOP; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN CREATE CONSTRAINT: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Drop constraints managed by pg_partman */ CREATE OR REPLACE FUNCTION drop_constraints(p_parent_table text, p_child_table text, p_debug boolean DEFAULT false) RETURNS void LANGUAGE plpgsql AS $$ DECLARE v_col text; v_constraint_cols text[]; v_existing_constraint_name text; v_exists boolean := FALSE; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_sql text; v_step_id bigint; BEGIN SELECT constraint_cols , jobmon INTO v_constraint_cols , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_constraint_cols IS NULL THEN RAISE EXCEPTION 'Given parent table (%) not set up for constraint management (constraint_cols is NULL)', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN DROP CONSTRAINT: '||p_parent_table); v_step_id := add_step(v_job_id, 'Entering constraint drop loop'); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; FOREACH v_col IN ARRAY v_constraint_cols LOOP SELECT c.conname INTO v_existing_constraint_name FROM pg_catalog.pg_constraint c JOIN pg_catalog.pg_attribute a ON c.conrelid = a.attrelid WHERE conrelid = p_child_table::regclass AND c.conname LIKE 'partmanconstr_%' AND c.contype = 'c' AND a.attname = v_col AND ARRAY[a.attnum] <@ c.conkey AND a.attisdropped = false; IF v_existing_constraint_name IS NOT NULL THEN v_exists := TRUE; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Dropping constraint on column: '||v_col); END IF; v_sql := 'ALTER TABLE '||p_child_table||' DROP CONSTRAINT '||v_existing_constraint_name; IF p_debug THEN RAISE NOTICE 'Constraint drop query: %', v_sql; END IF; EXECUTE v_sql; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Drop constraint query: '||v_sql); END IF; END IF; END LOOP; IF v_jobmon_schema IS NOT NULL AND v_exists IS FALSE THEN v_step_id := add_step(v_job_id, 'No constraints found to drop on child table: '||p_child_table); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN DROP CONSTRAINT: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to re-apply ownership & privileges on all child tables in a partition set using parent table as reference */ CREATE OR REPLACE FUNCTION reapply_privileges(p_parent_table text) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_all text[] := ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER']; v_child_owner text; v_child_table text; v_child_grant record; v_grant text; v_grantees text[]; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_match boolean; v_old_search_path text; v_parent_owner text; v_owner_sql text; v_revoke text[]; v_parent_grant record; v_sql text; v_step_id bigint; BEGIN SELECT jobmon INTO v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon IS NULL THEN RAISE EXCEPTION 'Given table is not managed by this extention: %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN RE-APPLYING PRIVILEGES TO ALL CHILD TABLES OF: '||p_parent_table); v_step_id := add_step(v_job_id, 'Setting new child table privileges'); END IF; SELECT tableowner INTO v_parent_owner FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; FOR v_child_table IN SELECT n.nspname||'.'||c.relname FROM pg_inherits i join pg_class c ON i.inhrelid = c.oid join pg_namespace n ON c.relnamespace = n.oid WHERE i.inhparent::regclass = p_parent_table::regclass ORDER BY i.inhrelid ASC LOOP IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'PENDING', 'Currently on child partition in ascending order: '||v_child_table); END IF; v_grantees := NULL; FOR v_parent_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = p_parent_table GROUP BY grantee LOOP -- Compare parent & child grants. Don't re-apply if it already exists v_match := false; FOR v_child_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = v_child_table GROUP BY grantee LOOP IF v_parent_grant.types = v_child_grant.types AND v_parent_grant.grantee = v_child_grant.grantee THEN v_match := true; END IF; END LOOP; IF v_match = false THEN EXECUTE 'GRANT '||array_to_string(v_parent_grant.types, ',')||' ON '||v_child_table||' TO '||v_parent_grant.grantee; SELECT array_agg(r) INTO v_revoke FROM (SELECT unnest(v_all) AS r EXCEPT SELECT unnest(v_parent_grant.types)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE '||array_to_string(v_revoke, ',')||' ON '||v_child_table||' FROM '||v_parent_grant.grantee||' CASCADE'; END IF; END IF; v_grantees := array_append(v_grantees, v_parent_grant.grantee::text); END LOOP; -- Revoke all privileges from roles that have none on the parent IF v_grantees IS NOT NULL THEN SELECT array_agg(r) INTO v_revoke FROM ( SELECT DISTINCT grantee::text AS r FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = v_child_table EXCEPT SELECT unnest(v_grantees)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE ALL ON '||v_child_table||' FROM '||array_to_string(v_revoke, ','); END IF; END IF; SELECT tableowner INTO v_child_owner FROM pg_tables WHERE schemaname ||'.'|| tablename = v_child_table; IF v_parent_owner <> v_child_owner THEN EXECUTE 'ALTER TABLE '||v_child_table||' OWNER TO '||v_parent_owner; END IF; END LOOP; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); PERFORM close_job(v_job_id); END IF; IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN RE-APPLYING PRIVILEGES TO ALL CHILD TABLES OF: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to undo id-based partitioning created by this extension */ CREATE OR REPLACE FUNCTION undo_partition_id(p_parent_table text, p_batch_count int DEFAULT 1, p_batch_interval bigint DEFAULT NULL, p_keep_table boolean DEFAULT true) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_batch_loop_count int := 0; v_child_loop_total bigint := 0; v_child_min bigint; v_child_table text; v_control text; v_function_name text; v_inner_loop_count int; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_move_sql text; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_part_interval bigint; v_row record; v_rowcount bigint; v_step_id bigint; v_trig_name text; v_total bigint := 0; v_undo_count int := 0; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman undo_id_partition')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'undo_id_partition already running.'; RETURN 0; END IF; SELECT part_interval::bigint , control , jobmon INTO v_part_interval , v_control , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic'); IF v_part_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN UNDO PARTITIONING: '||p_parent_table); v_step_id := add_step(v_job_id, 'Undoing partitioning for table '||p_parent_table); END IF; IF p_batch_interval IS NULL THEN p_batch_interval := v_part_interval; END IF; -- Stops new time partitons 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; -- Stop data going into child tables and stop new id partitions from being made. SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; 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, v_parent_schema, '_part_trig_func', FALSE); EXECUTE 'DROP TRIGGER IF EXISTS '||v_trig_name||' ON '||p_parent_table; EXECUTE 'DROP FUNCTION IF EXISTS '||v_function_name||'()'; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Stopped partition creation process. Removed trigger & trigger function'); END IF; <> WHILE v_batch_loop_count < p_batch_count LOOP SELECT n.nspname||'.'||c.relname INTO v_child_table FROM pg_inherits i JOIN pg_class c ON i.inhrelid = c.oid JOIN pg_namespace n ON c.relnamespace = n.oid WHERE i.inhparent::regclass = p_parent_table::regclass ORDER BY i.inhrelid ASC; EXIT WHEN v_child_table IS NULL; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing child partition: '||v_child_table); END IF; EXECUTE 'SELECT min('||v_control||') FROM '||v_child_table INTO v_child_min; IF v_child_min IS NULL THEN -- No rows left in this child table. Remove from partition set. EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; IF p_keep_table = false THEN EXECUTE 'DROP TABLE '||v_child_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table DROPPED. Moved '||v_child_loop_total||' rows to parent'); END IF; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table UNINHERITED, not DROPPED. Moved '||v_child_loop_total||' rows to parent'); END IF; END IF; v_undo_count := v_undo_count + 1; CONTINUE outer_child_loop; END IF; v_inner_loop_count := 1; v_child_loop_total := 0; <> LOOP -- Get everything from the current child minimum up to the multiples of the given interval v_move_sql := 'WITH move_data AS (DELETE FROM '||v_child_table|| ' WHERE '||v_control||' <= '||quote_literal(v_child_min + (p_batch_interval * v_inner_loop_count))||' RETURNING *) INSERT INTO '||p_parent_table||' SELECT * FROM move_data'; EXECUTE v_move_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total := v_total + v_rowcount; v_child_loop_total := v_child_loop_total + v_rowcount; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Moved '||v_child_loop_total||' rows to parent.'); END IF; EXIT inner_child_loop WHEN v_rowcount = 0; -- exit before loop incr if table is empty v_inner_loop_count := v_inner_loop_count + 1; v_batch_loop_count := v_batch_loop_count + 1; EXIT outer_child_loop WHEN v_batch_loop_count >= p_batch_count; -- Exit outer FOR loop if p_batch_count is reached END LOOP inner_child_loop; END LOOP outer_child_loop; IF v_batch_loop_count < p_batch_count THEN -- FOR loop never ran, so there's no child tables left. DELETE FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing config from pg_partman'); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; RAISE NOTICE 'Copied % row(s) to the parent. Removed % partitions.', v_total, v_undo_count; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Final stats'); PERFORM update_step(v_step_id, 'OK', 'Copied '||v_total||' row(s) to the parent. Removed '||v_undo_count||' partitions.'); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_total; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN UNDO PARTITIONING: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to undo partitioning. * Will actually work on any parent/child table set, not just ones created by pg_partman. */ CREATE FUNCTION undo_partition(p_parent_table text, p_batch_count int DEFAULT 1, p_keep_table boolean DEFAULT true, p_jobmon boolean DEFAULT true) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_batch_loop_count bigint := 0; v_child_count bigint; v_child_table text; v_copy_sql text; v_function_name text; v_job_id bigint; v_jobmon_schema text; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_part_interval interval; v_rowcount bigint; v_step_id bigint; v_total bigint := 0; v_trig_name text; v_undo_count int := 0; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman undo_partition')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'undo_partition already running.'; RETURN 0; END IF; IF p_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN UNDO PARTITIONING: '||p_parent_table); v_step_id := add_step(v_job_id, 'Undoing partitioning for table '||p_parent_table); END IF; -- Stops new time partitons 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; -- Stop data going into child tables and stop new id partitions from being made. SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; 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, v_parent_schema, '_part_trig_func', FALSE); EXECUTE 'DROP TRIGGER IF EXISTS '||v_trig_name||' ON '||p_parent_table; EXECUTE 'DROP FUNCTION IF EXISTS '||v_function_name||'()'; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Stopped partition creation process. Removed trigger & trigger function'); END IF; WHILE v_batch_loop_count < p_batch_count LOOP SELECT n.nspname||'.'||c.relname INTO v_child_table FROM pg_inherits i JOIN pg_class c ON i.inhrelid = c.oid JOIN pg_namespace n ON c.relnamespace = n.oid WHERE i.inhparent::regclass = p_parent_table::regclass ORDER BY i.inhrelid ASC; EXIT WHEN v_child_table IS NULL; EXECUTE 'SELECT count(*) FROM '||v_child_table INTO v_child_count; IF v_child_count = 0 THEN -- No rows left in this child table. Remove from partition set. EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; IF p_keep_table = false THEN EXECUTE 'DROP TABLE '||v_child_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table DROPPED. Moved '||coalesce(v_rowcount, 0)||' rows to parent'); END IF; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table UNINHERITED, not DROPPED. Copied '||coalesce(v_rowcount, 0)||' rows to parent'); END IF; END IF; v_undo_count := v_undo_count + 1; CONTINUE; END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing child partition: '||v_child_table); END IF; v_copy_sql := 'INSERT INTO '||p_parent_table||' SELECT * FROM '||v_child_table; EXECUTE v_copy_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total := v_total + v_rowcount; EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; IF p_keep_table = false THEN EXECUTE 'DROP TABLE '||v_child_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table DROPPED. Moved '||v_rowcount||' rows to parent'); END IF; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table UNINHERITED, not DROPPED. Copied '||v_rowcount||' rows to parent'); END IF; END IF; v_batch_loop_count := v_batch_loop_count + 1; v_undo_count := v_undo_count + 1; END LOOP; IF v_undo_count = 0 THEN -- FOR loop never ran, so there's no child tables left. DELETE FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing config from pg_partman (if it existed)'); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; RAISE NOTICE 'Copied % row(s) from % child table(s) to the parent: %', v_total, v_undo_count, p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Final stats'); PERFORM update_step(v_step_id, 'OK', 'Copied '||v_total||' row(s) from '||v_undo_count||' child table(s) to the parent'); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_total; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; IF v_job_id IS NULL THEN v_job_id := add_job('PARTMAN UNDO PARTITIONING: '||p_parent_table); v_step_id := add_step(v_job_id, 'Partition function maintenance for table '||p_parent_table||' failed'); ELSIF v_step_id IS NULL THEN v_step_id := add_step(v_job_id, 'EXCEPTION before first step logged'); END IF; PERFORM update_step(v_step_id, 'CRITICAL', 'ERROR: '||coalesce(SQLERRM,'unknown')); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; DO $$ DECLARE v_row record; BEGIN FOR v_row IN SELECT statement FROM partman_preserve_privs_temp LOOP IF v_row.statement IS NOT NULL THEN EXECUTE v_row.statement; END IF; END LOOP; END $$; DROP TABLE partman_preserve_privs_temp; pg_partman-2.2.2/updates/pg_partman--1.6.0--1.6.1.sql000066400000000000000000001251561262146621700214370ustar00rootroot00000000000000-- 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. CREATE TEMP TABLE partman_preserve_privs_temp (statement text); INSERT INTO partman_preserve_privs_temp SELECT 'GRANT EXECUTE ON FUNCTION @extschema@.partition_data_id(text, int, int, numeric, text) TO '||array_to_string(array_agg(grantee::text), ',')||';' FROM information_schema.routine_privileges WHERE routine_schema = '@extschema@' AND routine_name = 'partition_data_id'; INSERT INTO partman_preserve_privs_temp SELECT 'GRANT EXECUTE ON FUNCTION @extschema@.partition_data_time(text, int, interval, numeric, text) TO '||array_to_string(array_agg(grantee::text), ',')||';' FROM information_schema.routine_privileges WHERE routine_schema = '@extschema@' AND routine_name = 'partition_data_time'; DROP FUNCTION @extschema@.check_unique_column(text, text); DROP TYPE @extschema@.check_unique_table; DROP FUNCTION partition_data_id(text, int, int, numeric); DROP FUNCTION partition_data_time(text, int, interval, numeric); DROP FUNCTION IF EXISTS create_next_time_partition (text); /* * Populate the child table(s) of an id-based partition set with old data from the original parent */ CREATE FUNCTION partition_data_id(p_parent_table text, p_batch_count int DEFAULT 1, p_batch_interval int DEFAULT NULL, p_lock_wait numeric DEFAULT 0, p_order text DEFAULT 'ASC') RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_control text; v_current_partition_name text; v_lock_iter int := 1; v_lock_obtained boolean := FALSE; v_max_partition_id bigint; v_min_partition_id bigint; v_part_interval bigint; v_partition_id bigint[]; v_rowcount bigint; v_sql text; v_start_control bigint; v_total_rows bigint := 0; v_type text; BEGIN SELECT type , part_interval::bigint , control INTO v_type , v_part_interval , v_control FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF p_batch_interval IS NULL OR p_batch_interval > v_part_interval THEN p_batch_interval := v_part_interval; END IF; FOR i IN 1..p_batch_count LOOP IF p_order = 'ASC' THEN EXECUTE 'SELECT min('||v_control||') FROM ONLY '||p_parent_table INTO v_start_control; IF v_start_control IS NULL THEN RETURN 0; END IF; v_min_partition_id = v_start_control - (v_start_control % v_part_interval); v_partition_id := ARRAY[v_min_partition_id]; -- Check if custom batch interval overflows current partition maximum IF (v_start_control + p_batch_interval) >= (v_min_partition_id + v_part_interval) THEN v_max_partition_id := v_min_partition_id + v_part_interval; ELSE v_max_partition_id := v_start_control + p_batch_interval; END IF; ELSIF p_order = 'DESC' THEN EXECUTE 'SELECT max('||v_control||') FROM ONLY '||p_parent_table INTO v_start_control; IF v_start_control IS NULL THEN RETURN 0; END IF; v_min_partition_id = v_start_control - (v_start_control % v_part_interval); -- Must be greater than max value still in parent table since query below grabs < max v_max_partition_id := v_min_partition_id + v_part_interval; v_partition_id := ARRAY[v_min_partition_id]; -- Make sure minimum doesn't underflow current partition minimum IF (v_start_control - p_batch_interval) >= v_min_partition_id THEN v_min_partition_id = v_start_control - p_batch_interval; END IF; ELSE RAISE EXCEPTION 'Invalid value for p_order. Must be ASC or DESC'; END IF; -- do some locking with timeout, if required IF p_lock_wait > 0 THEN v_lock_iter := 0; WHILE v_lock_iter <= 5 LOOP v_lock_iter := v_lock_iter + 1; BEGIN v_sql := 'SELECT * FROM ONLY ' || p_parent_table || ' WHERE '||v_control||' >= '||quote_literal(v_min_partition_id)|| ' AND '||v_control||' < '||quote_literal(v_max_partition_id) ||' FOR UPDATE NOWAIT'; EXECUTE v_sql; 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 RETURN -1; END IF; END IF; v_current_partition_name := @extschema@.create_id_partition(p_parent_table, v_partition_id); EXECUTE 'WITH partition_data AS ( DELETE FROM ONLY '||p_parent_table||' WHERE '||v_control||' >= '||v_min_partition_id|| ' AND '||v_control||' < '||v_max_partition_id||' RETURNING *) INSERT INTO '||v_current_partition_name||' SELECT * FROM partition_data'; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total_rows := v_total_rows + v_rowcount; IF v_rowcount = 0 THEN EXIT; END IF; END LOOP; IF v_type = 'id-static' THEN PERFORM @extschema@.create_id_function(p_parent_table); END IF; RETURN v_total_rows; END $$; /* * Populate the child table(s) of a time-based partition set with old data from the original parent */ CREATE FUNCTION 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') RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_control text; v_datetime_string text; v_current_partition_name text; v_max_partition_timestamp timestamp; v_last_partition text; v_lock_iter int := 1; v_lock_obtained boolean := FALSE; v_min_partition_timestamp timestamp; v_part_interval interval; v_partition_timestamp timestamp[]; v_rowcount bigint; v_sql text; v_start_control timestamp; v_time_position int; v_total_rows bigint := 0; v_type text; BEGIN SELECT type , part_interval::interval , control , datetime_string , last_partition INTO v_type , v_part_interval , v_control , v_datetime_string , v_last_partition FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic' OR type = 'time-custom'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF p_batch_interval IS NULL OR p_batch_interval > v_part_interval THEN p_batch_interval := v_part_interval; END IF; FOR i IN 1..p_batch_count LOOP IF p_order = 'ASC' THEN EXECUTE 'SELECT min('||v_control||') FROM ONLY '||p_parent_table INTO v_start_control; ELSIF p_order = 'DESC' THEN EXECUTE 'SELECT max('||v_control||') FROM ONLY '||p_parent_table INTO v_start_control; ELSE RAISE EXCEPTION 'Invalid value for p_order. Must be ASC or DESC'; END IF; IF v_start_control IS NULL THEN RETURN 0; END IF; IF v_type = 'time-static' OR v_type = 'time-dynamic' THEN CASE WHEN v_part_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_part_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_part_interval = '1 hour' THEN v_min_partition_timestamp := date_trunc('hour', v_start_control); WHEN v_part_interval = '1 day' THEN v_min_partition_timestamp := date_trunc('day', v_start_control); WHEN v_part_interval = '1 week' THEN v_min_partition_timestamp := date_trunc('week', v_start_control); WHEN v_part_interval = '1 month' THEN v_min_partition_timestamp := date_trunc('month', v_start_control); WHEN v_part_interval = '3 months' THEN v_min_partition_timestamp := date_trunc('quarter', v_start_control); WHEN v_part_interval = '1 year' THEN v_min_partition_timestamp := date_trunc('year', v_start_control); END CASE; ELSIF v_type = 'time-custom' THEN -- Keep going backwards, checking if the time interval encompases the current v_start_control value v_time_position := (length(v_last_partition) - position('p_' in reverse(v_last_partition))) + 2; v_min_partition_timestamp := to_timestamp(substring(v_last_partition from v_time_position), v_datetime_string); v_max_partition_timestamp := v_min_partition_timestamp + v_part_interval; LOOP IF v_start_control >= v_min_partition_timestamp AND v_start_control < v_max_partition_timestamp THEN EXIT; ELSE v_max_partition_timestamp := v_min_partition_timestamp; BEGIN v_min_partition_timestamp := v_min_partition_timestamp - v_part_interval; 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_interval; END; END IF; END LOOP; END IF; v_partition_timestamp := ARRAY[v_min_partition_timestamp]; IF p_order = 'ASC' THEN IF (v_start_control + p_batch_interval) >= (v_min_partition_timestamp + v_part_interval) THEN v_max_partition_timestamp := v_min_partition_timestamp + v_part_interval; ELSE v_max_partition_timestamp := v_start_control + p_batch_interval; END IF; ELSIF p_order = 'DESC' THEN -- Must be greater than max value still in parent table since query below grabs < max v_max_partition_timestamp := v_min_partition_timestamp + v_part_interval; -- Make sure minimum doesn't underflow current partition minimum IF (v_start_control - p_batch_interval) >= v_min_partition_timestamp THEN v_min_partition_timestamp = v_start_control - p_batch_interval; END IF; ELSE RAISE EXCEPTION 'Invalid value for p_order. Must be ASC or DESC'; END IF; -- do some locking with timeout, if required IF p_lock_wait > 0 THEN v_lock_iter := 0; WHILE v_lock_iter <= 5 LOOP v_lock_iter := v_lock_iter + 1; BEGIN v_sql := 'SELECT * FROM ONLY ' || p_parent_table || ' WHERE '||v_control||' >= '||quote_literal(v_min_partition_timestamp)|| ' AND '||v_control||' < '||quote_literal(v_max_partition_timestamp) ||' FOR UPDATE NOWAIT'; EXECUTE v_sql; 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 RETURN -1; END IF; END IF; v_current_partition_name := @extschema@.create_time_partition(p_parent_table, v_partition_timestamp); EXECUTE 'WITH partition_data AS ( DELETE FROM ONLY '||p_parent_table||' WHERE '||v_control||' >= '||quote_literal(v_min_partition_timestamp)|| ' AND '||v_control||' < '||quote_literal(v_max_partition_timestamp)||' RETURNING *) INSERT INTO '||v_current_partition_name||' SELECT * FROM partition_data'; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total_rows := v_total_rows + v_rowcount; IF v_rowcount = 0 THEN EXIT; END IF; END LOOP; IF v_type = 'time-static' THEN PERFORM @extschema@.create_time_function(p_parent_table); END IF; RETURN v_total_rows; END $$; /* * Function to turn a table into the parent of a partition set */ CREATE OR REPLACE FUNCTION create_parent( p_parent_table text , p_control text , p_type text , p_interval text , p_constraint_cols text[] DEFAULT NULL , p_premake int DEFAULT 4 , p_start_partition text DEFAULT NULL , p_jobmon boolean DEFAULT true , p_debug boolean DEFAULT false) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_base_timestamp timestamp; v_count int := 1; v_datetime_string text; v_id_interval bigint; v_job_id bigint; v_jobmon_schema text; v_last_partition_name text; v_old_search_path text; v_partition_time timestamp; v_partition_time_array timestamp[]; v_partition_id bigint[]; v_max bigint; v_notnull boolean; v_start_time timestamp; v_starting_partition_id bigint; v_step_id bigint; v_step_overflow_id bigint; v_tablename text; v_time_interval interval; BEGIN IF position('.' in p_parent_table) = 0 THEN RAISE EXCEPTION 'Parent table must be schema qualified'; END IF; SELECT tablename INTO v_tablename FROM pg_tables WHERE schemaname || '.' || tablename = p_parent_table; IF v_tablename IS NULL THEN RAISE EXCEPTION 'Please create given parent table first: %', p_parent_table; END IF; SELECT attnotnull INTO v_notnull FROM pg_attribute WHERE attrelid = p_parent_table::regclass AND attname = p_control; IF v_notnull = false OR v_notnull IS NULL THEN RAISE EXCEPTION 'Control column (%) for parent table (%) must be NOT NULL', p_control, p_parent_table; END IF; IF NOT @extschema@.check_partition_type(p_type) THEN RAISE EXCEPTION '% is not a valid partitioning type', p_type; END IF; IF p_type = 'time-custom' AND @extschema@.check_version('9.2.0') IS FALSE THEN RAISE EXCEPTION 'The "time-custom" type requires a minimum PostgreSQL version of 9.2.0'; END IF; EXECUTE 'LOCK TABLE '||p_parent_table||' IN ACCESS EXCLUSIVE MODE'; IF p_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN SETUP PARENT: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating initial partitions on new parent table: '||p_parent_table); END IF; IF p_type = 'time-static' OR p_type = 'time-dynamic' OR p_type = 'time-custom' 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 <> 'time-custom' THEN RAISE EXCEPTION 'Must use a predefined time interval if not using type "time-custom". See documentation.'; 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; -- First partition is either the min premake or p_start_partition v_start_time := COALESCE(p_start_partition::timestamp, CURRENT_TIMESTAMP - (v_time_interval * p_premake)); 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 v_datetime_string := 'YYYY'; 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 v_partition_time_array := array_append(v_partition_time_array, v_base_timestamp); LOOP -- If current loop value is less than or equal to the value of the max premake, add time to array. IF (v_base_timestamp + (v_time_interval * v_count)) < (CURRENT_TIMESTAMP + (v_time_interval * p_premake)) THEN BEGIN v_partition_time := (v_base_timestamp + (v_time_interval * v_count))::timestamp; 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. 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'); CONTINUE; END; ELSE EXIT; -- all needed partitions added to array. Exit the loop. END IF; v_count := v_count + 1; END LOOP; INSERT INTO @extschema@.part_config (parent_table, type, part_interval, control, premake, constraint_cols, datetime_string, jobmon) VALUES (p_parent_table, p_type, v_time_interval, p_control, p_premake, p_constraint_cols, v_datetime_string, p_jobmon); v_last_partition_name := @extschema@.create_time_partition(p_parent_table, v_partition_time_array); -- Doing separate update because create function requires in config table last_partition to be set UPDATE @extschema@.part_config SET last_partition = v_last_partition_name WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Time partitions premade: '||p_premake); END IF; END IF; IF p_type = 'id-static' OR p_type = 'id-dynamic' THEN v_id_interval := p_interval::bigint; IF v_id_interval <= 1 THEN RAISE EXCEPTION 'Interval for serial partitioning must be greater than 1'; END IF; -- If custom start partition is set, use that. -- If custom start is not set and there is already data, start partitioning with the highest current value EXECUTE 'SELECT COALESCE('||quote_nullable(p_start_partition::bigint)||', max('||p_control||')::bigint, 0) FROM '||p_parent_table||' LIMIT 1' INTO v_max; 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 THEN v_partition_id = array_append(v_partition_id, (v_starting_partition_id - v_id_interval*i)); END IF; v_partition_id = array_append(v_partition_id, (v_id_interval*i) + v_starting_partition_id); END LOOP; INSERT INTO @extschema@.part_config (parent_table, type, part_interval, control, premake, constraint_cols, jobmon) VALUES (p_parent_table, p_type, v_id_interval, p_control, p_premake, p_constraint_cols, p_jobmon); v_last_partition_name := @extschema@.create_id_partition(p_parent_table, v_partition_id); -- Doing separate update because create function needs parent table in config table for apply_grants() UPDATE @extschema@.part_config SET last_partition = v_last_partition_name WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'ID partitions premade: '||p_premake); END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Creating partition function'); END IF; IF p_type = 'time-static' OR p_type = 'time-dynamic' OR p_type = 'time-custom' THEN PERFORM @extschema@.create_time_function(p_parent_table); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Time function created'); END IF; ELSIF p_type = 'id-static' OR p_type = 'id-dynamic' THEN PERFORM @extschema@.create_id_function(p_parent_table); 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); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); IF v_step_overflow_id IS NOT NULL THEN PERFORM fail_job(v_job_id); ELSE PERFORM close_job(v_job_id); END IF; EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN CREATE PARENT: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''Partition creation for table '||p_parent_table||' failed'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Create the trigger function for the parent table of an id-based partition set */ CREATE OR REPLACE FUNCTION create_id_function(p_parent_table text) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_control text; v_count int; v_current_partition_name text; v_current_partition_id bigint; v_datetime_string text; v_final_partition_id bigint; v_function_name text; v_job_id bigint; v_jobmon text; v_jobmon_schema text; v_last_partition text; v_max bigint; v_next_partition_id bigint; v_next_partition_name text; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_part_interval bigint; v_premake int; v_prev_partition_id bigint; v_prev_partition_name text; v_step_id bigint; v_trig_func text; v_type text; BEGIN SELECT type , part_interval::bigint , control , premake , last_partition , jobmon INTO v_type , v_part_interval , v_control , v_premake , v_last_partition , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE FUNCTION: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating partition function for table '||p_parent_table); END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; v_function_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, '_part_trig_func', FALSE); IF v_type = 'id-static' THEN EXECUTE 'SELECT COALESCE(max('||v_control||'), 0) FROM '||p_parent_table INTO v_max; v_current_partition_id = v_max - (v_max % v_part_interval); v_next_partition_id := v_current_partition_id + v_part_interval; v_current_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_current_partition_id::text, TRUE); v_trig_func := 'CREATE OR REPLACE FUNCTION '||v_function_name||'() RETURNS trigger LANGUAGE plpgsql AS $t$ DECLARE v_current_partition_id bigint; v_last_partition text := '||quote_literal(v_last_partition)||'; v_id_position int; v_next_partition_id bigint; v_next_partition_name text; BEGIN IF TG_OP = ''INSERT'' THEN IF NEW.'||v_control||' >= '||v_current_partition_id||' AND NEW.'||v_control||' < '||v_next_partition_id|| ' THEN INSERT INTO '||v_current_partition_name||' VALUES (NEW.*); '; FOR i IN 1..v_premake LOOP v_prev_partition_id := v_current_partition_id - (v_part_interval * i); v_next_partition_id := v_current_partition_id + (v_part_interval * i); v_final_partition_id := v_next_partition_id + v_part_interval; v_prev_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_prev_partition_id::text, TRUE); v_next_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_next_partition_id::text, TRUE); -- Check that child table exist before making a rule to insert to them. -- Handles edge case of changing premake immediately after running create_parent(). SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname ||'.'||tablename = v_prev_partition_name; IF v_count > 0 THEN -- Only handle previous partitions if they're starting above zero IF v_prev_partition_id >= 0 THEN v_trig_func := v_trig_func ||' ELSIF NEW.'||v_control||' >= '||v_prev_partition_id||' AND NEW.'||v_control||' < '||v_prev_partition_id + v_part_interval|| ' THEN INSERT INTO '||v_prev_partition_name||' VALUES (NEW.*); '; END IF; END IF; SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname ||'.'||tablename = v_next_partition_name; IF v_count > 0 THEN v_trig_func := v_trig_func ||' ELSIF NEW.'||v_control||' >= '||v_next_partition_id||' AND NEW.'||v_control||' < '||v_final_partition_id|| ' THEN INSERT INTO '||v_next_partition_name||' VALUES (NEW.*); '; END IF; END LOOP; v_trig_func := v_trig_func ||' ELSE RETURN NEW; END IF; v_current_partition_id := NEW.'||v_control||' - (NEW.'||v_control||' % '||v_part_interval||'); IF (NEW.'||v_control||' % '||v_part_interval||') > ('||v_part_interval||' / 2) THEN v_id_position := (length(v_last_partition) - position(''p_'' in reverse(v_last_partition))) + 2; v_next_partition_id := (substring(v_last_partition from v_id_position)::bigint) + '||v_part_interval||'; WHILE ((v_next_partition_id - v_current_partition_id) / '||v_part_interval||') <= '||v_premake||' LOOP v_next_partition_name := @extschema@.create_id_partition('||quote_literal(p_parent_table)||', ARRAY[v_next_partition_id]); UPDATE @extschema@.part_config SET last_partition = v_next_partition_name WHERE parent_table = '||quote_literal(p_parent_table)||'; PERFORM @extschema@.create_id_function('||quote_literal(p_parent_table)||'); PERFORM @extschema@.apply_constraints('||quote_literal(p_parent_table)||'); v_next_partition_id := v_next_partition_id + '||v_part_interval||'; END LOOP; END IF; END IF; RETURN NULL; END $t$;'; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Added function for current id interval: '||v_current_partition_id||' to '||v_final_partition_id-1); END IF; ELSIF v_type = 'id-dynamic' THEN -- The return inside the partition creation check is there to keep really high ID values from creating new partitions. v_trig_func := 'CREATE OR REPLACE FUNCTION '||v_function_name||'() RETURNS trigger LANGUAGE plpgsql AS $t$ DECLARE v_count int; v_current_partition_id bigint; v_current_partition_name text; v_id_position int; v_last_partition text := '||quote_literal(v_last_partition)||'; v_last_partition_id bigint; v_next_partition_id bigint; v_next_partition_name text; BEGIN IF TG_OP = ''INSERT'' THEN v_current_partition_id := NEW.'||v_control||' - (NEW.'||v_control||' % '||v_part_interval||'); v_current_partition_name := @extschema@.check_name_length('''||v_parent_tablename||''', '''||v_parent_schema||''', v_current_partition_id::text, TRUE); IF (NEW.'||v_control||' % '||v_part_interval||') > ('||v_part_interval||' / 2) THEN v_id_position := (length(v_last_partition) - position(''p_'' in reverse(v_last_partition))) + 2; v_last_partition_id = substring(v_last_partition from v_id_position)::bigint; v_next_partition_id := v_last_partition_id + '||v_part_interval||'; IF NEW.'||v_control||' >= v_next_partition_id THEN RETURN NEW; END IF; WHILE ((v_next_partition_id - v_current_partition_id) / '||v_part_interval||') <= '||v_premake||' LOOP v_next_partition_name := @extschema@.create_id_partition('||quote_literal(p_parent_table)||', ARRAY[v_next_partition_id]); IF v_next_partition_name IS NOT NULL THEN UPDATE @extschema@.part_config SET last_partition = v_next_partition_name WHERE parent_table = '||quote_literal(p_parent_table)||'; PERFORM @extschema@.create_id_function('||quote_literal(p_parent_table)||'); PERFORM @extschema@.apply_constraints('||quote_literal(p_parent_table)||'); END IF; v_next_partition_id := v_next_partition_id + '||v_part_interval||'; END LOOP; END IF; SELECT count(*) INTO v_count FROM pg_tables WHERE schemaname ||''.''|| tablename = v_current_partition_name; IF v_count > 0 THEN EXECUTE ''INSERT INTO ''||v_current_partition_name||'' VALUES($1.*)'' USING NEW; ELSE RETURN NEW; END IF; END IF; RETURN NULL; END $t$;'; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Added function for dynamic id table: '||p_parent_table); END IF; ELSE RAISE EXCEPTION 'ERROR: Invalid id partitioning type given: %', v_type; END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN CREATE FUNCTION: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''Partition function maintenance for table '||p_parent_table||' failed'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to undo partitioning. * Will actually work on any parent/child table set, not just ones created by pg_partman. */ CREATE OR REPLACE FUNCTION undo_partition(p_parent_table text, p_batch_count int DEFAULT 1, p_keep_table boolean DEFAULT true, p_jobmon boolean DEFAULT true) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_batch_loop_count bigint := 0; v_child_count bigint; v_child_table text; v_copy_sql text; v_function_name text; v_job_id bigint; v_jobmon_schema text; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_part_interval interval; v_rowcount bigint; v_step_id bigint; v_total bigint := 0; v_trig_name text; v_undo_count int := 0; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman undo_partition')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'undo_partition already running.'; RETURN 0; END IF; IF p_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN UNDO PARTITIONING: '||p_parent_table); v_step_id := add_step(v_job_id, 'Undoing partitioning for table '||p_parent_table); END IF; -- Stops new time partitons 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; -- Stop data going into child tables and stop new id partitions from being made. SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; 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, v_parent_schema, '_part_trig_func', FALSE); EXECUTE 'DROP TRIGGER IF EXISTS '||v_trig_name||' ON '||p_parent_table; EXECUTE 'DROP FUNCTION IF EXISTS '||v_function_name||'()'; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Stopped partition creation process. Removed trigger & trigger function'); END IF; WHILE v_batch_loop_count < p_batch_count LOOP SELECT n.nspname||'.'||c.relname INTO v_child_table FROM pg_inherits i JOIN pg_class c ON i.inhrelid = c.oid JOIN pg_namespace n ON c.relnamespace = n.oid WHERE i.inhparent::regclass = p_parent_table::regclass ORDER BY i.inhrelid ASC; EXIT WHEN v_child_table IS NULL; EXECUTE 'SELECT count(*) FROM '||v_child_table INTO v_child_count; IF v_child_count = 0 THEN -- No rows left in this child table. Remove from partition set. EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; IF p_keep_table = false THEN EXECUTE 'DROP TABLE '||v_child_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table DROPPED. Moved '||coalesce(v_rowcount, 0)||' rows to parent'); END IF; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table UNINHERITED, not DROPPED. Copied '||coalesce(v_rowcount, 0)||' rows to parent'); END IF; END IF; v_undo_count := v_undo_count + 1; CONTINUE; END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing child partition: '||v_child_table); END IF; v_copy_sql := 'INSERT INTO '||p_parent_table||' SELECT * FROM '||v_child_table; EXECUTE v_copy_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total := v_total + v_rowcount; EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; IF p_keep_table = false THEN EXECUTE 'DROP TABLE '||v_child_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table DROPPED. Moved '||v_rowcount||' rows to parent'); END IF; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table UNINHERITED, not DROPPED. Copied '||v_rowcount||' rows to parent'); END IF; END IF; v_batch_loop_count := v_batch_loop_count + 1; v_undo_count := v_undo_count + 1; END LOOP; IF v_undo_count = 0 THEN -- FOR loop never ran, so there's no child tables left. DELETE FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing config from pg_partman (if it existed)'); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; RAISE NOTICE 'Copied % row(s) from % child table(s) to the parent: %', v_total, v_undo_count, p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Final stats'); PERFORM update_step(v_step_id, 'OK', 'Copied '||v_total||' row(s) from '||v_undo_count||' child table(s) to the parent'); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_total; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN UNDO PARTITIONING: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''Partition function maintenance for table '||p_parent_table||' failed'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; DO $$ DECLARE v_row record; BEGIN FOR v_row IN SELECT statement FROM partman_preserve_privs_temp LOOP IF v_row.statement IS NOT NULL THEN EXECUTE v_row.statement; END IF; END LOOP; END $$; DROP TABLE partman_preserve_privs_temp; pg_partman-2.2.2/updates/pg_partman--1.6.1--1.7.0.sql000066400000000000000000002265121262146621700214360ustar00rootroot00000000000000-- 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 partitoning 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 ALTER TABLE @extschema@.part_config ADD COLUMN use_run_maintenance BOOLEAN NOT NULL DEFAULT true; UPDATE @extschema@.part_config SET use_run_maintenance = false WHERE type = 'id-static' OR type = 'id-dynamic'; CREATE TEMP TABLE partman_preserve_privs_temp (statement text); INSERT INTO partman_preserve_privs_temp SELECT 'GRANT EXECUTE ON FUNCTION @extschema@.create_parent(text, text, text, text, text[], int, boolean, text, boolean, boolean) TO '||array_to_string(array_agg(grantee::text), ',')||';' FROM information_schema.routine_privileges WHERE routine_schema = '@extschema@' AND routine_name = 'create_parent'; INSERT INTO partman_preserve_privs_temp SELECT 'GRANT EXECUTE ON FUNCTION @extschema@.apply_constraints(text, text, boolean, boolean) TO '||array_to_string(array_agg(grantee::text), ',')||';' FROM information_schema.routine_privileges WHERE routine_schema = '@extschema@' AND routine_name = 'apply_constraints'; DROP FUNCTION create_parent(text, text, text, text, text[], int, text, boolean, boolean); DROP FUNCTION apply_constraints(text, text, boolean); /* * Trigger function to enforce that time based partitioning must use run_maintenance() */ CREATE FUNCTION time_partition_maintenance_true_trig() RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER AS $$ BEGIN IF TG_OP = 'INSERT' OR TG_OP = 'UPDATE' THEN IF NEW.type IN ('time-static', 'time-dynamic', 'time-custom') AND NEW.use_run_maintenance = FALSE THEN RAISE EXCEPTION 'use_run_maintenance cannot be set to FALSE for time based partitioning'; END IF; END IF; RETURN NEW; END $$; CREATE TRIGGER time_partition_maintenance_true_trig BEFORE INSERT OR UPDATE OF type, use_run_maintenance ON @extschema@.part_config FOR EACH ROW EXECUTE PROCEDURE @extschema@.time_partition_maintenance_true_trig(); /* * Populate the child table(s) of an id-based partition set with old data from the original parent */ CREATE OR REPLACE FUNCTION partition_data_id(p_parent_table text, p_batch_count int DEFAULT 1, p_batch_interval int DEFAULT NULL, p_lock_wait numeric DEFAULT 0, p_order text DEFAULT 'ASC') RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_control text; v_current_partition_name text; v_lock_iter int := 1; v_lock_obtained boolean := FALSE; v_max_partition_id bigint; v_min_partition_id bigint; v_part_interval bigint; v_partition_id bigint[]; v_rowcount bigint; v_sql text; v_start_control bigint; v_total_rows bigint := 0; v_type text; BEGIN SELECT type , part_interval::bigint , control INTO v_type , v_part_interval , v_control FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF p_batch_interval IS NULL OR p_batch_interval > v_part_interval THEN p_batch_interval := v_part_interval; END IF; FOR i IN 1..p_batch_count LOOP IF p_order = 'ASC' THEN EXECUTE 'SELECT min('||v_control||') FROM ONLY '||p_parent_table INTO v_start_control; IF v_start_control IS NULL THEN EXIT; END IF; v_min_partition_id = v_start_control - (v_start_control % v_part_interval); v_partition_id := ARRAY[v_min_partition_id]; -- Check if custom batch interval overflows current partition maximum IF (v_start_control + p_batch_interval) >= (v_min_partition_id + v_part_interval) THEN v_max_partition_id := v_min_partition_id + v_part_interval; ELSE v_max_partition_id := v_start_control + p_batch_interval; END IF; ELSIF p_order = 'DESC' THEN EXECUTE 'SELECT max('||v_control||') FROM ONLY '||p_parent_table INTO v_start_control; IF v_start_control IS NULL THEN EXIT; END IF; v_min_partition_id = v_start_control - (v_start_control % v_part_interval); -- Must be greater than max value still in parent table since query below grabs < max v_max_partition_id := v_min_partition_id + v_part_interval; v_partition_id := ARRAY[v_min_partition_id]; -- Make sure minimum doesn't underflow current partition minimum IF (v_start_control - p_batch_interval) >= v_min_partition_id THEN v_min_partition_id = v_start_control - p_batch_interval; END IF; ELSE RAISE EXCEPTION 'Invalid value for p_order. Must be ASC or DESC'; END IF; -- do some locking with timeout, if required IF p_lock_wait > 0 THEN v_lock_iter := 0; WHILE v_lock_iter <= 5 LOOP v_lock_iter := v_lock_iter + 1; BEGIN v_sql := 'SELECT * FROM ONLY ' || p_parent_table || ' WHERE '||v_control||' >= '||quote_literal(v_min_partition_id)|| ' AND '||v_control||' < '||quote_literal(v_max_partition_id) ||' FOR UPDATE NOWAIT'; EXECUTE v_sql; 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 RETURN -1; END IF; END IF; v_current_partition_name := @extschema@.create_id_partition(p_parent_table, v_partition_id); EXECUTE 'WITH partition_data AS ( DELETE FROM ONLY '||p_parent_table||' WHERE '||v_control||' >= '||v_min_partition_id|| ' AND '||v_control||' < '||v_max_partition_id||' RETURNING *) INSERT INTO '||v_current_partition_name||' SELECT * FROM partition_data'; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total_rows := v_total_rows + v_rowcount; IF v_rowcount = 0 THEN EXIT; END IF; END LOOP; IF v_type = 'id-static' THEN PERFORM @extschema@.create_id_function(p_parent_table); END IF; RETURN v_total_rows; END $$; /* * Populate the child table(s) of a time-based partition set with old data from the original parent */ CREATE OR REPLACE FUNCTION 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') RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_control text; v_datetime_string text; v_current_partition_name text; v_max_partition_timestamp timestamp; v_last_partition text; v_lock_iter int := 1; v_lock_obtained boolean := FALSE; v_min_partition_timestamp timestamp; v_part_interval interval; v_partition_timestamp timestamp[]; v_rowcount bigint; v_sql text; v_start_control timestamp; v_time_position int; v_total_rows bigint := 0; v_type text; BEGIN SELECT type , part_interval::interval , control , datetime_string , last_partition INTO v_type , v_part_interval , v_control , v_datetime_string , v_last_partition FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic' OR type = 'time-custom'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF p_batch_interval IS NULL OR p_batch_interval > v_part_interval THEN p_batch_interval := v_part_interval; END IF; FOR i IN 1..p_batch_count LOOP IF p_order = 'ASC' THEN EXECUTE 'SELECT min('||v_control||') FROM ONLY '||p_parent_table INTO v_start_control; ELSIF p_order = 'DESC' THEN EXECUTE 'SELECT max('||v_control||') FROM ONLY '||p_parent_table INTO v_start_control; ELSE RAISE EXCEPTION 'Invalid value for p_order. Must be ASC or DESC'; END IF; IF v_start_control IS NULL THEN EXIT; END IF; IF v_type = 'time-static' OR v_type = 'time-dynamic' THEN CASE WHEN v_part_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_part_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_part_interval = '1 hour' THEN v_min_partition_timestamp := date_trunc('hour', v_start_control); WHEN v_part_interval = '1 day' THEN v_min_partition_timestamp := date_trunc('day', v_start_control); WHEN v_part_interval = '1 week' THEN v_min_partition_timestamp := date_trunc('week', v_start_control); WHEN v_part_interval = '1 month' THEN v_min_partition_timestamp := date_trunc('month', v_start_control); WHEN v_part_interval = '3 months' THEN v_min_partition_timestamp := date_trunc('quarter', v_start_control); WHEN v_part_interval = '1 year' THEN v_min_partition_timestamp := date_trunc('year', v_start_control); END CASE; ELSIF v_type = 'time-custom' THEN -- Keep going backwards, checking if the time interval encompases the current v_start_control value v_time_position := (length(v_last_partition) - position('p_' in reverse(v_last_partition))) + 2; v_min_partition_timestamp := to_timestamp(substring(v_last_partition from v_time_position), v_datetime_string); v_max_partition_timestamp := v_min_partition_timestamp + v_part_interval; LOOP IF v_start_control >= v_min_partition_timestamp AND v_start_control < v_max_partition_timestamp THEN EXIT; ELSE v_max_partition_timestamp := v_min_partition_timestamp; BEGIN v_min_partition_timestamp := v_min_partition_timestamp - v_part_interval; 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_interval; END; END IF; END LOOP; END IF; v_partition_timestamp := ARRAY[v_min_partition_timestamp]; IF p_order = 'ASC' THEN IF (v_start_control + p_batch_interval) >= (v_min_partition_timestamp + v_part_interval) THEN v_max_partition_timestamp := v_min_partition_timestamp + v_part_interval; ELSE v_max_partition_timestamp := v_start_control + p_batch_interval; END IF; ELSIF p_order = 'DESC' THEN -- Must be greater than max value still in parent table since query below grabs < max v_max_partition_timestamp := v_min_partition_timestamp + v_part_interval; -- Make sure minimum doesn't underflow current partition minimum IF (v_start_control - p_batch_interval) >= v_min_partition_timestamp THEN v_min_partition_timestamp = v_start_control - p_batch_interval; END IF; ELSE RAISE EXCEPTION 'Invalid value for p_order. Must be ASC or DESC'; END IF; -- do some locking with timeout, if required IF p_lock_wait > 0 THEN v_lock_iter := 0; WHILE v_lock_iter <= 5 LOOP v_lock_iter := v_lock_iter + 1; BEGIN v_sql := 'SELECT * FROM ONLY ' || p_parent_table || ' WHERE '||v_control||' >= '||quote_literal(v_min_partition_timestamp)|| ' AND '||v_control||' < '||quote_literal(v_max_partition_timestamp) ||' FOR UPDATE NOWAIT'; EXECUTE v_sql; 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 RETURN -1; END IF; END IF; v_current_partition_name := @extschema@.create_time_partition(p_parent_table, v_partition_timestamp); EXECUTE 'WITH partition_data AS ( DELETE FROM ONLY '||p_parent_table||' WHERE '||v_control||' >= '||quote_literal(v_min_partition_timestamp)|| ' AND '||v_control||' < '||quote_literal(v_max_partition_timestamp)||' RETURNING *) INSERT INTO '||v_current_partition_name||' SELECT * FROM partition_data'; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total_rows := v_total_rows + v_rowcount; IF v_rowcount = 0 THEN EXIT; END IF; END LOOP; IF v_type = 'time-static' THEN PERFORM @extschema@.create_time_function(p_parent_table); END IF; RETURN v_total_rows; END $$; /* * Function to manage pre-creation of the next partitions in a time-based partition set. * Also manages dropping old partitions if the retention option is set. */ CREATE OR REPLACE FUNCTION run_maintenance(p_jobmon BOOLEAN DEFAULT true) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_create_count int := 0; v_current_partition text; v_current_partition_id bigint; v_current_partition_timestamp timestamp; v_datetime_string text; v_drop_count int := 0; v_id_position int; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_last_partition text; v_last_partition_id bigint; v_last_partition_timestamp timestamp; v_next_partition_id bigint; v_next_partition_timestamp timestamp; v_old_search_path text; v_premade_count int; v_quarter text; v_step_id bigint; v_step_overflow_id bigint; v_step_serial_id bigint; v_row record; v_tablename text; v_time_position int; v_year text; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman run_maintenance')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'Partman maintenance already running.'; RETURN; END IF; IF p_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN RUN MAINTENANCE'); v_step_id := add_step(v_job_id, 'Running maintenance loop'); END IF; FOR v_row IN SELECT parent_table , type , part_interval , control , premake , datetime_string , last_partition , undo_in_progress FROM @extschema@.part_config WHERE use_run_maintenance = true LOOP CONTINUE WHEN v_row.undo_in_progress; -- Double check that last created partition exists IF v_row.last_partition IS NOT NULL THEN SELECT tablename INTO v_tablename FROM pg_tables WHERE schemaname || '.' || tablename = v_row.last_partition; IF v_tablename IS NULL THEN RAISE EXCEPTION 'Last known partition table missing for parent table %. Unable to determine next partition in sequence.', v_row.parent_table; END IF; ELSE RAISE EXCEPTION 'Last known partition missing from config table for parent table %.', p_parent_table; END IF; IF v_row.type = 'time-static' OR v_row.type = 'time-dynamic' OR v_row.type = 'time-custom' THEN IF v_row.type = 'time-static' OR v_row.type = 'time-dynamic' THEN CASE WHEN v_row.part_interval::interval = '15 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0); WHEN v_row.part_interval::interval = '30 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0); WHEN v_row.part_interval::interval = '1 hour' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP); WHEN v_row.part_interval::interval = '1 day' THEN v_current_partition_timestamp := date_trunc('day', CURRENT_TIMESTAMP); WHEN v_row.part_interval::interval = '1 week' THEN v_current_partition_timestamp := date_trunc('week', CURRENT_TIMESTAMP); WHEN v_row.part_interval::interval = '1 month' THEN v_current_partition_timestamp := date_trunc('month', CURRENT_TIMESTAMP); WHEN v_row.part_interval::interval = '3 months' THEN v_current_partition_timestamp := date_trunc('quarter', CURRENT_TIMESTAMP); WHEN v_row.part_interval::interval = '1 year' THEN v_current_partition_timestamp := date_trunc('year', CURRENT_TIMESTAMP); END CASE; ELSIF v_row.type = 'time-custom' THEN SELECT child_table INTO v_current_partition FROM @extschema@.custom_time_partitions WHERE parent_table = v_row.parent_table AND partition_range @> CURRENT_TIMESTAMP; IF v_current_partition IS NULL THEN RAISE EXCEPTION 'Current time partition missing from custom_time_partitions config table for table % and timestamp %', CURRENT_TIMESTAMP, v_row.parent_table; END IF; v_time_position := (length(v_current_partition) - position('p_' in reverse(v_current_partition))) + 2; v_current_partition_timestamp := to_timestamp(substring(v_current_partition from v_time_position), v_row.datetime_string); END IF; v_time_position := (length(v_row.last_partition) - position('p_' in reverse(v_row.last_partition))) + 2; IF v_row.part_interval::interval <> '3 months' OR (v_row.part_interval::interval = '3 months' AND v_row.type = 'time-custom') THEN v_last_partition_timestamp := to_timestamp(substring(v_row.last_partition from v_time_position), v_row.datetime_string); ELSE -- to_timestamp doesn't recognize 'Q' date string formater. Handle it v_year := split_part(substring(v_row.last_partition from v_time_position), 'q', 1); v_quarter := split_part(substring(v_row.last_partition from v_time_position), 'q', 2); CASE WHEN v_quarter = '1' THEN v_last_partition_timestamp := to_timestamp(v_year || '-01-01', 'YYYY-MM-DD'); WHEN v_quarter = '2' THEN v_last_partition_timestamp := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); WHEN v_quarter = '3' THEN v_last_partition_timestamp := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); WHEN v_quarter = '4' THEN v_last_partition_timestamp := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); END CASE; END IF; -- Check and see how many premade partitions there are. v_premade_count = round(EXTRACT('epoch' FROM age(v_last_partition_timestamp, v_current_partition_timestamp)) / EXTRACT('epoch' FROM v_row.part_interval::interval)); v_next_partition_timestamp := v_last_partition_timestamp; -- 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 BEGIN v_next_partition_timestamp := v_next_partition_timestamp + v_row.part_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 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', 'Child partition creation skippd for parent table '||v_partition_time); END IF; RAISE WARNING 'Attempted partition time interval is outside PostgreSQL''s supported time range. Child partition creation skipped for parent table %', v_row.parent_table; CONTINUE; END; v_last_partition := @extschema@.create_time_partition(v_row.parent_table, ARRAY[v_next_partition_timestamp]); IF v_last_partition IS NOT NULL THEN UPDATE @extschema@.part_config SET last_partition = v_last_partition WHERE parent_table = v_row.parent_table; END IF; v_create_count := v_create_count + 1; IF v_row.type = 'time-static' THEN PERFORM @extschema@.create_time_function(v_row.parent_table); END IF; -- Manage additonal constraints if set PERFORM @extschema@.apply_constraints(v_row.parent_table); v_premade_count = round(EXTRACT('epoch' FROM age(v_next_partition_timestamp, v_current_partition_timestamp)) / EXTRACT('epoch' FROM v_row.part_interval::interval)); END LOOP; ELSIF v_row.type = 'id-static' OR v_row.type ='id-dynamic' THEN -- EXECUTE 'SELECT max('||v_row.control||') - ('||v_row.control||' % '||v_row.part_interval::int||') FROM '||v_row.parent_table INTO v_current_partition_id; EXECUTE 'SELECT '||v_row.control||' - ('||v_row.control||' % '||v_row.part_interval::int||') FROM '||v_row.parent_table||' WHERE '||v_row.control||' = (SELECT max('||v_row.control||') FROM '||v_row.parent_table||')' INTO v_current_partition_id; v_id_position := (length(v_row.last_partition) - position('p_' in reverse(v_row.last_partition))) + 2; v_last_partition_id = substring(v_row.last_partition from v_id_position)::bigint; -- This catches if there's invalid data in a parent table set that's outside all child table ranges. IF v_last_partition_id < v_current_partition_id THEN IF v_jobmon_schema IS NOT NULL THEN v_step_serial_id := add_step(v_job_id, 'Found inconsistent data in serial partition set.'); PERFORM update_step(v_step_serial_id, 'CRITICAL', 'Child partition creation skipped for parent table '||v_row.parent_table||'. Current max serial id value ('||v_current_partition_id||') is greater than the id range covered by the last partition created ('||v_row.last_partition||'). Run check_parent() to find possible cause.'); END IF; RAISE WARNING 'Child partition creation skipped for parent table %. Found inconsistent data in serial partition set. Current max serial id value (%) is greater than the id range covered by the last partition created (%). Run check_parent() to find possible cause.', v_row.parent_table, v_current_partition_id, v_row.last_partition; CONTINUE; END IF; v_next_partition_id := v_last_partition_id + v_row.part_interval::bigint; WHILE ((v_next_partition_id - v_current_partition_id) / v_row.part_interval::bigint) <= v_row.premake LOOP v_last_partition := @extschema@.create_id_partition(v_row.parent_table, ARRAY[v_next_partition_id]); IF v_last_partition IS NOT NULL THEN UPDATE @extschema@.part_config SET last_partition = v_last_partition WHERE parent_table = v_row.parent_table; PERFORM @extschema@.create_id_function(v_row.parent_table); PERFORM @extschema@.apply_constraints(v_row.parent_table); END IF; v_next_partition_id := v_next_partition_id + v_row.part_interval::bigint; END LOOP; END IF; -- end main IF check for time or id END LOOP; -- end of creation loop -- Manage dropping old partitions if retention option is set FOR v_row IN SELECT parent_table FROM @extschema@.part_config WHERE retention IS NOT NULL AND undo_in_progress = false AND (type = 'time-static' OR type = 'time-dynamic' OR type = 'time-custom') LOOP v_drop_count := v_drop_count + @extschema@.drop_partition_time(v_row.parent_table); END LOOP; FOR v_row IN SELECT parent_table FROM @extschema@.part_config WHERE retention IS NOT NULL AND undo_in_progress = false AND (type = 'id-static' OR type = 'id-dynamic') LOOP v_drop_count := v_drop_count + @extschema@.drop_partition_id(v_row.parent_table); END LOOP; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Partition maintenance finished. '||v_create_count||' partitons made. '||v_drop_count||' partitions dropped.'); IF v_step_overflow_id IS NOT NULL OR v_step_serial_id IS NOT NULL THEN PERFORM fail_job(v_job_id); ELSE PERFORM close_job(v_job_id); END IF; EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN RUN MAINTENANCE'')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to turn a table into the parent of a partition set */ CREATE FUNCTION create_parent( p_parent_table text , p_control text , p_type text , p_interval text , p_constraint_cols text[] DEFAULT NULL , p_premake int DEFAULT 4 , p_use_run_maintenance boolean DEFAULT NULL , p_start_partition text DEFAULT NULL , p_jobmon boolean DEFAULT true , p_debug boolean DEFAULT false) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_base_timestamp timestamp; v_count int := 1; v_datetime_string text; v_id_interval bigint; v_job_id bigint; v_jobmon_schema text; v_last_partition_name text; v_old_search_path text; v_partition_time timestamp; v_partition_time_array timestamp[]; v_partition_id bigint[]; v_max bigint; v_notnull boolean; v_run_maint boolean; v_start_time timestamp; v_starting_partition_id bigint; v_step_id bigint; v_step_overflow_id bigint; v_tablename text; v_time_interval interval; BEGIN IF position('.' in p_parent_table) = 0 THEN RAISE EXCEPTION 'Parent table must be schema qualified'; END IF; SELECT tablename INTO v_tablename FROM pg_tables WHERE schemaname || '.' || tablename = p_parent_table; IF v_tablename IS NULL THEN RAISE EXCEPTION 'Please create given parent table first: %', p_parent_table; END IF; SELECT attnotnull INTO v_notnull FROM pg_attribute WHERE attrelid = p_parent_table::regclass AND attname = p_control; IF v_notnull = false OR v_notnull IS NULL THEN RAISE EXCEPTION 'Control column (%) for parent table (%) must be NOT NULL', p_control, p_parent_table; END IF; IF NOT @extschema@.check_partition_type(p_type) THEN RAISE EXCEPTION '% is not a valid partitioning type', p_type; END IF; IF p_type = 'time-custom' AND @extschema@.check_version('9.2.0') IS FALSE THEN RAISE EXCEPTION 'The "time-custom" type requires a minimum PostgreSQL version of 9.2.0'; END IF; EXECUTE 'LOCK TABLE '||p_parent_table||' IN ACCESS EXCLUSIVE MODE'; IF p_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF p_use_run_maintenance IS NOT NULL THEN IF p_use_run_maintenance IS FALSE AND (p_type = 'time-static' OR p_type = 'time-dynamic' OR p_type = 'time-custom') THEN RAISE EXCEPTION 'p_run_maintenance cannot be set to false for time based partitioning'; END IF; v_run_maint := p_use_run_maintenance; ELSIF p_type = 'time-static' OR p_type = 'time-dynamic' OR p_type = 'time-custom' THEN v_run_maint := TRUE; ELSIF p_type = 'id-static' OR p_type ='id-dynamic' THEN v_run_maint := FALSE; ELSE RAISE EXCEPTION 'use_run_maintenance value cannot be set NULL'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN SETUP PARENT: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating initial partitions on new parent table: '||p_parent_table); END IF; IF p_type = 'time-static' OR p_type = 'time-dynamic' OR p_type = 'time-custom' 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 <> 'time-custom' THEN RAISE EXCEPTION 'Must use a predefined time interval if not using type "time-custom". See documentation.'; 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; -- First partition is either the min premake or p_start_partition v_start_time := COALESCE(p_start_partition::timestamp, CURRENT_TIMESTAMP - (v_time_interval * p_premake)); 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 v_datetime_string := 'YYYY'; 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 v_partition_time_array := array_append(v_partition_time_array, v_base_timestamp); LOOP -- If current loop value is less than or equal to the value of the max premake, add time to array. IF (v_base_timestamp + (v_time_interval * v_count)) < (CURRENT_TIMESTAMP + (v_time_interval * p_premake)) THEN BEGIN v_partition_time := (v_base_timestamp + (v_time_interval * v_count))::timestamp; 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. 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'); CONTINUE; END; ELSE EXIT; -- all needed partitions added to array. Exit the loop. END IF; v_count := v_count + 1; END LOOP; INSERT INTO @extschema@.part_config (parent_table, type, part_interval, control, premake, constraint_cols, datetime_string, use_run_maintenance, jobmon) VALUES (p_parent_table, p_type, v_time_interval, p_control, p_premake, p_constraint_cols, v_datetime_string, v_run_maint, p_jobmon); v_last_partition_name := @extschema@.create_time_partition(p_parent_table, v_partition_time_array); -- Doing separate update because create function requires in config table last_partition to be set UPDATE @extschema@.part_config SET last_partition = v_last_partition_name WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Time partitions premade: '||p_premake); END IF; END IF; IF p_type = 'id-static' OR p_type = 'id-dynamic' THEN v_id_interval := p_interval::bigint; IF v_id_interval <= 1 THEN RAISE EXCEPTION 'Interval for serial partitioning must be greater than 1'; END IF; -- If custom start partition is set, use that. -- If custom start is not set and there is already data, start partitioning with the highest current value EXECUTE 'SELECT COALESCE('||quote_nullable(p_start_partition::bigint)||', max('||p_control||')::bigint, 0) FROM '||p_parent_table||' LIMIT 1' INTO v_max; 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 THEN v_partition_id = array_append(v_partition_id, (v_starting_partition_id - v_id_interval*i)); END IF; v_partition_id = array_append(v_partition_id, (v_id_interval*i) + v_starting_partition_id); END LOOP; INSERT INTO @extschema@.part_config (parent_table, type, part_interval, control, premake, constraint_cols, use_run_maintenance, jobmon) VALUES (p_parent_table, p_type, v_id_interval, p_control, p_premake, p_constraint_cols, v_run_maint, p_jobmon); v_last_partition_name := @extschema@.create_id_partition(p_parent_table, v_partition_id); -- Doing separate update because create function needs parent table in config table for apply_grants() UPDATE @extschema@.part_config SET last_partition = v_last_partition_name WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'ID partitions premade: '||p_premake); END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Creating partition function'); END IF; IF p_type = 'time-static' OR p_type = 'time-dynamic' OR p_type = 'time-custom' THEN PERFORM @extschema@.create_time_function(p_parent_table); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Time function created'); END IF; ELSIF p_type = 'id-static' OR p_type = 'id-dynamic' THEN PERFORM @extschema@.create_id_function(p_parent_table); 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); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); IF v_step_overflow_id IS NOT NULL THEN PERFORM fail_job(v_job_id); ELSE PERFORM close_job(v_job_id); END IF; EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN CREATE PARENT: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''Partition creation for table '||p_parent_table||' failed'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Create the trigger function for the parent table of an id-based partition set */ CREATE OR REPLACE FUNCTION create_id_function(p_parent_table text) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_control text; v_count int; v_current_partition_name text; v_current_partition_id bigint; v_datetime_string text; v_final_partition_id bigint; v_function_name text; v_job_id bigint; v_jobmon text; v_jobmon_schema text; v_last_partition text; v_max bigint; v_next_partition_id bigint; v_next_partition_name text; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_part_interval bigint; v_premake int; v_prev_partition_id bigint; v_prev_partition_name text; v_run_maint boolean; v_step_id bigint; v_trig_func text; v_type text; BEGIN SELECT type , part_interval::bigint , control , premake , last_partition , use_run_maintenance , jobmon INTO v_type , v_part_interval , v_control , v_premake , v_last_partition , v_run_maint , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE FUNCTION: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating partition function for table '||p_parent_table); END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; v_function_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, '_part_trig_func', FALSE); IF v_type = 'id-static' THEN EXECUTE 'SELECT COALESCE(max('||v_control||'), 0) FROM '||p_parent_table INTO v_max; v_current_partition_id = v_max - (v_max % v_part_interval); v_next_partition_id := v_current_partition_id + v_part_interval; v_current_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_current_partition_id::text, TRUE); v_trig_func := 'CREATE OR REPLACE FUNCTION '||v_function_name||'() RETURNS trigger LANGUAGE plpgsql AS $t$ DECLARE v_current_partition_id bigint; v_last_partition text := '||quote_literal(v_last_partition)||'; v_id_position int; v_next_partition_id bigint; v_next_partition_name text; BEGIN IF TG_OP = ''INSERT'' THEN IF NEW.'||v_control||' >= '||v_current_partition_id||' AND NEW.'||v_control||' < '||v_next_partition_id|| ' THEN INSERT INTO '||v_current_partition_name||' VALUES (NEW.*); '; FOR i IN 1..v_premake LOOP v_prev_partition_id := v_current_partition_id - (v_part_interval * i); v_next_partition_id := v_current_partition_id + (v_part_interval * i); v_final_partition_id := v_next_partition_id + v_part_interval; v_prev_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_prev_partition_id::text, TRUE); v_next_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_next_partition_id::text, TRUE); -- Check that child table exist before making a rule to insert to them. -- Handles edge case of changing premake immediately after running create_parent(). SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname ||'.'||tablename = v_prev_partition_name; IF v_count > 0 THEN -- Only handle previous partitions if they're starting above zero IF v_prev_partition_id >= 0 THEN v_trig_func := v_trig_func ||' ELSIF NEW.'||v_control||' >= '||v_prev_partition_id||' AND NEW.'||v_control||' < '||v_prev_partition_id + v_part_interval|| ' THEN INSERT INTO '||v_prev_partition_name||' VALUES (NEW.*); '; END IF; END IF; SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname ||'.'||tablename = v_next_partition_name; IF v_count > 0 THEN v_trig_func := v_trig_func ||' ELSIF NEW.'||v_control||' >= '||v_next_partition_id||' AND NEW.'||v_control||' < '||v_final_partition_id|| ' THEN INSERT INTO '||v_next_partition_name||' VALUES (NEW.*);'; END IF; END LOOP; v_trig_func := v_trig_func ||' ELSE RETURN NEW; END IF;'; IF v_run_maint IS FALSE THEN v_trig_func := v_trig_func ||' v_current_partition_id := NEW.'||v_control||' - (NEW.'||v_control||' % '||v_part_interval||'); IF (NEW.'||v_control||' % '||v_part_interval||') > ('||v_part_interval||' / 2) THEN v_id_position := (length(v_last_partition) - position(''p_'' in reverse(v_last_partition))) + 2; v_next_partition_id := (substring(v_last_partition from v_id_position)::bigint) + '||v_part_interval||'; WHILE ((v_next_partition_id - v_current_partition_id) / '||v_part_interval||') <= '||v_premake||' LOOP v_next_partition_name := @extschema@.create_id_partition('||quote_literal(p_parent_table)||', ARRAY[v_next_partition_id]); UPDATE @extschema@.part_config SET last_partition = v_next_partition_name WHERE parent_table = '||quote_literal(p_parent_table)||'; PERFORM @extschema@.create_id_function('||quote_literal(p_parent_table)||'); PERFORM @extschema@.apply_constraints('||quote_literal(p_parent_table)||'); v_next_partition_id := v_next_partition_id + '||v_part_interval||'; END LOOP; END IF;'; END IF; v_trig_func := v_trig_func ||' END IF; RETURN NULL; END $t$;'; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Added function for current id interval: '||v_current_partition_id||' to '||v_final_partition_id-1); END IF; ELSIF v_type = 'id-dynamic' THEN -- The return inside the partition creation check is there to keep really high ID values from creating new partitions. v_trig_func := 'CREATE OR REPLACE FUNCTION '||v_function_name||'() RETURNS trigger LANGUAGE plpgsql AS $t$ DECLARE v_count int; v_current_partition_id bigint; v_current_partition_name text; v_id_position int; v_last_partition text := '||quote_literal(v_last_partition)||'; v_last_partition_id bigint; v_next_partition_id bigint; v_next_partition_name text; BEGIN IF TG_OP = ''INSERT'' THEN v_current_partition_id := NEW.'||v_control||' - (NEW.'||v_control||' % '||v_part_interval||'); v_current_partition_name := @extschema@.check_name_length('''||v_parent_tablename||''', '''||v_parent_schema||''', v_current_partition_id::text, TRUE); SELECT count(*) INTO v_count FROM pg_tables WHERE schemaname ||''.''|| tablename = v_current_partition_name; IF v_count > 0 THEN EXECUTE ''INSERT INTO ''||v_current_partition_name||'' VALUES($1.*)'' USING NEW; ELSE RETURN NEW; END IF;'; IF v_run_maint IS FALSE THEN v_trig_func := v_trig_func ||' IF (NEW.'||v_control||' % '||v_part_interval||') > ('||v_part_interval||' / 2) THEN v_id_position := (length(v_last_partition) - position(''p_'' in reverse(v_last_partition))) + 2; v_last_partition_id = substring(v_last_partition from v_id_position)::bigint; v_next_partition_id := v_last_partition_id + '||v_part_interval||'; IF NEW.'||v_control||' >= v_next_partition_id THEN RETURN NEW; END IF; WHILE ((v_next_partition_id - v_current_partition_id) / '||v_part_interval||') <= '||v_premake||' LOOP v_next_partition_name := @extschema@.create_id_partition('||quote_literal(p_parent_table)||', ARRAY[v_next_partition_id]); IF v_next_partition_name IS NOT NULL THEN UPDATE @extschema@.part_config SET last_partition = v_next_partition_name WHERE parent_table = '||quote_literal(p_parent_table)||'; PERFORM @extschema@.create_id_function('||quote_literal(p_parent_table)||'); PERFORM @extschema@.apply_constraints('||quote_literal(p_parent_table)||'); END IF; v_next_partition_id := v_next_partition_id + '||v_part_interval||'; END LOOP; END IF;'; END IF; v_trig_func := v_trig_func ||' END IF; RETURN NULL; END $t$;'; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Added function for dynamic id table: '||p_parent_table); END IF; ELSE RAISE EXCEPTION 'ERROR: Invalid id partitioning type given: %', v_type; END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN CREATE FUNCTION: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''Partition function maintenance for table '||p_parent_table||' failed'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to create id partitions */ CREATE OR REPLACE FUNCTION create_id_partition (p_parent_table text, p_partition_ids bigint[]) RETURNS text LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_all text[] := ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER']; v_analyze boolean := FALSE; v_control text; v_grantees text[]; v_hasoids boolean; v_id bigint; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_parent_grant record; v_parent_owner text; v_parent_schema text; v_parent_tablename text; v_parent_tablespace text; v_part_interval bigint; v_partition_name text; v_revoke text[]; v_sql text; v_step_id bigint; v_tablename text; BEGIN SELECT control , part_interval , jobmon INTO v_control , v_part_interval , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; SELECT tableowner, schemaname, tablename, tablespace INTO v_parent_owner, v_parent_schema, v_parent_tablename, v_parent_tablespace FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; FOREACH v_id IN ARRAY p_partition_ids LOOP v_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_id::text, TRUE); -- If child table already exists, skip creation SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_tablename IS NOT NULL 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_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating new partition '||v_partition_name||' with interval from '||v_id||' to '||(v_id + v_part_interval)-1); END IF; v_sql := 'CREATE TABLE '||v_partition_name||' (LIKE '||p_parent_table||' INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING STORAGE INCLUDING COMMENTS)'; SELECT relhasoids INTO v_hasoids FROM pg_class WHERE oid::regclass = p_parent_table::regclass; IF v_hasoids IS TRUE THEN v_sql := v_sql || ' WITH (OIDS)'; END IF; EXECUTE v_sql; SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_parent_tablespace IS NOT NULL THEN EXECUTE 'ALTER TABLE '||v_partition_name||' SET TABLESPACE '||v_parent_tablespace; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' ADD CONSTRAINT '||v_tablename||'_partition_check CHECK ('||v_control||'>='||quote_literal(v_id)||' AND '||v_control||'<'||quote_literal(v_id + v_part_interval)||')'; EXECUTE 'ALTER TABLE '||v_partition_name||' INHERIT '||p_parent_table; FOR v_parent_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = p_parent_table GROUP BY grantee LOOP EXECUTE 'GRANT '||array_to_string(v_parent_grant.types, ',')||' ON '||v_partition_name||' TO '||v_parent_grant.grantee; SELECT array_agg(r) INTO v_revoke FROM (SELECT unnest(v_all) AS r EXCEPT SELECT unnest(v_parent_grant.types)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE '||array_to_string(v_revoke, ',')||' ON '||v_partition_name||' FROM '||v_parent_grant.grantee||' CASCADE'; END IF; v_grantees := array_append(v_grantees, v_parent_grant.grantee::text); END LOOP; -- Revoke all privileges from roles that have none on the parent IF v_grantees IS NOT NULL THEN SELECT array_agg(r) INTO v_revoke FROM ( SELECT DISTINCT grantee::text AS r FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = v_partition_name EXCEPT SELECT unnest(v_grantees)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE ALL ON '||v_partition_name||' FROM '||array_to_string(v_revoke, ','); END IF; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' OWNER TO '||v_parent_owner; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); PERFORM close_job(v_job_id); END IF; END LOOP; IF v_analyze THEN EXECUTE 'ANALYZE '||p_parent_table; END IF; IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_partition_name; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN CREATE TABLE: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to create a child table in a time-based partition set */ CREATE OR REPLACE FUNCTION create_time_partition (p_parent_table text, p_partition_times timestamp[]) RETURNS text LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_all text[] := ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER']; v_analyze boolean := FALSE; v_control text; v_grantees text[]; v_hasoids boolean; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_parent_grant record; v_parent_owner text; v_parent_schema text; v_parent_tablename text; v_partition_name text; v_partition_suffix text; v_parent_tablespace text; v_part_interval interval; v_partition_timestamp_end timestamp; v_partition_timestamp_start timestamp; v_quarter text; v_revoke text[]; v_sql text; v_step_id bigint; v_step_overflow_id bigint; v_tablename text; v_trunc_value text; v_time timestamp; v_type text; v_year text; BEGIN SELECT type , control , part_interval , jobmon INTO v_type , v_control , v_part_interval , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic' OR type = 'time-custom'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; SELECT tableowner, schemaname, tablename, tablespace INTO v_parent_owner, v_parent_schema, v_parent_tablename, v_parent_tablespace FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; FOREACH v_time IN ARRAY p_partition_times LOOP v_partition_suffix := to_char(v_time, 'YYYY'); IF v_part_interval < '1 year' AND v_part_interval <> '1 week' THEN v_partition_suffix := v_partition_suffix ||'_'|| to_char(v_time, 'MM'); IF v_part_interval < '1 month' AND v_part_interval <> '1 week' THEN v_partition_suffix := v_partition_suffix ||'_'|| to_char(v_time, 'DD'); IF v_part_interval < '1 day' THEN v_partition_suffix := v_partition_suffix || '_' || to_char(v_time, 'HH24MI'); IF v_part_interval < '1 minute' THEN v_partition_suffix := v_partition_suffix || to_char(v_time, 'SS'); END IF; -- end < minute IF END IF; -- end < day IF END IF; -- end < month IF END IF; -- end < year IF v_partition_timestamp_start := v_time; BEGIN v_partition_timestamp_end := v_time + v_part_interval; EXCEPTION WHEN datetime_field_overflow THEN 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'); CONTINUE; END; IF v_part_interval = '1 week' THEN v_partition_suffix := to_char(v_time, 'IYYY') || 'w' || to_char(v_time, 'IW'); END IF; -- "Q" is ignored in to_timestamp, so handle special case IF v_part_interval = '3 months' AND (v_type = 'time-static' OR v_type = 'time-dynamic') THEN v_year := to_char(v_time, 'YYYY'); v_quarter := to_char(v_time, 'Q'); v_partition_suffix := v_year || 'q' || v_quarter; END IF; v_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_partition_suffix, TRUE); SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_tablename IS NOT NULL 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_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating new partition '||v_partition_name||' with interval from '||v_partition_timestamp_start||' to '||(v_partition_timestamp_end-'1sec'::interval)); END IF; v_sql := 'CREATE TABLE '||v_partition_name||' (LIKE '||p_parent_table||' INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING STORAGE INCLUDING COMMENTS)'; SELECT relhasoids INTO v_hasoids FROM pg_class WHERE oid::regclass = p_parent_table::regclass; IF v_hasoids IS TRUE THEN v_sql := v_sql || ' WITH (OIDS)'; END IF; EXECUTE v_sql; SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_parent_tablespace IS NOT NULL THEN EXECUTE 'ALTER TABLE '||v_partition_name||' SET TABLESPACE '||v_parent_tablespace; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' ADD CONSTRAINT '||v_tablename||'_partition_check CHECK ('||v_control||'>='||quote_literal(v_partition_timestamp_start)||' AND '||v_control||'<'||quote_literal(v_partition_timestamp_end)||')'; EXECUTE 'ALTER TABLE '||v_partition_name||' INHERIT '||p_parent_table; -- If custom time, set extra config options. IF v_type = 'time-custom' THEN INSERT INTO @extschema@.custom_time_partitions (parent_table, child_table, partition_range) VALUES ( p_parent_table, v_partition_name, tstzrange(v_partition_timestamp_start, v_partition_timestamp_end, '[)') ); END IF; FOR v_parent_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = p_parent_table GROUP BY grantee LOOP EXECUTE 'GRANT '||array_to_string(v_parent_grant.types, ',')||' ON '||v_partition_name||' TO '||v_parent_grant.grantee; SELECT array_agg(r) INTO v_revoke FROM (SELECT unnest(v_all) AS r EXCEPT SELECT unnest(v_parent_grant.types)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE '||array_to_string(v_revoke, ',')||' ON '||v_partition_name||' FROM '||v_parent_grant.grantee||' CASCADE'; END IF; v_grantees := array_append(v_grantees, v_parent_grant.grantee::text); END LOOP; -- Revoke all privileges from roles that have none on the parent IF v_grantees IS NOT NULL THEN SELECT array_agg(r) INTO v_revoke FROM ( SELECT DISTINCT grantee::text AS r FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = v_partition_name EXCEPT SELECT unnest(v_grantees)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE ALL ON '||v_partition_name||' FROM '||array_to_string(v_revoke, ','); END IF; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' OWNER TO '||v_parent_owner; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); IF v_step_overflow_id IS NOT NULL THEN PERFORM fail_job(v_job_id); ELSE PERFORM close_job(v_job_id); END IF; END IF; END LOOP; IF v_analyze THEN EXECUTE 'ANALYZE '||p_parent_table; END IF; IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_partition_name; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN CREATE TABLE: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Apply constraints managed by partman extension */ CREATE FUNCTION apply_constraints(p_parent_table text, p_child_table text DEFAULT NULL, p_analyze boolean DEFAULT TRUE, p_debug boolean DEFAULT FALSE) RETURNS void LANGUAGE plpgsql AS $$ DECLARE v_child_table text; v_child_tablename text; v_col text; v_constraint_cols text[]; v_constraint_col_type text; v_constraint_name text; v_datetime_string text; v_existing_constraint_name text; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_last_partition text; v_last_partition_id int; v_last_partition_timestamp timestamp; v_constraint_values record; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_part_interval text; v_partition_suffix text; v_premake int; v_sql text; v_step_id bigint; v_suffix_position int; v_type text; BEGIN SELECT type , part_interval , premake , datetime_string , last_partition , constraint_cols , jobmon INTO v_type , v_part_interval , v_premake , v_datetime_string , v_last_partition , v_constraint_cols , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_constraint_cols IS NULL THEN IF p_debug THEN RAISE NOTICE 'Given parent table (%) not set up for constraint management (constraint_cols is NULL)', p_parent_table; END IF; -- Returns silently to allow this function to be simply called by maintenance processes without having to check if config options are set. RETURN; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE CONSTRAINT: '||p_parent_table); END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; -- If p_child_table is null, figure out the partition that is the one right before the premake value backwards. IF p_child_table IS NULL THEN IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Automatically determining most recent child on which to apply constraints'); END IF; v_suffix_position := (length(v_last_partition) - position('p_' in reverse(v_last_partition))) + 2; IF v_type IN ('time-static', 'time-dynamic') THEN v_last_partition_timestamp := to_timestamp(substring(v_last_partition from v_suffix_position), v_datetime_string); v_partition_suffix := to_char(v_last_partition_timestamp - (v_part_interval::interval * ((v_premake * 2)+1) ), v_datetime_string); ELSIF v_type IN ('id-static', 'id-dynamic') THEN v_last_partition_id := substring(v_last_partition from v_suffix_position)::int; v_partition_suffix := (v_last_partition_id - (v_part_interval::int * ((v_premake * 2)+1) ))::text; END IF; v_child_table := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_partition_suffix, TRUE); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Target child table: '||v_child_table); END IF; ELSE v_child_table := p_child_table; END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Checking if target child table exists'); END IF; SELECT tablename INTO v_child_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_child_table; IF v_child_tablename IS NULL THEN IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'NOTICE', 'Target child table ('||v_child_table||') does not exist. Skipping constraint creation.'); PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; IF p_debug THEN RAISE NOTICE 'Target child table (%) does not exist. Skipping constraint creation.', v_child_table; END IF; RETURN; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; FOREACH v_col IN ARRAY v_constraint_cols LOOP SELECT c.conname INTO v_existing_constraint_name FROM pg_catalog.pg_constraint c JOIN pg_catalog.pg_attribute a ON c.conrelid = a.attrelid WHERE conrelid = v_child_table::regclass AND c.conname LIKE 'partmanconstr_%' AND c.contype = 'c' AND a.attname = v_col AND ARRAY[a.attnum] <@ c.conkey AND a.attisdropped = false; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Applying new constraint on column: '||v_col); END IF; IF v_existing_constraint_name IS NOT NULL THEN IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'NOTICE', 'Partman managed constraint already exists on this table ('||v_child_table||') and column ('||v_col||'). Skipping creation.'); END IF; RAISE WARNING 'Partman managed constraint already exists on this table (%) and column (%). Skipping creation.', v_child_table, v_col ; CONTINUE; END IF; -- 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 'SELECT min('||v_col||')::text AS min, max('||v_col||')::text AS max FROM '||v_child_table INTO v_constraint_values; IF v_constraint_values IS NOT NULL THEN v_sql := concat('ALTER TABLE ', v_child_table, ' ADD CONSTRAINT ', v_constraint_name , ' CHECK (', v_col, ' >= ', quote_literal(v_constraint_values.min), ' AND ' , v_col, ' <= ', quote_literal(v_constraint_values.max), ')' ); IF p_debug THEN RAISE NOTICE 'Constraint creation query: %', v_sql; END IF; EXECUTE v_sql; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'New constraint created: '||v_sql); END IF; ELSE IF p_debug THEN RAISE NOTICE 'Given column (%) contains all NULLs. No constraint created', v_col; END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'NOTICE', 'Given column ('||v_col||') contains all NULLs. No constraint created'); END IF; END IF; END LOOP; IF p_analyze THEN EXECUTE 'ANALYZE '||p_parent_table; END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN CREATE CONSTRAINT: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; -- Restore dropped object privileges DO $$ DECLARE v_row record; BEGIN FOR v_row IN SELECT statement FROM partman_preserve_privs_temp LOOP IF v_row.statement IS NOT NULL THEN EXECUTE v_row.statement; END IF; END LOOP; END $$; DROP TABLE partman_preserve_privs_temp; pg_partman-2.2.2/updates/pg_partman--1.7.0--1.7.1.sql000066400000000000000000001504121262146621700214320ustar00rootroot00000000000000-- 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 CREATE TEMP TABLE partman_preserve_privs_temp (statement text); INSERT INTO partman_preserve_privs_temp SELECT 'GRANT EXECUTE ON FUNCTION @extschema@.undo_partition_id(text, int, bigint, boolean, numeric) TO '||array_to_string(array_agg(grantee::text), ',')||';' FROM information_schema.routine_privileges WHERE routine_schema = '@extschema@' AND routine_name = 'undo_partition_id'; INSERT INTO partman_preserve_privs_temp SELECT 'GRANT EXECUTE ON FUNCTION @extschema@.undo_partition_time(text, int, interval, boolean, numeric) TO '||array_to_string(array_agg(grantee::text), ',')||';' FROM information_schema.routine_privileges WHERE routine_schema = '@extschema@' AND routine_name = 'undo_partition_time'; INSERT INTO partman_preserve_privs_temp SELECT 'GRANT EXECUTE ON FUNCTION @extschema@.undo_partition(text, int, boolean, boolean, numeric) TO '||array_to_string(array_agg(grantee::text), ',')||';' FROM information_schema.routine_privileges WHERE routine_schema = '@extschema@' AND routine_name = 'undo_partition'; DROP FUNCTION undo_partition_id(text, int, bigint, boolean); DROP FUNCTION undo_partition_time(text, int, interval, boolean); DROP FUNCTION undo_partition(text, int, boolean, boolean); /* * Apply foreign keys that exist on the given parent to the given child table */ CREATE FUNCTION apply_foreign_keys(p_parent_table text, p_child_table text DEFAULT NULL, p_debug boolean DEFAULT false) RETURNS void LANGUAGE plpgsql AS $$ DECLARE v_job_id bigint; v_jobmon text; v_jobmon_schema text; v_old_search_path text; v_ref_schema text; v_ref_table text; v_row record; v_schemaname text; v_sql text; v_step_id bigint; v_tablename text; BEGIN SELECT jobmon INTO v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN APPLYING FOREIGN KEYS: '||p_parent_table); END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Checking if target child table exists'); END IF; SELECT schemaname, tablename INTO v_schemaname, v_tablename FROM pg_catalog.pg_tables WHERE schemaname||'.'||tablename = p_child_table; IF v_tablename IS NULL THEN IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'CRITICAL', 'Target child table ('||v_child_table||') does not exist.'); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION 'Target child table (%.%) does not exist.', v_schemaname, v_tablename; RETURN; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; FOR v_row IN SELECT keys.conname , keys.confrelid::regclass::text AS ref_table , '"'||string_agg(att.attname, '","')||'"' AS ref_column , '"'||string_agg(att2.attname, '","')||'"' AS child_column FROM ( SELECT con.conname , unnest(con.conkey) as ref , unnest(con.confkey) as child , con.confrelid , con.conrelid FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid JOIN pg_catalog.pg_constraint con ON c.oid = con.conrelid WHERE n.nspname ||'.'|| c.relname = p_parent_table AND con.contype = 'f' ORDER BY con.conkey ) keys JOIN pg_catalog.pg_class cl ON cl.oid = keys.confrelid JOIN pg_catalog.pg_attribute att ON att.attrelid = keys.confrelid AND att.attnum = keys.child JOIN pg_catalog.pg_attribute att2 ON att2.attrelid = keys.conrelid AND att2.attnum = keys.ref GROUP BY keys.conname, keys.confrelid LOOP SELECT schemaname, tablename INTO v_ref_schema, v_ref_table FROM pg_tables WHERE schemaname||'.'||tablename = v_row.ref_table; v_sql := format('ALTER TABLE %I.%I ADD FOREIGN KEY (%s) REFERENCES %I.%I (%s)', v_schemaname, v_tablename, v_row.child_column, v_ref_schema, v_ref_table, v_row.ref_column); IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Applying FK: '||v_sql); END IF; EXECUTE v_sql; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'FK applied'); END IF; END LOOP; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN APPLYING FOREIGN KEYS: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to create id partitions */ CREATE OR REPLACE FUNCTION create_id_partition (p_parent_table text, p_partition_ids bigint[]) RETURNS text LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_all text[] := ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER']; v_analyze boolean := FALSE; v_control text; v_grantees text[]; v_hasoids boolean; v_id bigint; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_parent_grant record; v_parent_owner text; v_parent_schema text; v_parent_tablename text; v_parent_tablespace text; v_part_interval bigint; v_partition_name text; v_revoke text[]; v_sql text; v_step_id bigint; v_tablename text; v_unlogged char; BEGIN SELECT control , part_interval , jobmon INTO v_control , v_part_interval , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; SELECT tableowner, schemaname, tablename, tablespace INTO v_parent_owner, v_parent_schema, v_parent_tablename, v_parent_tablespace FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; FOREACH v_id IN ARRAY p_partition_ids LOOP v_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_id::text, TRUE); -- If child table already exists, skip creation SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_tablename IS NOT NULL 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_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating new partition '||v_partition_name||' with interval from '||v_id||' to '||(v_id + v_part_interval)-1); END IF; SELECT relpersistence INTO v_unlogged FROM pg_catalog.pg_class WHERE oid::regclass = p_parent_table::regclass; v_sql := 'CREATE'; IF v_unlogged = 'u' THEN v_sql := v_sql || ' UNLOGGED'; END IF; v_sql := v_sql || ' TABLE '||v_partition_name||' (LIKE '||p_parent_table||' INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING STORAGE INCLUDING COMMENTS)'; SELECT relhasoids INTO v_hasoids FROM pg_catalog.pg_class WHERE oid::regclass = p_parent_table::regclass; IF v_hasoids IS TRUE THEN v_sql := v_sql || ' WITH (OIDS)'; END IF; EXECUTE v_sql; SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_parent_tablespace IS NOT NULL THEN EXECUTE 'ALTER TABLE '||v_partition_name||' SET TABLESPACE '||v_parent_tablespace; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' ADD CONSTRAINT '||v_tablename||'_partition_check CHECK ('||v_control||'>='||quote_literal(v_id)||' AND '||v_control||'<'||quote_literal(v_id + v_part_interval)||')'; EXECUTE 'ALTER TABLE '||v_partition_name||' INHERIT '||p_parent_table; FOR v_parent_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = p_parent_table GROUP BY grantee LOOP EXECUTE 'GRANT '||array_to_string(v_parent_grant.types, ',')||' ON '||v_partition_name||' TO '||v_parent_grant.grantee; SELECT array_agg(r) INTO v_revoke FROM (SELECT unnest(v_all) AS r EXCEPT SELECT unnest(v_parent_grant.types)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE '||array_to_string(v_revoke, ',')||' ON '||v_partition_name||' FROM '||v_parent_grant.grantee||' CASCADE'; END IF; v_grantees := array_append(v_grantees, v_parent_grant.grantee::text); END LOOP; -- Revoke all privileges from roles that have none on the parent IF v_grantees IS NOT NULL THEN SELECT array_agg(r) INTO v_revoke FROM ( SELECT DISTINCT grantee::text AS r FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = v_partition_name EXCEPT SELECT unnest(v_grantees)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE ALL ON '||v_partition_name||' FROM '||array_to_string(v_revoke, ','); END IF; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' OWNER TO '||v_parent_owner; PERFORM @extschema@.apply_foreign_keys(quote_ident(v_parent_schema)||'.'||quote_ident(v_parent_tablename), v_partition_name); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); PERFORM close_job(v_job_id); END IF; END LOOP; IF v_analyze THEN EXECUTE 'ANALYZE '||p_parent_table; END IF; IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_partition_name; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN CREATE TABLE: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to create a child table in a time-based partition set */ CREATE OR REPLACE FUNCTION create_time_partition (p_parent_table text, p_partition_times timestamp[]) RETURNS text LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_all text[] := ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER']; v_analyze boolean := FALSE; v_control text; v_grantees text[]; v_hasoids boolean; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_parent_grant record; v_parent_owner text; v_parent_schema text; v_parent_tablename text; v_partition_name text; v_partition_suffix text; v_parent_tablespace text; v_part_interval interval; v_partition_timestamp_end timestamp; v_partition_timestamp_start timestamp; v_quarter text; v_revoke text[]; v_sql text; v_step_id bigint; v_step_overflow_id bigint; v_tablename text; v_trunc_value text; v_time timestamp; v_type text; v_unlogged char; v_year text; BEGIN SELECT type , control , part_interval , jobmon INTO v_type , v_control , v_part_interval , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic' OR type = 'time-custom'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; SELECT tableowner, schemaname, tablename, tablespace INTO v_parent_owner, v_parent_schema, v_parent_tablename, v_parent_tablespace FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; FOREACH v_time IN ARRAY p_partition_times LOOP v_partition_suffix := to_char(v_time, 'YYYY'); IF v_part_interval < '1 year' AND v_part_interval <> '1 week' THEN v_partition_suffix := v_partition_suffix ||'_'|| to_char(v_time, 'MM'); IF v_part_interval < '1 month' AND v_part_interval <> '1 week' THEN v_partition_suffix := v_partition_suffix ||'_'|| to_char(v_time, 'DD'); IF v_part_interval < '1 day' THEN v_partition_suffix := v_partition_suffix || '_' || to_char(v_time, 'HH24MI'); IF v_part_interval < '1 minute' THEN v_partition_suffix := v_partition_suffix || to_char(v_time, 'SS'); END IF; -- end < minute IF END IF; -- end < day IF END IF; -- end < month IF END IF; -- end < year IF v_partition_timestamp_start := v_time; BEGIN v_partition_timestamp_end := v_time + v_part_interval; EXCEPTION WHEN datetime_field_overflow THEN 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'); CONTINUE; END; IF v_part_interval = '1 week' THEN v_partition_suffix := to_char(v_time, 'IYYY') || 'w' || to_char(v_time, 'IW'); END IF; -- "Q" is ignored in to_timestamp, so handle special case IF v_part_interval = '3 months' AND (v_type = 'time-static' OR v_type = 'time-dynamic') THEN v_year := to_char(v_time, 'YYYY'); v_quarter := to_char(v_time, 'Q'); v_partition_suffix := v_year || 'q' || v_quarter; END IF; v_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_partition_suffix, TRUE); SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_tablename IS NOT NULL 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_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating new partition '||v_partition_name||' with interval from '||v_partition_timestamp_start||' to '||(v_partition_timestamp_end-'1sec'::interval)); END IF; SELECT relpersistence INTO v_unlogged FROM pg_catalog.pg_class WHERE oid::regclass = p_parent_table::regclass; v_sql := 'CREATE'; IF v_unlogged = 'u' THEN v_sql := v_sql || ' UNLOGGED'; END IF; v_sql := v_sql || ' TABLE '||v_partition_name||' (LIKE '||p_parent_table||' INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING STORAGE INCLUDING COMMENTS)'; SELECT relhasoids INTO v_hasoids FROM pg_catalog.pg_class WHERE oid::regclass = p_parent_table::regclass; IF v_hasoids IS TRUE THEN v_sql := v_sql || ' WITH (OIDS)'; END IF; EXECUTE v_sql; SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_parent_tablespace IS NOT NULL THEN EXECUTE 'ALTER TABLE '||v_partition_name||' SET TABLESPACE '||v_parent_tablespace; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' ADD CONSTRAINT '||v_tablename||'_partition_check CHECK ('||v_control||'>='||quote_literal(v_partition_timestamp_start)||' AND '||v_control||'<'||quote_literal(v_partition_timestamp_end)||')'; EXECUTE 'ALTER TABLE '||v_partition_name||' INHERIT '||p_parent_table; -- If custom time, set extra config options. IF v_type = 'time-custom' THEN INSERT INTO @extschema@.custom_time_partitions (parent_table, child_table, partition_range) VALUES ( p_parent_table, v_partition_name, tstzrange(v_partition_timestamp_start, v_partition_timestamp_end, '[)') ); END IF; FOR v_parent_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = p_parent_table GROUP BY grantee LOOP EXECUTE 'GRANT '||array_to_string(v_parent_grant.types, ',')||' ON '||v_partition_name||' TO '||v_parent_grant.grantee; SELECT array_agg(r) INTO v_revoke FROM (SELECT unnest(v_all) AS r EXCEPT SELECT unnest(v_parent_grant.types)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE '||array_to_string(v_revoke, ',')||' ON '||v_partition_name||' FROM '||v_parent_grant.grantee||' CASCADE'; END IF; v_grantees := array_append(v_grantees, v_parent_grant.grantee::text); END LOOP; -- Revoke all privileges from roles that have none on the parent IF v_grantees IS NOT NULL THEN SELECT array_agg(r) INTO v_revoke FROM ( SELECT DISTINCT grantee::text AS r FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = v_partition_name EXCEPT SELECT unnest(v_grantees)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE ALL ON '||v_partition_name||' FROM '||array_to_string(v_revoke, ','); END IF; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' OWNER TO '||v_parent_owner; PERFORM @extschema@.apply_foreign_keys(quote_ident(v_parent_schema)||'.'||quote_ident(v_parent_tablename), v_partition_name); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); IF v_step_overflow_id IS NOT NULL THEN PERFORM fail_job(v_job_id); ELSE PERFORM close_job(v_job_id); END IF; END IF; END LOOP; IF v_analyze THEN EXECUTE 'ANALYZE '||p_parent_table; END IF; IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_partition_name; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN CREATE TABLE: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to undo id-based partitioning created by this extension */ CREATE FUNCTION undo_partition_id(p_parent_table text, p_batch_count int DEFAULT 1, p_batch_interval bigint DEFAULT NULL, p_keep_table boolean DEFAULT true, p_lock_wait numeric DEFAULT 0) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_batch_loop_count int := 0; v_child_loop_total bigint := 0; v_child_min bigint; v_child_table text; v_control text; v_exists int; v_function_name text; v_inner_loop_count int; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_lock_iter int := 1; v_lock_obtained boolean := FALSE; v_move_sql text; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_part_interval bigint; v_row record; v_rowcount bigint; v_step_id bigint; v_trig_name text; v_total bigint := 0; v_undo_count int := 0; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman undo_id_partition')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'undo_id_partition already running.'; RETURN 0; END IF; SELECT part_interval::bigint , control , jobmon INTO v_part_interval , v_control , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic'); IF v_part_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN UNDO PARTITIONING: '||p_parent_table); v_step_id := add_step(v_job_id, 'Undoing partitioning for table '||p_parent_table); END IF; IF p_batch_interval IS NULL THEN p_batch_interval := v_part_interval; END IF; -- Stops new time partitons 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; -- Stop data going into child tables and stop new id partitions from being made. SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; 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, v_parent_schema, '_part_trig_func', FALSE); SELECT tgname INTO v_trig_name FROM pg_catalog.pg_trigger t WHERE tgname = v_trig_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 'LOCK TABLE ONLY '||p_parent_table||' IN ACCESS EXCLUSIVE MODE NOWAIT'; 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'; RETURN -1; END IF; END IF; -- END p_lock_wait IF EXECUTE 'DROP TRIGGER IF EXISTS '||v_trig_name||' ON '||p_parent_table; END IF; -- END trigger IF v_lock_obtained := FALSE; -- reset for reuse later EXECUTE 'DROP FUNCTION IF EXISTS '||v_function_name||'()'; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Stopped partition creation process. Removed trigger & trigger function'); END IF; <> WHILE v_batch_loop_count < p_batch_count LOOP SELECT n.nspname||'.'||c.relname INTO v_child_table FROM pg_inherits i JOIN pg_class c ON i.inhrelid = c.oid JOIN pg_namespace n ON c.relnamespace = n.oid WHERE i.inhparent::regclass = p_parent_table::regclass ORDER BY i.inhrelid ASC; EXIT WHEN v_child_table IS NULL; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing child partition: '||v_child_table); END IF; EXECUTE 'SELECT min('||v_control||') FROM '||v_child_table INTO v_child_min; IF v_child_min IS NULL THEN -- No rows left in this child table. Remove from partition set. -- lockwait timeout for table 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 'LOCK TABLE ONLY '||v_child_table||' IN ACCESS EXCLUSIVE MODE NOWAIT'; 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 child table for removal from partition set'; RETURN -1; END IF; END IF; -- END p_lock_wait IF v_lock_obtained := FALSE; -- reset for reuse later EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; IF p_keep_table = false THEN EXECUTE 'DROP TABLE '||v_child_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table DROPPED. Moved '||v_child_loop_total||' rows to parent'); END IF; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table UNINHERITED, not DROPPED. Moved '||v_child_loop_total||' rows to parent'); END IF; END IF; v_undo_count := v_undo_count + 1; CONTINUE outer_child_loop; END IF; v_inner_loop_count := 1; v_child_loop_total := 0; <> LOOP -- lockwait timeout for row batches 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 'SELECT * FROM ' || v_child_table || ' WHERE '||v_control||' <= '||quote_literal(v_child_min + (p_batch_interval * v_inner_loop_count)) ||' FOR UPDATE NOWAIT'; 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 batch of rows to move'; RETURN -1; END IF; END IF; -- Get everything from the current child minimum up to the multiples of the given interval v_move_sql := 'WITH move_data AS (DELETE FROM '||v_child_table|| ' WHERE '||v_control||' <= '||quote_literal(v_child_min + (p_batch_interval * v_inner_loop_count))||' RETURNING *) INSERT INTO '||p_parent_table||' SELECT * FROM move_data'; EXECUTE v_move_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total := v_total + v_rowcount; v_child_loop_total := v_child_loop_total + v_rowcount; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Moved '||v_child_loop_total||' rows to parent.'); END IF; EXIT inner_child_loop WHEN v_rowcount = 0; -- exit before loop incr if table is empty v_inner_loop_count := v_inner_loop_count + 1; v_batch_loop_count := v_batch_loop_count + 1; EXIT outer_child_loop WHEN v_batch_loop_count >= p_batch_count; -- Exit outer FOR loop if p_batch_count is reached END LOOP inner_child_loop; END LOOP outer_child_loop; IF v_batch_loop_count < p_batch_count THEN -- FOR loop never ran, so there's no child tables left. DELETE FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing config from pg_partman'); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; RAISE NOTICE 'Copied % row(s) to the parent. Removed % partitions.', v_total, v_undo_count; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Final stats'); PERFORM update_step(v_step_id, 'OK', 'Copied '||v_total||' row(s) to the parent. Removed '||v_undo_count||' partitions.'); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_total; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN UNDO PARTITIONING: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to undo time-based partitioning created by this extension */ CREATE FUNCTION undo_partition_time(p_parent_table text, p_batch_count int DEFAULT 1, p_batch_interval interval DEFAULT NULL, p_keep_table boolean DEFAULT true, p_lock_wait numeric DEFAULT 0) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_batch_loop_count int := 0; v_child_min timestamptz; v_child_loop_total bigint := 0; v_child_table text; v_control text; v_function_name text; v_inner_loop_count int; v_lock_iter int := 1; v_lock_obtained boolean := FALSE; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_move_sql text; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_part_interval interval; v_row record; v_rowcount bigint; v_step_id bigint; v_total bigint := 0; v_trig_name text; v_undo_count int := 0; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman undo_time_partition')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'undo_time_partition already running.'; RETURN 0; END IF; SELECT part_interval::interval , control , jobmon INTO v_part_interval , v_control , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic' OR type = 'time-custom'); IF v_part_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN UNDO PARTITIONING: '||p_parent_table); v_step_id := add_step(v_job_id, 'Undoing partitioning for table '||p_parent_table); END IF; IF p_batch_interval IS NULL THEN p_batch_interval := v_part_interval; END IF; -- Stops new time partitons 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; -- Stop data going into child tables. SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; 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, v_parent_schema, '_part_trig_func', FALSE); SELECT tgname INTO v_trig_name FROM pg_catalog.pg_trigger t WHERE tgname = v_trig_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 'LOCK TABLE ONLY '||p_parent_table||' IN ACCESS EXCLUSIVE MODE NOWAIT'; 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'; RETURN -1; END IF; END IF; -- END p_lock_wait IF EXECUTE 'DROP TRIGGER IF EXISTS '||v_trig_name||' ON '||p_parent_table; END IF; -- END trigger IF v_lock_obtained := FALSE; -- reset for reuse later EXECUTE 'DROP FUNCTION IF EXISTS '||v_function_name||'()'; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Stopped partition creation process. Removed trigger & trigger function'); END IF; <> WHILE v_batch_loop_count < p_batch_count LOOP SELECT n.nspname||'.'||c.relname INTO v_child_table FROM pg_inherits i JOIN pg_class c ON i.inhrelid = c.oid JOIN pg_namespace n ON c.relnamespace = n.oid WHERE i.inhparent::regclass = p_parent_table::regclass ORDER BY i.inhrelid ASC; EXIT WHEN v_child_table IS NULL; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing child partition: '||v_child_table); END IF; EXECUTE 'SELECT min('||v_control||') FROM '||v_child_table INTO v_child_min; IF v_child_min IS NULL THEN -- No rows left in this child table. Remove from partition set. -- lockwait timeout for table 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 'LOCK TABLE ONLY '||v_child_table||' IN ACCESS EXCLUSIVE MODE NOWAIT'; 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 child table for removal from partition set'; RETURN -1; END IF; END IF; -- END p_lock_wait IF v_lock_obtained := FALSE; -- reset for reuse later EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; IF p_keep_table = false THEN EXECUTE 'DROP TABLE '||v_child_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table DROPPED. Moved '||v_child_loop_total||' rows to parent'); END IF; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table UNINHERITED, not DROPPED. Moved '||v_child_loop_total||' rows to parent'); END IF; END IF; v_undo_count := v_undo_count + 1; CONTINUE outer_child_loop; END IF; v_inner_loop_count := 1; v_child_loop_total := 0; <> LOOP -- do some locking with timeout, if required 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 'SELECT * FROM ' || v_child_table || ' WHERE '||v_control||' <= '||quote_literal(v_child_min + (p_batch_interval * v_inner_loop_count)) ||' FOR UPDATE NOWAIT'; 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 batch of rows to move'; RETURN -1; END IF; END IF; -- Get everything from the current child minimum up to the multiples of the given interval v_move_sql := 'WITH move_data AS (DELETE FROM '||v_child_table|| ' WHERE '||v_control||' <= '||quote_literal(v_child_min + (p_batch_interval * v_inner_loop_count))||' RETURNING *) INSERT INTO '||p_parent_table||' SELECT * FROM move_data'; EXECUTE v_move_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total := v_total + v_rowcount; v_child_loop_total := v_child_loop_total + v_rowcount; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Moved '||v_child_loop_total||' rows to parent.'); END IF; EXIT inner_child_loop WHEN v_rowcount = 0; -- exit before loop incr if table is empty v_inner_loop_count := v_inner_loop_count + 1; v_batch_loop_count := v_batch_loop_count + 1; EXIT outer_child_loop WHEN v_batch_loop_count >= p_batch_count; -- Exit outer FOR loop if p_batch_count is reached END LOOP inner_child_loop; END LOOP outer_child_loop; IF v_batch_loop_count < p_batch_count THEN -- FOR loop never ran, so there's no child tables left. DELETE FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing config from pg_partman'); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; RAISE NOTICE 'Copied % row(s) to the parent. Removed % partitions.', v_total, v_undo_count; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Final stats'); PERFORM update_step(v_step_id, 'OK', 'Copied '||v_total||' row(s) to the parent. Removed '||v_undo_count||' partitions.'); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_total; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN UNDO PARTITIONING: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to undo partitioning. * Will actually work on any parent/child table set, not just ones created by pg_partman. */ CREATE FUNCTION undo_partition(p_parent_table text, p_batch_count int DEFAULT 1, p_keep_table boolean DEFAULT true, p_jobmon boolean DEFAULT true, p_lock_wait numeric DEFAULT 0) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_batch_loop_count bigint := 0; v_child_count bigint; v_child_table text; v_copy_sql text; v_function_name text; v_job_id bigint; v_jobmon_schema text; v_lock_iter int := 1; v_lock_obtained boolean := FALSE; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_part_interval interval; v_rowcount bigint; v_step_id bigint; v_total bigint := 0; v_trig_name text; v_undo_count int := 0; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman undo_partition')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'undo_partition already running.'; RETURN 0; END IF; IF p_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN UNDO PARTITIONING: '||p_parent_table); v_step_id := add_step(v_job_id, 'Undoing partitioning for table '||p_parent_table); END IF; -- Stops new time partitons 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; -- Stop data going into child tables and stop new id partitions from being made. SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; 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, v_parent_schema, '_part_trig_func', FALSE); SELECT tgname INTO v_trig_name FROM pg_catalog.pg_trigger t WHERE tgname = v_trig_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 'LOCK TABLE ONLY '||p_parent_table||' IN ACCESS EXCLUSIVE MODE NOWAIT'; 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'; RETURN -1; END IF; END IF; -- END p_lock_wait IF EXECUTE 'DROP TRIGGER IF EXISTS '||v_trig_name||' ON '||p_parent_table; END IF; -- END trigger IF v_lock_obtained := FALSE; -- reset for reuse later EXECUTE 'DROP FUNCTION IF EXISTS '||v_function_name||'()'; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Stopped partition creation process. Removed trigger & trigger function'); END IF; WHILE v_batch_loop_count < p_batch_count LOOP SELECT n.nspname||'.'||c.relname INTO v_child_table FROM pg_inherits i JOIN pg_class c ON i.inhrelid = c.oid JOIN pg_namespace n ON c.relnamespace = n.oid WHERE i.inhparent::regclass = p_parent_table::regclass ORDER BY i.inhrelid ASC; EXIT WHEN v_child_table IS NULL; EXECUTE 'SELECT count(*) FROM '||v_child_table INTO v_child_count; IF v_child_count = 0 THEN -- No rows left in this child table. Remove from partition set. -- lockwait timeout for table 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 'LOCK TABLE ONLY '||v_child_table||' IN ACCESS EXCLUSIVE MODE NOWAIT'; 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 child table for removal from partition set'; RETURN -1; END IF; END IF; -- END p_lock_wait IF v_lock_obtained := FALSE; -- reset for reuse later EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; IF p_keep_table = false THEN EXECUTE 'DROP TABLE '||v_child_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table DROPPED. Moved '||coalesce(v_rowcount, 0)||' rows to parent'); END IF; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table UNINHERITED, not DROPPED. Copied '||coalesce(v_rowcount, 0)||' rows to parent'); END IF; END IF; v_undo_count := v_undo_count + 1; CONTINUE; END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing child partition: '||v_child_table); END IF; -- do some locking with timeout, if required 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 'SELECT * FROM '|| v_child_table ||' FOR UPDATE NOWAIT'; 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 batch of rows to move'; RETURN -1; END IF; END IF; v_copy_sql := 'INSERT INTO '||p_parent_table||' SELECT * FROM '||v_child_table; EXECUTE v_copy_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total := v_total + v_rowcount; EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; IF p_keep_table = false THEN EXECUTE 'DROP TABLE '||v_child_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table DROPPED. Moved '||v_rowcount||' rows to parent'); END IF; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table UNINHERITED, not DROPPED. Copied '||v_rowcount||' rows to parent'); END IF; END IF; v_batch_loop_count := v_batch_loop_count + 1; v_undo_count := v_undo_count + 1; END LOOP; IF v_undo_count = 0 THEN -- FOR loop never ran, so there's no child tables left. DELETE FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing config from pg_partman (if it existed)'); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; RAISE NOTICE 'Copied % row(s) from % child table(s) to the parent: %', v_total, v_undo_count, p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Final stats'); PERFORM update_step(v_step_id, 'OK', 'Copied '||v_total||' row(s) from '||v_undo_count||' child table(s) to the parent'); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_total; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN UNDO PARTITIONING: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''Partition function maintenance for table '||p_parent_table||' failed'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; -- Restore dropped object privileges DO $$ DECLARE v_row record; BEGIN FOR v_row IN SELECT statement FROM partman_preserve_privs_temp LOOP IF v_row.statement IS NOT NULL THEN EXECUTE v_row.statement; END IF; END LOOP; END $$; DROP TABLE IF EXISTS partman_preserve_privs_temp; pg_partman-2.2.2/updates/pg_partman--1.7.1--1.7.2.sql000066400000000000000000001052741262146621700214420ustar00rootroot00000000000000-- 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 foreig 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(). ALTER TABLE @extschema@.part_config ADD inherit_fk boolean NOT NULL DEFAULT true; CREATE TEMP TABLE partman_preserve_privs_temp (statement text); INSERT INTO partman_preserve_privs_temp SELECT 'GRANT EXECUTE ON FUNCTION @extschema@.create_parent(text, text, text, text, text[], int, boolean, text, boolean, boolean, boolean) TO '||array_to_string(array_agg(grantee::text), ',')||';' FROM information_schema.routine_privileges WHERE routine_schema = '@extschema@' AND routine_name = 'create_parent'; DROP FUNCTION create_parent(text, text, text, text, text[], int, boolean, text, boolean, boolean); /* * Apply foreign keys that exist on the given parent to the given child table */ CREATE OR REPLACE FUNCTION apply_foreign_keys(p_parent_table text, p_child_table text DEFAULT NULL, p_debug boolean DEFAULT false) RETURNS void LANGUAGE plpgsql AS $$ DECLARE v_job_id bigint; v_jobmon text; v_jobmon_schema text; v_old_search_path text; v_ref_schema text; v_ref_table text; v_row record; v_schemaname text; v_sql text; v_step_id bigint; v_tablename text; BEGIN SELECT jobmon INTO v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN APPLYING FOREIGN KEYS: '||p_parent_table); END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Checking if target child table exists'); END IF; SELECT schemaname, tablename INTO v_schemaname, v_tablename FROM pg_catalog.pg_tables WHERE schemaname||'.'||tablename = p_child_table; IF v_tablename IS NULL THEN IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'CRITICAL', 'Target child table ('||v_child_table||') does not exist.'); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION 'Target child table (%.%) does not exist.', v_schemaname, v_tablename; RETURN; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; FOR v_row IN SELECT n.nspname||'.'||cl.relname AS ref_table , '"'||string_agg(att.attname, '","')||'"' AS ref_column , '"'||string_agg(att2.attname, '","')||'"' AS child_column FROM ( SELECT con.conname , unnest(con.conkey) as ref , unnest(con.confkey) as child , con.confrelid , con.conrelid FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid JOIN pg_catalog.pg_constraint con ON c.oid = con.conrelid WHERE n.nspname ||'.'|| c.relname = p_parent_table AND con.contype = 'f' ORDER BY con.conkey ) keys JOIN pg_catalog.pg_class cl ON cl.oid = keys.confrelid JOIN pg_catalog.pg_namespace n ON cl.relnamespace = n.oid JOIN pg_catalog.pg_attribute att ON att.attrelid = keys.confrelid AND att.attnum = keys.child JOIN pg_catalog.pg_attribute att2 ON att2.attrelid = keys.conrelid AND att2.attnum = keys.ref GROUP BY keys.conname, n.nspname, cl.relname LOOP SELECT schemaname, tablename INTO v_ref_schema, v_ref_table FROM pg_tables WHERE schemaname||'.'||tablename = v_row.ref_table; v_sql := format('ALTER TABLE %I.%I ADD FOREIGN KEY (%s) REFERENCES %I.%I (%s)', v_schemaname, v_tablename, v_row.child_column, v_ref_schema, v_ref_table, v_row.ref_column); IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Applying FK: '||v_sql); END IF; EXECUTE v_sql; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'FK applied'); END IF; END LOOP; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN APPLYING FOREIGN KEYS: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to turn a table into the parent of a partition set */ CREATE FUNCTION create_parent( p_parent_table text , p_control text , p_type text , p_interval text , p_constraint_cols text[] DEFAULT NULL , p_premake int DEFAULT 4 , p_use_run_maintenance boolean DEFAULT NULL , p_start_partition text DEFAULT NULL , p_inherit_fk boolean DEFAULT true , p_jobmon boolean DEFAULT true , p_debug boolean DEFAULT false) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_base_timestamp timestamp; v_count int := 1; v_datetime_string text; v_id_interval bigint; v_job_id bigint; v_jobmon_schema text; v_last_partition_name text; v_old_search_path text; v_partition_time timestamp; v_partition_time_array timestamp[]; v_partition_id bigint[]; v_max bigint; v_notnull boolean; v_run_maint boolean; v_start_time timestamp; v_starting_partition_id bigint; v_step_id bigint; v_step_overflow_id bigint; v_tablename text; v_time_interval interval; BEGIN IF position('.' in p_parent_table) = 0 THEN RAISE EXCEPTION 'Parent table must be schema qualified'; END IF; SELECT tablename INTO v_tablename FROM pg_tables WHERE schemaname || '.' || tablename = p_parent_table; IF v_tablename IS NULL THEN RAISE EXCEPTION 'Please create given parent table first: %', p_parent_table; END IF; SELECT attnotnull INTO v_notnull FROM pg_attribute WHERE attrelid = p_parent_table::regclass AND attname = p_control; IF v_notnull = false OR v_notnull IS NULL THEN RAISE EXCEPTION 'Control column (%) for parent table (%) must be NOT NULL', p_control, p_parent_table; END IF; IF NOT @extschema@.check_partition_type(p_type) THEN RAISE EXCEPTION '% is not a valid partitioning type', p_type; END IF; IF p_type = 'time-custom' AND @extschema@.check_version('9.2.0') IS FALSE THEN RAISE EXCEPTION 'The "time-custom" type requires a minimum PostgreSQL version of 9.2.0'; END IF; EXECUTE 'LOCK TABLE '||p_parent_table||' IN ACCESS EXCLUSIVE MODE'; IF p_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF p_use_run_maintenance IS NOT NULL THEN IF p_use_run_maintenance IS FALSE AND (p_type = 'time-static' OR p_type = 'time-dynamic' OR p_type = 'time-custom') THEN RAISE EXCEPTION 'p_run_maintenance cannot be set to false for time based partitioning'; END IF; v_run_maint := p_use_run_maintenance; ELSIF p_type = 'time-static' OR p_type = 'time-dynamic' OR p_type = 'time-custom' THEN v_run_maint := TRUE; ELSIF p_type = 'id-static' OR p_type ='id-dynamic' THEN v_run_maint := FALSE; ELSE RAISE EXCEPTION 'use_run_maintenance value cannot be set NULL'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN SETUP PARENT: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating initial partitions on new parent table: '||p_parent_table); END IF; IF p_type = 'time-static' OR p_type = 'time-dynamic' OR p_type = 'time-custom' 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 <> 'time-custom' THEN RAISE EXCEPTION 'Must use a predefined time interval if not using type "time-custom". See documentation.'; 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; -- First partition is either the min premake or p_start_partition v_start_time := COALESCE(p_start_partition::timestamp, CURRENT_TIMESTAMP - (v_time_interval * p_premake)); 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 v_datetime_string := 'YYYY'; 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 v_partition_time_array := array_append(v_partition_time_array, v_base_timestamp); LOOP -- If current loop value is less than or equal to the value of the max premake, add time to array. IF (v_base_timestamp + (v_time_interval * v_count)) < (CURRENT_TIMESTAMP + (v_time_interval * p_premake)) THEN BEGIN v_partition_time := (v_base_timestamp + (v_time_interval * v_count))::timestamp; 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. 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'); CONTINUE; END; ELSE EXIT; -- all needed partitions added to array. Exit the loop. END IF; v_count := v_count + 1; END LOOP; INSERT INTO @extschema@.part_config ( parent_table , type , part_interval , control , premake , constraint_cols , datetime_string , use_run_maintenance , inherit_fk , jobmon) VALUES ( p_parent_table , p_type , v_time_interval , p_control , p_premake , p_constraint_cols , v_datetime_string , v_run_maint , p_inherit_fk , p_jobmon); v_last_partition_name := @extschema@.create_time_partition(p_parent_table, v_partition_time_array); -- Doing separate update because create function requires in config table last_partition to be set UPDATE @extschema@.part_config SET last_partition = v_last_partition_name WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Time partitions premade: '||p_premake); END IF; END IF; IF p_type = 'id-static' OR p_type = 'id-dynamic' THEN v_id_interval := p_interval::bigint; IF v_id_interval <= 1 THEN RAISE EXCEPTION 'Interval for serial partitioning must be greater than 1'; END IF; -- If custom start partition is set, use that. -- If custom start is not set and there is already data, start partitioning with the highest current value EXECUTE 'SELECT COALESCE('||quote_nullable(p_start_partition::bigint)||', max('||p_control||')::bigint, 0) FROM '||p_parent_table||' LIMIT 1' INTO v_max; 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 THEN v_partition_id = array_append(v_partition_id, (v_starting_partition_id - v_id_interval*i)); END IF; v_partition_id = array_append(v_partition_id, (v_id_interval*i) + v_starting_partition_id); END LOOP; INSERT INTO @extschema@.part_config ( parent_table , type , part_interval , control , premake , constraint_cols , use_run_maintenance , inherit_fk , jobmon) VALUES ( p_parent_table , p_type , v_id_interval , p_control , p_premake , p_constraint_cols , v_run_maint , p_inherit_fk , p_jobmon); v_last_partition_name := @extschema@.create_id_partition(p_parent_table, v_partition_id); -- Doing separate update because create function needs parent table in config table for apply_grants() UPDATE @extschema@.part_config SET last_partition = v_last_partition_name WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'ID partitions premade: '||p_premake); END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Creating partition function'); END IF; IF p_type = 'time-static' OR p_type = 'time-dynamic' OR p_type = 'time-custom' THEN PERFORM @extschema@.create_time_function(p_parent_table); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Time function created'); END IF; ELSIF p_type = 'id-static' OR p_type = 'id-dynamic' THEN PERFORM @extschema@.create_id_function(p_parent_table); 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); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); IF v_step_overflow_id IS NOT NULL THEN PERFORM fail_job(v_job_id); ELSE PERFORM close_job(v_job_id); END IF; EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN CREATE PARENT: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''Partition creation for table '||p_parent_table||' failed'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to create id partitions */ CREATE OR REPLACE FUNCTION create_id_partition (p_parent_table text, p_partition_ids bigint[]) RETURNS text LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_all text[] := ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER']; v_analyze boolean := FALSE; v_control text; v_grantees text[]; v_hasoids boolean; v_id bigint; v_inherit_fk boolean; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_parent_grant record; v_parent_owner text; v_parent_schema text; v_parent_tablename text; v_parent_tablespace text; v_part_interval bigint; v_partition_name text; v_revoke text[]; v_sql text; v_step_id bigint; v_tablename text; v_unlogged char; BEGIN SELECT control , part_interval , inherit_fk , jobmon INTO v_control , v_part_interval , v_inherit_fk , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; SELECT tableowner, schemaname, tablename, tablespace INTO v_parent_owner, v_parent_schema, v_parent_tablename, v_parent_tablespace FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; FOREACH v_id IN ARRAY p_partition_ids LOOP v_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_id::text, TRUE); -- If child table already exists, skip creation SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_tablename IS NOT NULL 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_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating new partition '||v_partition_name||' with interval from '||v_id||' to '||(v_id + v_part_interval)-1); END IF; SELECT relpersistence INTO v_unlogged FROM pg_catalog.pg_class WHERE oid::regclass = p_parent_table::regclass; v_sql := 'CREATE'; IF v_unlogged = 'u' THEN v_sql := v_sql || ' UNLOGGED'; END IF; v_sql := v_sql || ' TABLE '||v_partition_name||' (LIKE '||p_parent_table||' INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING STORAGE INCLUDING COMMENTS)'; SELECT relhasoids INTO v_hasoids FROM pg_catalog.pg_class WHERE oid::regclass = p_parent_table::regclass; IF v_hasoids IS TRUE THEN v_sql := v_sql || ' WITH (OIDS)'; END IF; EXECUTE v_sql; SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_parent_tablespace IS NOT NULL THEN EXECUTE 'ALTER TABLE '||v_partition_name||' SET TABLESPACE '||v_parent_tablespace; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' ADD CONSTRAINT '||v_tablename||'_partition_check CHECK ('||v_control||'>='||quote_literal(v_id)||' AND '||v_control||'<'||quote_literal(v_id + v_part_interval)||')'; EXECUTE 'ALTER TABLE '||v_partition_name||' INHERIT '||p_parent_table; FOR v_parent_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = p_parent_table GROUP BY grantee LOOP EXECUTE 'GRANT '||array_to_string(v_parent_grant.types, ',')||' ON '||v_partition_name||' TO '||v_parent_grant.grantee; SELECT array_agg(r) INTO v_revoke FROM (SELECT unnest(v_all) AS r EXCEPT SELECT unnest(v_parent_grant.types)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE '||array_to_string(v_revoke, ',')||' ON '||v_partition_name||' FROM '||v_parent_grant.grantee||' CASCADE'; END IF; v_grantees := array_append(v_grantees, v_parent_grant.grantee::text); END LOOP; -- Revoke all privileges from roles that have none on the parent IF v_grantees IS NOT NULL THEN SELECT array_agg(r) INTO v_revoke FROM ( SELECT DISTINCT grantee::text AS r FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = v_partition_name EXCEPT SELECT unnest(v_grantees)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE ALL ON '||v_partition_name||' FROM '||array_to_string(v_revoke, ','); END IF; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' OWNER TO '||v_parent_owner; IF v_inherit_fk THEN PERFORM @extschema@.apply_foreign_keys(quote_ident(v_parent_schema)||'.'||quote_ident(v_parent_tablename), v_partition_name); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); PERFORM close_job(v_job_id); END IF; END LOOP; IF v_analyze THEN EXECUTE 'ANALYZE '||p_parent_table; END IF; IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_partition_name; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN CREATE TABLE: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to create a child table in a time-based partition set */ CREATE OR REPLACE FUNCTION create_time_partition (p_parent_table text, p_partition_times timestamp[]) RETURNS text LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_all text[] := ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER']; v_analyze boolean := FALSE; v_control text; v_grantees text[]; v_hasoids boolean; v_inherit_fk boolean; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_parent_grant record; v_parent_owner text; v_parent_schema text; v_parent_tablename text; v_partition_name text; v_partition_suffix text; v_parent_tablespace text; v_part_interval interval; v_partition_timestamp_end timestamp; v_partition_timestamp_start timestamp; v_quarter text; v_revoke text[]; v_sql text; v_step_id bigint; v_step_overflow_id bigint; v_tablename text; v_trunc_value text; v_time timestamp; v_type text; v_unlogged char; v_year text; BEGIN SELECT type , control , part_interval , inherit_fk , jobmon INTO v_type , v_control , v_part_interval , v_inherit_fk , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic' OR type = 'time-custom'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; SELECT tableowner, schemaname, tablename, tablespace INTO v_parent_owner, v_parent_schema, v_parent_tablename, v_parent_tablespace FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; FOREACH v_time IN ARRAY p_partition_times LOOP v_partition_suffix := to_char(v_time, 'YYYY'); IF v_part_interval < '1 year' AND v_part_interval <> '1 week' THEN v_partition_suffix := v_partition_suffix ||'_'|| to_char(v_time, 'MM'); IF v_part_interval < '1 month' AND v_part_interval <> '1 week' THEN v_partition_suffix := v_partition_suffix ||'_'|| to_char(v_time, 'DD'); IF v_part_interval < '1 day' THEN v_partition_suffix := v_partition_suffix || '_' || to_char(v_time, 'HH24MI'); IF v_part_interval < '1 minute' THEN v_partition_suffix := v_partition_suffix || to_char(v_time, 'SS'); END IF; -- end < minute IF END IF; -- end < day IF END IF; -- end < month IF END IF; -- end < year IF v_partition_timestamp_start := v_time; BEGIN v_partition_timestamp_end := v_time + v_part_interval; EXCEPTION WHEN datetime_field_overflow THEN 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'); CONTINUE; END; IF v_part_interval = '1 week' THEN v_partition_suffix := to_char(v_time, 'IYYY') || 'w' || to_char(v_time, 'IW'); END IF; -- "Q" is ignored in to_timestamp, so handle special case IF v_part_interval = '3 months' AND (v_type = 'time-static' OR v_type = 'time-dynamic') THEN v_year := to_char(v_time, 'YYYY'); v_quarter := to_char(v_time, 'Q'); v_partition_suffix := v_year || 'q' || v_quarter; END IF; v_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_partition_suffix, TRUE); SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_tablename IS NOT NULL 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_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating new partition '||v_partition_name||' with interval from '||v_partition_timestamp_start||' to '||(v_partition_timestamp_end-'1sec'::interval)); END IF; SELECT relpersistence INTO v_unlogged FROM pg_catalog.pg_class WHERE oid::regclass = p_parent_table::regclass; v_sql := 'CREATE'; IF v_unlogged = 'u' THEN v_sql := v_sql || ' UNLOGGED'; END IF; v_sql := v_sql || ' TABLE '||v_partition_name||' (LIKE '||p_parent_table||' INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING STORAGE INCLUDING COMMENTS)'; SELECT relhasoids INTO v_hasoids FROM pg_catalog.pg_class WHERE oid::regclass = p_parent_table::regclass; IF v_hasoids IS TRUE THEN v_sql := v_sql || ' WITH (OIDS)'; END IF; EXECUTE v_sql; SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_parent_tablespace IS NOT NULL THEN EXECUTE 'ALTER TABLE '||v_partition_name||' SET TABLESPACE '||v_parent_tablespace; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' ADD CONSTRAINT '||v_tablename||'_partition_check CHECK ('||v_control||'>='||quote_literal(v_partition_timestamp_start)||' AND '||v_control||'<'||quote_literal(v_partition_timestamp_end)||')'; EXECUTE 'ALTER TABLE '||v_partition_name||' INHERIT '||p_parent_table; -- If custom time, set extra config options. IF v_type = 'time-custom' THEN INSERT INTO @extschema@.custom_time_partitions (parent_table, child_table, partition_range) VALUES ( p_parent_table, v_partition_name, tstzrange(v_partition_timestamp_start, v_partition_timestamp_end, '[)') ); END IF; FOR v_parent_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = p_parent_table GROUP BY grantee LOOP EXECUTE 'GRANT '||array_to_string(v_parent_grant.types, ',')||' ON '||v_partition_name||' TO '||v_parent_grant.grantee; SELECT array_agg(r) INTO v_revoke FROM (SELECT unnest(v_all) AS r EXCEPT SELECT unnest(v_parent_grant.types)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE '||array_to_string(v_revoke, ',')||' ON '||v_partition_name||' FROM '||v_parent_grant.grantee||' CASCADE'; END IF; v_grantees := array_append(v_grantees, v_parent_grant.grantee::text); END LOOP; -- Revoke all privileges from roles that have none on the parent IF v_grantees IS NOT NULL THEN SELECT array_agg(r) INTO v_revoke FROM ( SELECT DISTINCT grantee::text AS r FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = v_partition_name EXCEPT SELECT unnest(v_grantees)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE ALL ON '||v_partition_name||' FROM '||array_to_string(v_revoke, ','); END IF; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' OWNER TO '||v_parent_owner; IF v_inherit_fk THEN PERFORM @extschema@.apply_foreign_keys(quote_ident(v_parent_schema)||'.'||quote_ident(v_parent_tablename), v_partition_name); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); IF v_step_overflow_id IS NOT NULL THEN PERFORM fail_job(v_job_id); ELSE PERFORM close_job(v_job_id); END IF; END IF; END LOOP; IF v_analyze THEN EXECUTE 'ANALYZE '||p_parent_table; END IF; IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_partition_name; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN CREATE TABLE: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; -- Restore dropped object privileges DO $$ DECLARE v_row record; BEGIN FOR v_row IN SELECT statement FROM partman_preserve_privs_temp LOOP IF v_row.statement IS NOT NULL THEN EXECUTE v_row.statement; END IF; END LOOP; END $$; DROP TABLE IF EXISTS partman_preserve_privs_temp; pg_partman-2.2.2/updates/pg_partman--1.7.2--1.8.0.sql000066400000000000000000005020001262146621700214260ustar00rootroot00000000000000-- 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 partiton 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. CREATE TEMP TABLE partman_preserve_privs_temp (statement text); INSERT INTO partman_preserve_privs_temp SELECT 'GRANT EXECUTE ON FUNCTION @extschema@.create_parent(text, text, text, text, text[], int, boolean, text, boolean, boolean, boolean) TO '||array_to_string(array_agg(grantee::text), ',')||';' FROM information_schema.routine_privileges WHERE routine_schema = '@extschema@' AND routine_name = 'create_parent'; INSERT INTO partman_preserve_privs_temp SELECT 'GRANT EXECUTE ON FUNCTION @extschema@.create_partition_id(text, bigint[], boolean) TO '||array_to_string(array_agg(grantee::text), ',')||';' FROM information_schema.routine_privileges WHERE routine_schema = '@extschema@' AND routine_name = 'create_id_partition'; INSERT INTO partman_preserve_privs_temp SELECT 'GRANT EXECUTE ON FUNCTION @extschema@.create_partition_time(text, timestamp[], boolean) TO '||array_to_string(array_agg(grantee::text), ',')||';' FROM information_schema.routine_privileges WHERE routine_schema = '@extschema@' AND routine_name = 'create_time_partition'; INSERT INTO partman_preserve_privs_temp SELECT 'GRANT EXECUTE ON FUNCTION @extschema@.create_function_time(text) TO '||array_to_string(array_agg(grantee::text), ',')||';' FROM information_schema.routine_privileges WHERE routine_schema = '@extschema@' AND routine_name = 'create_time_function'; INSERT INTO partman_preserve_privs_temp SELECT 'GRANT EXECUTE ON FUNCTION @extschema@.create_function_id(text) TO '||array_to_string(array_agg(grantee::text), ',')||';' FROM information_schema.routine_privileges WHERE routine_schema = '@extschema@' AND routine_name = 'create_id_function'; INSERT INTO partman_preserve_privs_temp SELECT 'GRANT EXECUTE ON FUNCTION @extschema@.run_maintenance(text, boolean, boolean) TO '||array_to_string(array_agg(grantee::text), ',')||';' FROM information_schema.routine_privileges WHERE routine_schema = '@extschema@' AND routine_name = 'run_maintenance'; DROP TRIGGER time_partition_maintenance_true_trig ON @extschema@.part_config; DROP FUNCTION @extschema@.time_partition_maintenance_true_trig(); DROP FUNCTION @extschema@.create_parent(text, text, text, text, text[], int, boolean, text, boolean, boolean, boolean); DROP FUNCTION @extschema@.create_id_partition (text, bigint[]); DROP FUNCTION @extschema@.create_time_partition (text, timestamp[]); DROP FUNCTION @extschema@.create_time_function(text); DROP FUNCTION @extschema@.create_id_function(text); DROP FUNCTION @extschema@.run_maintenance(boolean); -- FK set deferrable because create_parent() inserts to this table before part_config CREATE TABLE @extschema@.part_config_sub ( sub_parent text PRIMARY KEY REFERENCES @extschema@.part_config (parent_table) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED , sub_type text NOT NULL , sub_control text NOT NULL , sub_part_interval text NOT NULL , sub_constraint_cols text[] , sub_premake int NOT NULL DEFAULT 4 , sub_inherit_fk boolean NOT NULL DEFAULT true , 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_use_run_maintenance BOOLEAN NOT NULL DEFAULT true , sub_jobmon boolean NOT NULL DEFAULT true ); ALTER TABLE @extschema@.part_config DROP COLUMN last_partition; /* * Ensure that sub-partitioned tables that are themselves sub-partitions have the same configuration options set when they are part of the same inheritance tree */ CREATE FUNCTION check_subpart_sameconfig(text) RETURNS boolean LANGUAGE sql STABLE AS $$ WITH child_tables AS ( SELECT n.nspname||'.'||c.relname AS 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::regclass = $1::regclass ) SELECT CASE WHEN count(*) <= 1 THEN true WHEN count(*) > 1 THEN false END FROM ( SELECT DISTINCT sub_type , sub_control , sub_part_interval , sub_constraint_cols , sub_premake , sub_inherit_fk , sub_retention , sub_retention_schema , sub_retention_keep_table , sub_retention_keep_index , sub_use_run_maintenance , sub_jobmon FROM @extschema@.part_config_sub a JOIN child_tables b on a.sub_parent = b.tablename) x; $$; ALTER TABLE @extschema@.part_config_sub ADD CONSTRAINT subpart_sameconfig_chk CHECK (check_subpart_sameconfig(sub_parent)); /* * Create a partition set that is a subpartition of an already existing partition set. * Given the parent table of any current partition set, it will turn all existing children into parent tables of their own partition sets * using the configuration options given as parameters to this function. * Uses another config table that allows for turning all future child partitions into a new parent automatically. * To avoid logical complications and contention issues, ALL subpartitions must be maintained using run_maintenance(). * This means the automatic, trigger based partition creation for serial partitioning will not work if it is a subpartition. */ CREATE FUNCTION create_sub_parent( p_top_parent text , p_control text , p_type text , p_interval text , p_constraint_cols text[] DEFAULT NULL , p_premake int DEFAULT 4 , p_start_partition text DEFAULT NULL , p_inherit_fk boolean DEFAULT true , p_jobmon boolean DEFAULT true , p_debug boolean DEFAULT false) RETURNS boolean LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_last_partition text; v_row record; v_row_last_part record; v_run_maint boolean; v_sql text; v_success boolean := false; v_top_type text; BEGIN SELECT use_run_maintenance INTO v_run_maint FROM @extschema@.part_config WHERE parent_table = p_top_parent; IF v_run_maint 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; ELSIF v_run_maint = false THEN RAISE EXCEPTION 'Any parent table that will be part of a sub-partitioned set (on any level) must have use_run_maintenance set to true in part_config table, even for serial partitioning. See documentation for more info.'; END IF; FOR v_row IN -- Loop through all current children to turn them into partitioned tables SELECT show_partitions AS child_table FROM @extschema@.show_partitions(p_top_parent) LOOP -- Just call existing create_parent() function but add the given parameters to the part_config_sub table as well v_sql := format('SELECT @extschema@.create_parent( p_parent_table := %L , p_control := %L , p_type := %L , p_interval := %L , p_constraint_cols := %L , p_premake := %L , p_use_run_maintenance := %L , p_start_partition := %L , p_inherit_fk := %L , p_jobmon := %L , p_debug := %L )' , v_row.child_table , p_control , p_type , p_interval , p_constraint_cols , p_premake , true , p_start_partition , p_inherit_fk , p_jobmon , p_debug); EXECUTE v_sql; END LOOP; INSERT INTO @extschema@.part_config_sub ( sub_parent , sub_control , sub_type , sub_part_interval , sub_constraint_cols , sub_premake , sub_inherit_fk , sub_use_run_maintenance , sub_jobmon) VALUES ( p_top_parent , p_control , p_type , p_interval , p_constraint_cols , p_premake , p_inherit_fk , true , p_jobmon); v_success := true; RETURN v_success; END $$; /* * Function to create id partitions */ CREATE FUNCTION create_partition_id(p_parent_table text, p_partition_ids bigint[], p_analyze boolean DEFAULT true) RETURNS boolean LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_all text[] := ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER']; v_analyze boolean := FALSE; v_control text; v_grantees text[]; v_hasoids boolean; v_id bigint; v_id_position int; v_inherit_fk boolean; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_parent_grant record; v_parent_owner text; v_parent_schema text; v_parent_tablename text; v_parent_tablespace text; v_part_interval bigint; v_partition_created boolean := false; v_partition_name text; v_revoke text[]; v_row record; v_sql text; v_step_id bigint; v_sub_id_max bigint; v_sub_id_min bigint; v_tablename text; v_top_interval bigint; v_top_parent text; v_unlogged char; BEGIN SELECT control , part_interval , inherit_fk , jobmon INTO v_control , v_part_interval , v_inherit_fk , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; -- Check if parent table is a subpartition of an already existing id based partition set managed by pg_partman -- If so, limit what child tables can be created based on parent suffix WITH top_oid AS ( SELECT i.inhparent AS top_parent_oid FROM pg_catalog.pg_class c JOIN pg_catalog.pg_inherits i ON c.oid = i.inhrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname||'.'||c.relname = p_parent_table ) SELECT n.nspname||'.'||c.relname INTO v_top_parent FROM pg_catalog.pg_class c JOIN top_oid t ON c.oid = t.top_parent_oid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace JOIN @extschema@.part_config p ON p.parent_table = n.nspname||'.'||c.relname WHERE c.oid = t.top_parent_oid AND p.type = 'id-static' OR p.type = 'id-dynamic'; IF v_top_parent IS NOT NULL THEN SELECT part_interval::bigint INTO v_top_interval FROM @extschema@.part_config WHERE parent_table = v_top_parent; v_id_position := (length(p_parent_table) - position('p_' in reverse(p_parent_table))) + 2; v_sub_id_min = substring(p_parent_table from v_id_position)::bigint; v_sub_id_max = (v_sub_id_min + v_top_interval) - 1; END IF; SELECT tableowner, schemaname, tablename, tablespace INTO v_parent_owner, v_parent_schema, v_parent_tablename, v_parent_tablespace FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; 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. 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; END IF; END IF; v_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_id::text, TRUE); -- If child table already exists, skip creation SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_tablename IS NOT NULL 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_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating new partition '||v_partition_name||' with interval from '||v_id||' to '||(v_id + v_part_interval)-1); END IF; SELECT relpersistence INTO v_unlogged FROM pg_catalog.pg_class WHERE oid::regclass = p_parent_table::regclass; v_sql := 'CREATE'; IF v_unlogged = 'u' THEN v_sql := v_sql || ' UNLOGGED'; END IF; v_sql := v_sql || ' TABLE '||v_partition_name||' (LIKE '||p_parent_table||' INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING STORAGE INCLUDING COMMENTS)'; SELECT relhasoids INTO v_hasoids FROM pg_catalog.pg_class WHERE oid::regclass = p_parent_table::regclass; IF v_hasoids IS TRUE THEN v_sql := v_sql || ' WITH (OIDS)'; END IF; EXECUTE v_sql; SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_parent_tablespace IS NOT NULL THEN EXECUTE 'ALTER TABLE '||v_partition_name||' SET TABLESPACE '||v_parent_tablespace; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' ADD CONSTRAINT '||v_tablename||'_partition_check CHECK ('||v_control||'>='||quote_literal(v_id)||' AND '||v_control||'<'||quote_literal(v_id + v_part_interval)||')'; EXECUTE 'ALTER TABLE '||v_partition_name||' INHERIT '||p_parent_table; FOR v_parent_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = p_parent_table GROUP BY grantee LOOP EXECUTE 'GRANT '||array_to_string(v_parent_grant.types, ',')||' ON '||v_partition_name||' TO '||v_parent_grant.grantee; SELECT array_agg(r) INTO v_revoke FROM (SELECT unnest(v_all) AS r EXCEPT SELECT unnest(v_parent_grant.types)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE '||array_to_string(v_revoke, ',')||' ON '||v_partition_name||' FROM '||v_parent_grant.grantee||' CASCADE'; END IF; v_grantees := array_append(v_grantees, v_parent_grant.grantee::text); END LOOP; -- Revoke all privileges from roles that have none on the parent IF v_grantees IS NOT NULL THEN SELECT array_agg(r) INTO v_revoke FROM ( SELECT DISTINCT grantee::text AS r FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = v_partition_name EXCEPT SELECT unnest(v_grantees)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE ALL ON '||v_partition_name||' FROM '||array_to_string(v_revoke, ','); END IF; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' OWNER TO '||v_parent_owner; IF v_inherit_fk THEN PERFORM @extschema@.apply_foreign_keys(quote_ident(v_parent_schema)||'.'||quote_ident(v_parent_tablename), v_partition_name); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); 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 FOR v_row IN SELECT sub_parent , sub_control , sub_type , sub_part_interval , sub_constraint_cols , sub_premake , sub_inherit_fk , sub_retention , sub_retention_schema , sub_retention_keep_table , sub_retention_keep_index , sub_use_run_maintenance , sub_jobmon FROM @extschema@.part_config_sub WHERE sub_parent = p_parent_table LOOP IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Subpartitioning '||v_partition_name); END IF; v_sql := format('SELECT @extschema@.create_parent( p_parent_table := %L , p_control := %L , p_type := %L , p_interval := %L , p_constraint_cols := %L , p_premake := %L , p_use_run_maintenance := %L , p_inherit_fk := %L , p_jobmon := %L )' , v_partition_name , v_row.sub_control , v_row.sub_type , v_row.sub_part_interval , v_row.sub_constraint_cols , v_row.sub_premake , v_row.sub_inherit_fk , v_row.sub_use_run_maintenance , v_row.sub_jobmon); EXECUTE v_sql; 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 WHERE parent_table = v_partition_name; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END LOOP; -- end sub partitioning LOOP IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); 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 EXECUTE 'ANALYZE '||p_parent_table; END IF; IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_partition_created; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN CREATE TABLE: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to create a child table in a time-based partition set */ CREATE FUNCTION create_partition_time (p_parent_table text, p_partition_times timestamp[], p_analyze boolean DEFAULT true) RETURNS boolean LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_all text[] := ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER']; v_analyze boolean := FALSE; v_control text; v_datetime_string text; v_grantees text[]; v_hasoids boolean; v_inherit_fk boolean; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_parent_grant record; v_parent_owner text; v_parent_schema text; v_parent_tablename text; v_partition_created boolean := false; v_partition_name text; v_partition_suffix text; v_parent_tablespace text; v_part_interval interval; v_partition_timestamp_end timestamp; v_partition_timestamp_start timestamp; v_quarter text; v_revoke text[]; v_row record; v_sql text; v_step_id bigint; v_step_overflow_id bigint; v_sub_timestamp_max timestamp; v_sub_timestamp_min timestamp; v_tablename text; v_time_position int; v_top_interval interval; v_top_parent text; v_trunc_value text; v_time timestamp; v_type text; v_unlogged char; v_year text; BEGIN SELECT type , control , part_interval , inherit_fk , jobmon , datetime_string INTO v_type , v_control , v_part_interval , v_inherit_fk , v_jobmon , v_datetime_string FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic' OR type = 'time-custom'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; -- Check if parent table is a subpartition of an already existing time-based partition set managed by pg_partman -- If so, limit what child tables can be created based on parent suffix WITH top_oid AS ( SELECT i.inhparent AS top_parent_oid FROM pg_catalog.pg_class c JOIN pg_catalog.pg_inherits i ON c.oid = i.inhrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname||'.'||c.relname = p_parent_table ) SELECT n.nspname||'.'||c.relname INTO v_top_parent FROM pg_catalog.pg_class c JOIN top_oid t ON c.oid = t.top_parent_oid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace JOIN @extschema@.part_config p ON p.parent_table = n.nspname||'.'||c.relname WHERE c.oid = t.top_parent_oid AND p.type = 'time-static' OR p.type = 'time-dynamic'; IF v_top_parent IS NOT NULL THEN SELECT part_interval::interval INTO v_top_interval FROM @extschema@.part_config WHERE parent_table = v_top_parent; v_time_position := (length(p_parent_table) - position('p_' in reverse(p_parent_table))) + 2; IF v_part_interval::interval <> '3 months' OR (v_part_interval::interval = '3 months' AND v_type = 'time-custom') THEN v_sub_timestamp_min := to_timestamp(substring(p_parent_table from v_time_position), v_datetime_string); ELSE -- to_timestamp doesn't recognize 'Q' date string formater. Handle it v_year := split_part(substring(p_parent_table from v_time_position), 'q', 1); v_quarter := split_part(substring(p_parent_table from v_time_position), 'q', 2); CASE WHEN v_quarter = '1' THEN v_sub_timestamp_min := to_timestamp(v_year || '-01-01', 'YYYY-MM-DD'); WHEN v_quarter = '2' THEN v_sub_timestamp_min := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); WHEN v_quarter = '3' THEN v_sub_timestamp_min := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); WHEN v_quarter = '4' THEN v_sub_timestamp_min := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); END CASE; END IF; v_sub_timestamp_max = (v_sub_timestamp_min + v_top_interval::interval) - '1 sec'::interval; END IF; SELECT tableowner, schemaname, tablename, tablespace INTO v_parent_owner, v_parent_schema, v_parent_tablename, v_parent_tablespace FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; FOREACH v_time IN ARRAY p_partition_times LOOP v_partition_timestamp_start := v_time; BEGIN v_partition_timestamp_end := v_time + v_part_interval; EXCEPTION WHEN datetime_field_overflow THEN 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'); CONTINUE; END; -- This suffix generation code is in partition_data_time() as well v_partition_suffix := to_char(v_time, 'YYYY'); IF v_part_interval < '1 year' AND v_part_interval <> '1 week' THEN v_partition_suffix := v_partition_suffix ||'_'|| to_char(v_time, 'MM'); IF v_part_interval < '1 month' AND v_part_interval <> '1 week' THEN v_partition_suffix := v_partition_suffix ||'_'|| to_char(v_time, 'DD'); IF v_part_interval < '1 day' THEN v_partition_suffix := v_partition_suffix || '_' || to_char(v_time, 'HH24MI'); IF v_part_interval < '1 minute' THEN v_partition_suffix := v_partition_suffix || to_char(v_time, 'SS'); END IF; -- end < minute IF END IF; -- end < day IF END IF; -- end < month IF END IF; -- end < year IF IF v_part_interval = '1 week' THEN v_partition_suffix := to_char(v_time, 'IYYY') || 'w' || to_char(v_time, 'IW'); END IF; -- "Q" is ignored in to_timestamp, so handle special case IF v_part_interval = '3 months' AND (v_type = 'time-static' OR v_type = 'time-dynamic') THEN v_year := to_char(v_time, 'YYYY'); v_quarter := to_char(v_time, 'Q'); v_partition_suffix := v_year || 'q' || v_quarter; END IF; -- 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 CONTINUE; END IF; END IF; v_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_partition_suffix, TRUE); SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_tablename IS NOT NULL 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_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating new partition '||v_partition_name||' with interval from '||v_partition_timestamp_start||' to '||(v_partition_timestamp_end-'1sec'::interval)); END IF; SELECT relpersistence INTO v_unlogged FROM pg_catalog.pg_class WHERE oid::regclass = p_parent_table::regclass; v_sql := 'CREATE'; IF v_unlogged = 'u' THEN v_sql := v_sql || ' UNLOGGED'; END IF; v_sql := v_sql || ' TABLE '||v_partition_name||' (LIKE '||p_parent_table||' INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING STORAGE INCLUDING COMMENTS)'; SELECT relhasoids INTO v_hasoids FROM pg_catalog.pg_class WHERE oid::regclass = p_parent_table::regclass; IF v_hasoids IS TRUE THEN v_sql := v_sql || ' WITH (OIDS)'; END IF; EXECUTE v_sql; SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_parent_tablespace IS NOT NULL THEN EXECUTE 'ALTER TABLE '||v_partition_name||' SET TABLESPACE '||v_parent_tablespace; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' ADD CONSTRAINT '||v_tablename||'_partition_check CHECK ('||v_control||'>='||quote_literal(v_partition_timestamp_start)||' AND '||v_control||'<'||quote_literal(v_partition_timestamp_end)||')'; EXECUTE 'ALTER TABLE '||v_partition_name||' INHERIT '||p_parent_table; -- If custom time, set extra config options. IF v_type = 'time-custom' THEN INSERT INTO @extschema@.custom_time_partitions (parent_table, child_table, partition_range) VALUES ( p_parent_table, v_partition_name, tstzrange(v_partition_timestamp_start, v_partition_timestamp_end, '[)') ); END IF; FOR v_parent_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = p_parent_table GROUP BY grantee LOOP EXECUTE 'GRANT '||array_to_string(v_parent_grant.types, ',')||' ON '||v_partition_name||' TO '||v_parent_grant.grantee; SELECT array_agg(r) INTO v_revoke FROM (SELECT unnest(v_all) AS r EXCEPT SELECT unnest(v_parent_grant.types)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE '||array_to_string(v_revoke, ',')||' ON '||v_partition_name||' FROM '||v_parent_grant.grantee||' CASCADE'; END IF; v_grantees := array_append(v_grantees, v_parent_grant.grantee::text); END LOOP; -- Revoke all privileges from roles that have none on the parent IF v_grantees IS NOT NULL THEN SELECT array_agg(r) INTO v_revoke FROM ( SELECT DISTINCT grantee::text AS r FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = v_partition_name EXCEPT SELECT unnest(v_grantees)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE ALL ON '||v_partition_name||' FROM '||array_to_string(v_revoke, ','); END IF; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' OWNER TO '||v_parent_owner; IF v_inherit_fk THEN PERFORM @extschema@.apply_foreign_keys(quote_ident(v_parent_schema)||'.'||quote_ident(v_parent_tablename), v_partition_name); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); 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 FOR v_row IN SELECT sub_parent , sub_control , sub_type , sub_part_interval , sub_constraint_cols , sub_premake , sub_inherit_fk , sub_retention , sub_retention_schema , sub_retention_keep_table , sub_retention_keep_index , sub_use_run_maintenance , sub_jobmon FROM @extschema@.part_config_sub WHERE sub_parent = p_parent_table LOOP IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Subpartitioning '||v_partition_name); END IF; v_sql := format('SELECT @extschema@.create_parent( p_parent_table := %L , p_control := %L , p_type := %L , p_interval := %L , p_constraint_cols := %L , p_premake := %L , p_use_run_maintenance := %L , p_inherit_fk := %L , p_jobmon := %L )' , v_partition_name , v_row.sub_control , v_row.sub_type , v_row.sub_part_interval , v_row.sub_constraint_cols , v_row.sub_premake , v_row.sub_inherit_fk , v_row.sub_use_run_maintenance , v_row.sub_jobmon); EXECUTE v_sql; 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 WHERE parent_table = v_partition_name; END LOOP; -- end sub partitioning LOOP IF v_jobmon_schema IS NOT NULL THEN IF v_step_overflow_id IS NOT NULL THEN PERFORM fail_job(v_job_id); ELSE PERFORM close_job(v_job_id); END IF; 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 EXECUTE 'ANALYZE '||p_parent_table; END IF; IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_partition_created; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN CREATE TABLE: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to turn a table into the parent of a partition set */ CREATE FUNCTION create_parent( p_parent_table text , p_control text , p_type text , p_interval text , p_constraint_cols text[] DEFAULT NULL , p_premake int DEFAULT 4 , p_use_run_maintenance boolean DEFAULT NULL , p_start_partition text DEFAULT NULL , p_inherit_fk boolean DEFAULT true , p_jobmon boolean DEFAULT true , p_debug boolean DEFAULT false) RETURNS boolean LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_base_timestamp timestamp; v_count int := 1; v_datetime_string text; v_higher_parent text := p_parent_table; v_id_interval bigint; v_id_position int; v_job_id bigint; v_jobmon_schema text; v_last_partition_created boolean; v_max bigint; v_notnull boolean; v_old_search_path text; v_parent_partition_id bigint; v_parent_partition_timestamp timestamp; v_partition_time timestamp; v_partition_time_array timestamp[]; v_partition_id_array bigint[]; v_row record; v_run_maint boolean; v_sql text; v_start_time timestamp; v_starting_partition_id bigint; v_step_id bigint; v_step_overflow_id bigint; v_sub_parent text; v_success boolean := false; v_tablename text; v_time_interval interval; v_time_position int; v_top_parent text := p_parent_table; BEGIN IF position('.' in p_parent_table) = 0 THEN RAISE EXCEPTION 'Parent table must be schema qualified'; END IF; SELECT tablename INTO v_tablename FROM pg_tables WHERE schemaname || '.' || tablename = p_parent_table; IF v_tablename IS NULL THEN RAISE EXCEPTION 'Please create given parent table first: %', p_parent_table; END IF; SELECT attnotnull INTO v_notnull FROM pg_attribute WHERE attrelid = p_parent_table::regclass AND attname = p_control; IF v_notnull = false OR v_notnull IS NULL THEN RAISE EXCEPTION 'Control column (%) for parent table (%) must be NOT NULL', p_control, p_parent_table; END IF; IF NOT @extschema@.check_partition_type(p_type) THEN RAISE EXCEPTION '% is not a valid partitioning type', p_type; END IF; IF p_type = 'time-custom' AND @extschema@.check_version('9.2.0') IS FALSE THEN RAISE EXCEPTION 'The "time-custom" type requires a minimum PostgreSQL version of 9.2.0'; END IF; EXECUTE 'LOCK TABLE '||p_parent_table||' IN ACCESS EXCLUSIVE MODE'; IF p_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF p_use_run_maintenance IS NOT NULL THEN IF p_use_run_maintenance IS FALSE AND (p_type = 'time-static' OR p_type = 'time-dynamic' OR p_type = 'time-custom') THEN RAISE EXCEPTION 'p_run_maintenance cannot be set to false for time based partitioning'; END IF; v_run_maint := p_use_run_maintenance; ELSIF p_type = 'time-static' OR p_type = 'time-dynamic' OR p_type = 'time-custom' THEN v_run_maint := TRUE; ELSIF p_type = 'id-static' OR p_type ='id-dynamic' THEN v_run_maint := FALSE; ELSE RAISE EXCEPTION 'use_run_maintenance value cannot be set NULL'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN SETUP PARENT: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating initial partitions on new parent table: '||p_parent_table); END IF; -- If this parent table has siblings that are also partitioned (subpartitions), ensure it 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 WITH parent_table AS ( SELECT h.inhparent as parent_oid from pg_inherits h where h.inhrelid::regclass = p_parent_table::regclass ), sibling_children as ( select i.inhrelid::regclass::text as tablename from pg_inherits i join parent_table p on i.inhparent = p.parent_oid ) SELECT DISTINCT sub_type , sub_control , sub_part_interval , sub_constraint_cols , sub_premake , sub_inherit_fk , sub_retention , sub_retention_schema , sub_retention_keep_table , sub_retention_keep_index , sub_use_run_maintenance , sub_jobmon FROM @extschema@.part_config_sub a JOIN sibling_children b on a.sub_parent = b.tablename LIMIT 1 LOOP INSERT INTO @extschema@.part_config_sub ( sub_parent , sub_type , sub_control , sub_part_interval , sub_constraint_cols , sub_premake , sub_inherit_fk , sub_retention , sub_retention_schema , sub_retention_keep_table , sub_retention_keep_index , sub_use_run_maintenance , sub_jobmon) VALUES ( p_parent_table , v_row.sub_type , v_row.sub_control , v_row.sub_part_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_use_run_maintenance , v_row.sub_jobmon); END LOOP; IF p_type = 'time-static' OR p_type = 'time-dynamic' OR p_type = 'time-custom' 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 <> 'time-custom' THEN RAISE EXCEPTION 'Must use a predefined time interval if not using type "time-custom". See documentation.'; 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; -- First partition is either the min premake or p_start_partition v_start_time := COALESCE(p_start_partition::timestamp, CURRENT_TIMESTAMP - (v_time_interval * p_premake)); 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 v_datetime_string := 'YYYY'; 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 v_partition_time_array := array_append(v_partition_time_array, v_base_timestamp); LOOP -- If current loop value is less than or equal to the value of the max premake, add time to array. IF (v_base_timestamp + (v_time_interval * v_count)) < (CURRENT_TIMESTAMP + (v_time_interval * p_premake)) THEN BEGIN v_partition_time := (v_base_timestamp + (v_time_interval * v_count))::timestamp; 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. 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'); CONTINUE; END; ELSE EXIT; -- all needed partitions added to array. Exit the loop. END IF; v_count := v_count + 1; END LOOP; INSERT INTO @extschema@.part_config ( parent_table , type , part_interval , control , premake , constraint_cols , datetime_string , use_run_maintenance , inherit_fk , jobmon) VALUES ( p_parent_table , p_type , v_time_interval , p_control , p_premake , p_constraint_cols , v_datetime_string , v_run_maint , p_inherit_fk , p_jobmon); v_last_partition_created := @extschema@.create_partition_time(p_parent_table, v_partition_time_array, false); 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 ( SELECT i.inhparent AS top_parent_oid FROM pg_catalog.pg_inherits i JOIN pg_catalog.pg_class c ON c.oid = i.inhrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname||'.'||c.relname = p_parent_table ) SELECT n.nspname||'.'||c.relname INTO v_top_parent 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 JOIN @extschema@.part_config p ON p.parent_table = n.nspname||'.'||c.relname; IF v_top_parent IS NOT NULL THEN -- If so create the lowest possible partition that is within the boundary of the parent v_time_position := (length(p_parent_table) - position('p_' in reverse(p_parent_table))) + 2; v_parent_partition_timestamp := to_timestamp(substring(p_parent_table from v_time_position), v_datetime_string); IF v_base_timestamp >= v_parent_partition_timestamp THEN WHILE v_base_timestamp >= v_parent_partition_timestamp LOOP v_base_timestamp := v_base_timestamp - v_time_interval; END LOOP; v_base_timestamp := v_base_timestamp + v_time_interval; -- add one back since while loop set it one lower than is needed ELSIF v_base_timestamp < v_parent_partition_timestamp THEN WHILE v_base_timestamp < v_parent_partition_timestamp LOOP v_base_timestamp := v_base_timestamp + v_time_interval; END LOOP; -- Don't need to remove one since new starting time will fit in top parent interval 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); ELSE -- Currently unknown edge case if code gets here RAISE EXCEPTION 'No child tables created. Unexpected edge case encountered. Please report this error to author with conditions that led to it.'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Time partitions premade: '||p_premake); END IF; END IF; IF p_type = 'id-static' OR p_type = 'id-dynamic' THEN v_id_interval := p_interval::bigint; IF v_id_interval <= 1 THEN RAISE EXCEPTION 'Interval for serial partitioning must be greater than 1'; END IF; -- Check if parent table is a subpartition of an already existing id partition set managed by pg_partman. WHILE v_higher_parent IS NOT NULL LOOP -- initially set in DECLARE WITH top_oid AS ( SELECT i.inhparent AS top_parent_oid FROM pg_catalog.pg_inherits i JOIN pg_catalog.pg_class c ON c.oid = i.inhrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname||'.'||c.relname = v_higher_parent ) SELECT n.nspname||'.'||c.relname INTO v_higher_parent 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 JOIN @extschema@.part_config p ON p.parent_table = n.nspname||'.'||c.relname WHERE p.type = 'id-static' OR p.type = 'id-dynamic'; IF v_higher_parent IS NOT NULL THEN -- v_top_parent initially set in DECLARE v_top_parent := v_higher_parent; END IF; END LOOP; -- If custom start partition is set, use that. -- If custom start is not set and there is already data, start partitioning with the highest current value and ensure it's grabbed from highest top parent table v_sql := 'SELECT COALESCE('||quote_nullable(p_start_partition::bigint)||', max('||p_control||')::bigint, 0) FROM '||v_top_parent||' LIMIT 1'; EXECUTE v_sql INTO v_max; 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 THEN v_partition_id_array = array_append(v_partition_id_array, (v_starting_partition_id - v_id_interval*i)); END IF; v_partition_id_array = array_append(v_partition_id_array, (v_id_interval*i) + v_starting_partition_id); END LOOP; INSERT INTO @extschema@.part_config ( parent_table , type , part_interval , control , premake , constraint_cols , use_run_maintenance , inherit_fk , jobmon) VALUES ( p_parent_table , p_type , v_id_interval , p_control , p_premake , p_constraint_cols , v_run_maint , p_inherit_fk , p_jobmon); v_last_partition_created := @extschema@.create_partition_id(p_parent_table, v_partition_id_array, false); 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 -- See if it's actually a subpartition of a parent id partition WITH top_oid AS ( SELECT i.inhparent AS top_parent_oid FROM pg_catalog.pg_inherits i JOIN pg_catalog.pg_class c ON c.oid = i.inhrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname||'.'||c.relname = p_parent_table ) SELECT n.nspname||'.'||c.relname INTO v_top_parent 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 JOIN @extschema@.part_config p ON p.parent_table = n.nspname||'.'||c.relname WHERE p.type = 'id-static' OR p.type = 'id-dynamic'; IF v_top_parent IS NOT NULL THEN -- Create the lowest possible partition that is within the boundary of the parent v_id_position := (length(p_parent_table) - position('p_' in reverse(p_parent_table))) + 2; v_parent_partition_id = substring(p_parent_table from v_id_position)::bigint; IF v_starting_partition_id >= v_parent_partition_id THEN WHILE v_starting_partition_id >= v_parent_partition_id LOOP v_starting_partition_id := v_starting_partition_id - v_id_interval; END LOOP; v_starting_partition_id := v_starting_partition_id + v_id_interval; -- add one back since while loop set it one lower than is needed ELSIF v_starting_partition_id < v_parent_partition_id THEN WHILE v_starting_partition_id < v_parent_partition_id LOOP v_starting_partition_id := v_starting_partition_id + v_id_interval; END LOOP; -- Don't need to remove one since new starting id will fit in top parent interval 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); ELSE -- Currently unknown edge case if code gets here RAISE EXCEPTION 'No child tables created. Unexpected edge case encountered. Please report this error to author with conditions that led to it.'; END IF; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Creating partition function'); END IF; IF p_type = 'time-static' OR p_type = 'time-dynamic' OR p_type = 'time-custom' THEN PERFORM @extschema@.create_function_time(p_parent_table); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Time function created'); END IF; ELSIF p_type = 'id-static' OR p_type = 'id-dynamic' THEN PERFORM @extschema@.create_function_id(p_parent_table); 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); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); IF v_step_overflow_id IS NOT NULL THEN PERFORM fail_job(v_job_id); ELSE PERFORM close_job(v_job_id); END IF; EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; v_success := true; RETURN v_success; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN CREATE PARENT: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''Partition creation for table '||p_parent_table||' failed'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Create the trigger function for the parent table of a time-based partition set */ CREATE FUNCTION create_function_time(p_parent_table text) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_control text; v_count int; v_current_partition_name text; v_current_partition_timestamp timestamptz; v_datetime_string text; v_final_partition_timestamp timestamptz; v_function_name text; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_new_length int; v_next_partition_name text; v_next_partition_timestamp timestamptz; v_parent_schema text; v_parent_tablename text; v_part_interval interval; v_premake int; v_prev_partition_name text; v_prev_partition_timestamp timestamptz; v_step_id bigint; v_trig_func text; v_type text; BEGIN SELECT type , part_interval::interval , control , premake , datetime_string , jobmon INTO v_type , v_part_interval , v_control , v_premake , v_datetime_string , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic' OR type = 'time-custom'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE FUNCTION: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating partition function for table '||p_parent_table); END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; v_function_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, '_part_trig_func', FALSE); IF v_type = 'time-static' THEN CASE WHEN v_part_interval = '15 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0); WHEN v_part_interval = '30 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0); WHEN v_part_interval = '1 hour' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP); WHEN v_part_interval = '1 day' THEN v_current_partition_timestamp := date_trunc('day', CURRENT_TIMESTAMP); WHEN v_part_interval = '1 week' THEN v_current_partition_timestamp := date_trunc('week', CURRENT_TIMESTAMP); WHEN v_part_interval = '1 month' THEN v_current_partition_timestamp := date_trunc('month', CURRENT_TIMESTAMP); -- Type time-static plus this interval is the special quarterly interval WHEN v_part_interval = '3 months' THEN v_current_partition_timestamp := date_trunc('quarter', CURRENT_TIMESTAMP); WHEN v_part_interval = '1 year' THEN v_current_partition_timestamp := date_trunc('year', CURRENT_TIMESTAMP); END CASE; v_current_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, to_char(v_current_partition_timestamp, v_datetime_string), TRUE); v_next_partition_timestamp := v_current_partition_timestamp + v_part_interval::interval; v_trig_func := 'CREATE OR REPLACE FUNCTION '||v_function_name||'() RETURNS trigger LANGUAGE plpgsql AS $t$ BEGIN IF TG_OP = ''INSERT'' THEN IF NEW.'||v_control||' >= '||quote_literal(v_current_partition_timestamp)||' AND NEW.'||v_control||' < '||quote_literal(v_next_partition_timestamp)|| ' THEN '; SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname ||'.'||tablename = v_current_partition_name; IF v_count > 0 THEN v_trig_func := v_trig_func || ' INSERT INTO '||v_current_partition_name||' VALUES (NEW.*); '; ELSE v_trig_func := v_trig_func || ' -- Child table for current values does not exist in this partition set, so write to parent RETURN NEW;'; END IF; FOR i IN 1..v_premake LOOP v_prev_partition_timestamp := v_current_partition_timestamp - (v_part_interval::interval * i); v_next_partition_timestamp := v_current_partition_timestamp + (v_part_interval::interval * i); v_final_partition_timestamp := v_next_partition_timestamp + (v_part_interval::interval); v_prev_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, to_char(v_prev_partition_timestamp, v_datetime_string), TRUE); v_next_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, to_char(v_next_partition_timestamp, v_datetime_string), TRUE); -- Check that child table exist before making a rule to insert to them. -- Handles edge case of changing premake immediately after running create_parent(). SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname ||'.'||tablename = v_prev_partition_name; IF v_count > 0 THEN v_trig_func := v_trig_func ||' ELSIF NEW.'||v_control||' >= '||quote_literal(v_prev_partition_timestamp)||' AND NEW.'||v_control||' < '|| quote_literal(v_prev_partition_timestamp + v_part_interval::interval)|| ' THEN INSERT INTO '||v_prev_partition_name||' VALUES (NEW.*);'; END IF; SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname ||'.'||tablename = v_next_partition_name; IF v_count > 0 THEN v_trig_func := v_trig_func ||' ELSIF NEW.'||v_control||' >= '||quote_literal(v_next_partition_timestamp)||' AND NEW.'||v_control||' < '|| quote_literal(v_final_partition_timestamp)|| ' THEN INSERT INTO '||v_next_partition_name||' VALUES (NEW.*);'; END IF; END LOOP; v_trig_func := v_trig_func ||' ELSE RETURN NEW; END IF; END IF; RETURN NULL; END $t$;'; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Added function for current time interval: '|| v_current_partition_timestamp||' to '||(v_final_partition_timestamp-'1sec'::interval)); END IF; ELSIF v_type = 'time-dynamic' THEN v_trig_func := 'CREATE OR REPLACE FUNCTION '||v_function_name||'() RETURNS trigger LANGUAGE plpgsql AS $t$ DECLARE v_count int; v_partition_name text; v_partition_timestamp timestamptz; BEGIN IF TG_OP = ''INSERT'' THEN '; CASE WHEN v_part_interval = '15 mins' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''hour'', NEW.'||v_control||') + ''15min''::interval * floor(date_part(''minute'', NEW.'||v_control||') / 15.0);'; WHEN v_part_interval = '30 mins' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''hour'', NEW.'||v_control||') + ''30min''::interval * floor(date_part(''minute'', NEW.'||v_control||') / 30.0);'; WHEN v_part_interval = '1 hour' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''hour'', NEW.'||v_control||');'; WHEN v_part_interval = '1 day' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''day'', NEW.'||v_control||');'; WHEN v_part_interval = '1 week' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''week'', NEW.'||v_control||');'; WHEN v_part_interval = '1 month' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''month'', NEW.'||v_control||');'; WHEN v_part_interval = '3 months' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''quarter'', NEW.'||v_control||');'; WHEN v_part_interval = '1 year' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''year'', NEW.'||v_control||');'; END CASE; v_trig_func := v_trig_func||' v_partition_name := @extschema@.check_name_length('''||v_parent_tablename||''', '''||v_parent_schema||''', to_char(v_partition_timestamp, '||quote_literal(v_datetime_string)||'), TRUE); SELECT count(*) INTO v_count FROM pg_tables WHERE schemaname ||''.''|| tablename = v_partition_name; IF v_count > 0 THEN EXECUTE ''INSERT INTO ''||v_partition_name||'' VALUES($1.*)'' USING NEW; ELSE RETURN NEW; END IF; END IF; RETURN NULL; END $t$;'; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Added function for dynamic time table: '||p_parent_table); END IF; ELSIF v_type = 'time-custom' THEN v_trig_func := 'CREATE OR REPLACE FUNCTION '||v_function_name||'() RETURNS trigger LANGUAGE plpgsql AS $t$ DECLARE v_child_table text; v_count int; BEGIN SELECT child_table INTO v_child_table FROM @extschema@.custom_time_partitions WHERE partition_range @> NEW.'||v_control||' AND parent_table = '||quote_literal(p_parent_table)||'; SELECT count(*) INTO v_count FROM pg_tables WHERE schemaname ||''.''|| tablename = v_child_table; IF v_count > 0 THEN EXECUTE ''INSERT INTO ''||v_child_table||'' VALUES ($1.*)'' USING NEW; ELSE RETURN NEW; END IF; RETURN NULL; END $t$;'; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Added function for custom time table: '||p_parent_table); END IF; ELSE RAISE EXCEPTION 'ERROR: Invalid time partitioning type given: %', v_type; END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN CREATE FUNCTION: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Create the trigger function for the parent table of an id-based partition set */ CREATE FUNCTION create_function_id(p_parent_table text) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_control text; v_count int; v_current_partition_name text; v_current_partition_id bigint; v_datetime_string text; v_final_partition_id bigint; v_function_name text; v_higher_parent text := p_parent_table; v_id_position int; v_job_id bigint; v_jobmon text; v_jobmon_schema text; v_last_partition text; v_max bigint; v_next_partition_id bigint; v_next_partition_name text; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_part_interval bigint; v_premake int; v_prev_partition_id bigint; v_prev_partition_name text; v_run_maint boolean; v_step_id bigint; v_top_parent text := p_parent_table; v_trig_func text; v_type text; BEGIN SELECT type , part_interval::bigint , control , premake , use_run_maintenance , jobmon INTO v_type , v_part_interval , v_control , v_premake , v_run_maint , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; SELECT show_partitions INTO v_last_partition FROM @extschema@.show_partitions(p_parent_table, 'DESC') LIMIT 1; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE FUNCTION: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating partition function for table '||p_parent_table); END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; v_function_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, '_part_trig_func', FALSE); IF v_type = 'id-static' THEN -- Get the highest level top parent if multi-level partitioned in order to get proper max() value below WHILE v_higher_parent IS NOT NULL LOOP -- initially set in DECLARE WITH top_oid AS ( SELECT i.inhparent AS top_parent_oid FROM pg_catalog.pg_inherits i JOIN pg_catalog.pg_class c ON c.oid = i.inhrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname||'.'||c.relname = v_higher_parent ) SELECT n.nspname||'.'||c.relname INTO v_higher_parent 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 JOIN @extschema@.part_config p ON p.parent_table = n.nspname||'.'||c.relname WHERE p.type = 'id-static' OR p.type = 'id-dynamic'; IF v_higher_parent IS NOT NULL THEN -- initially set in DECLARE v_top_parent := v_higher_parent; END IF; END LOOP; EXECUTE 'SELECT COALESCE(max('||v_control||'), 0) FROM '||v_top_parent INTO v_max; v_current_partition_id = v_max - (v_max % v_part_interval); v_next_partition_id := v_current_partition_id + v_part_interval; v_current_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_current_partition_id::text, TRUE); v_trig_func := 'CREATE OR REPLACE FUNCTION '||v_function_name||'() RETURNS trigger LANGUAGE plpgsql AS $t$ DECLARE v_current_partition_id bigint; v_last_partition text := '||quote_literal(v_last_partition)||'; v_id_position int; v_next_partition_id bigint; v_next_partition_name text; v_partition_created boolean; BEGIN IF TG_OP = ''INSERT'' THEN IF NEW.'||v_control||' >= '||v_current_partition_id||' AND NEW.'||v_control||' < '||v_next_partition_id|| ' THEN '; SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname ||'.'||tablename = v_current_partition_name; IF v_count > 0 THEN v_trig_func := v_trig_func || ' INSERT INTO '||v_current_partition_name||' VALUES (NEW.*); '; ELSE v_trig_func := v_trig_func || ' -- Child table for current values does not exist in this partition set, so write to parent RETURN NEW;'; END IF; FOR i IN 1..v_premake LOOP v_prev_partition_id := v_current_partition_id - (v_part_interval * i); v_next_partition_id := v_current_partition_id + (v_part_interval * i); v_final_partition_id := v_next_partition_id + v_part_interval; v_prev_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_prev_partition_id::text, TRUE); v_next_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_next_partition_id::text, TRUE); -- Check that child table exist before making a rule to insert to them. -- Handles edge case of changing premake immediately after running create_parent(). SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname ||'.'||tablename = v_prev_partition_name; IF v_count > 0 THEN -- Only handle previous partitions if they're starting above zero IF v_prev_partition_id >= 0 THEN v_trig_func := v_trig_func ||' ELSIF NEW.'||v_control||' >= '||v_prev_partition_id||' AND NEW.'||v_control||' < '||v_prev_partition_id + v_part_interval|| ' THEN INSERT INTO '||v_prev_partition_name||' VALUES (NEW.*); '; END IF; END IF; SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname ||'.'||tablename = v_next_partition_name; IF v_count > 0 THEN v_trig_func := v_trig_func ||' ELSIF NEW.'||v_control||' >= '||v_next_partition_id||' AND NEW.'||v_control||' < '||v_final_partition_id|| ' THEN INSERT INTO '||v_next_partition_name||' VALUES (NEW.*);'; END IF; END LOOP; v_trig_func := v_trig_func ||' ELSE RETURN NEW; END IF;'; IF v_run_maint IS FALSE THEN v_trig_func := v_trig_func ||' v_current_partition_id := NEW.'||v_control||' - (NEW.'||v_control||' % '||v_part_interval||'); IF (NEW.'||v_control||' % '||v_part_interval||') > ('||v_part_interval||' / 2) THEN v_id_position := (length(v_last_partition) - position(''p_'' in reverse(v_last_partition))) + 2; v_next_partition_id := (substring(v_last_partition from v_id_position)::bigint) + '||v_part_interval||'; WHILE ((v_next_partition_id - v_current_partition_id) / '||v_part_interval||') <= '||v_premake||' LOOP v_partition_created := @extschema@.create_partition_id('||quote_literal(p_parent_table)||', ARRAY[v_next_partition_id]); IF v_partition_created THEN PERFORM @extschema@.create_function_id('||quote_literal(p_parent_table)||'); PERFORM @extschema@.apply_constraints('||quote_literal(p_parent_table)||'); END IF; v_next_partition_id := v_next_partition_id + '||v_part_interval||'; END LOOP; END IF;'; END IF; v_trig_func := v_trig_func ||' END IF; RETURN NULL; END $t$;'; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Added function for current id interval: '||v_current_partition_id||' to '||v_final_partition_id-1); END IF; ELSIF v_type = 'id-dynamic' THEN -- The return inside the partition creation check is there to keep really high ID values from creating new partitions. v_trig_func := 'CREATE OR REPLACE FUNCTION '||v_function_name||'() RETURNS trigger LANGUAGE plpgsql AS $t$ DECLARE v_count int; v_current_partition_id bigint; v_current_partition_name text; v_id_position int; v_last_partition text := '||quote_literal(v_last_partition)||'; v_last_partition_id bigint; v_next_partition_id bigint; v_next_partition_name text; v_partition_created boolean; BEGIN IF TG_OP = ''INSERT'' THEN v_current_partition_id := NEW.'||v_control||' - (NEW.'||v_control||' % '||v_part_interval||'); v_current_partition_name := @extschema@.check_name_length('''||v_parent_tablename||''', '''||v_parent_schema||''', v_current_partition_id::text, TRUE); SELECT count(*) INTO v_count FROM pg_tables WHERE schemaname ||''.''|| tablename = v_current_partition_name; IF v_count > 0 THEN EXECUTE ''INSERT INTO ''||v_current_partition_name||'' VALUES($1.*)'' USING NEW; ELSE RETURN NEW; END IF;'; IF v_run_maint IS FALSE THEN v_trig_func := v_trig_func ||' IF (NEW.'||v_control||' % '||v_part_interval||') > ('||v_part_interval||' / 2) THEN v_id_position := (length(v_last_partition) - position(''p_'' in reverse(v_last_partition))) + 2; v_last_partition_id = substring(v_last_partition from v_id_position)::bigint; v_next_partition_id := v_last_partition_id + '||v_part_interval||'; IF NEW.'||v_control||' >= v_next_partition_id THEN RETURN NEW; END IF; WHILE ((v_next_partition_id - v_current_partition_id) / '||v_part_interval||') <= '||v_premake||' LOOP v_partition_created := @extschema@.create_partition_id('||quote_literal(p_parent_table)||', ARRAY[v_next_partition_id]); IF v_partition_created THEN PERFORM @extschema@.create_function_id('||quote_literal(p_parent_table)||'); PERFORM @extschema@.apply_constraints('||quote_literal(p_parent_table)||'); END IF; v_next_partition_id := v_next_partition_id + '||v_part_interval||'; END LOOP; END IF;'; END IF; v_trig_func := v_trig_func ||' END IF; RETURN NULL; END $t$;'; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Added function for dynamic id table: '||p_parent_table); END IF; ELSE RAISE EXCEPTION 'ERROR: Invalid id partitioning type given: %', v_type; END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN CREATE FUNCTION: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''Partition function maintenance for table '||p_parent_table||' failed'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Populate the child table(s) of a time-based partition set with old data from the original parent */ CREATE OR REPLACE FUNCTION 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') RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_control text; v_datetime_string text; v_current_partition_name text; v_max_partition_timestamp timestamp; v_last_partition text; v_lock_iter int := 1; v_lock_obtained boolean := FALSE; v_min_partition_timestamp timestamp; v_parent_schema text; v_parent_tablename text; v_part_interval interval; v_partition_suffix text; v_partition_timestamp timestamp[]; v_quarter text; v_rowcount bigint; v_sql text; v_start_control timestamp; v_time_position int; v_total_rows bigint := 0; v_type text; v_year text; BEGIN SELECT type , part_interval::interval , control , datetime_string INTO v_type , v_part_interval , v_control , v_datetime_string FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic' OR type = 'time-custom'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF p_batch_interval IS NULL OR p_batch_interval > v_part_interval THEN p_batch_interval := v_part_interval; END IF; SELECT show_partitions INTO v_last_partition FROM @extschema@.show_partitions(p_parent_table, 'DESC') LIMIT 1; FOR i IN 1..p_batch_count LOOP IF p_order = 'ASC' THEN EXECUTE 'SELECT min('||v_control||') FROM ONLY '||p_parent_table INTO v_start_control; ELSIF p_order = 'DESC' THEN EXECUTE 'SELECT max('||v_control||') FROM ONLY '||p_parent_table INTO v_start_control; ELSE RAISE EXCEPTION 'Invalid value for p_order. Must be ASC or DESC'; END IF; IF v_start_control IS NULL THEN EXIT; END IF; IF v_type = 'time-static' OR v_type = 'time-dynamic' THEN CASE WHEN v_part_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_part_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_part_interval = '1 hour' THEN v_min_partition_timestamp := date_trunc('hour', v_start_control); WHEN v_part_interval = '1 day' THEN v_min_partition_timestamp := date_trunc('day', v_start_control); WHEN v_part_interval = '1 week' THEN v_min_partition_timestamp := date_trunc('week', v_start_control); WHEN v_part_interval = '1 month' THEN v_min_partition_timestamp := date_trunc('month', v_start_control); WHEN v_part_interval = '3 months' THEN v_min_partition_timestamp := date_trunc('quarter', v_start_control); WHEN v_part_interval = '1 year' THEN v_min_partition_timestamp := date_trunc('year', v_start_control); END CASE; ELSIF v_type = 'time-custom' THEN -- Keep going backwards, checking if the time interval encompases the current v_start_control value v_time_position := (length(v_last_partition) - position('p_' in reverse(v_last_partition))) + 2; v_min_partition_timestamp := to_timestamp(substring(v_last_partition from v_time_position), v_datetime_string); v_max_partition_timestamp := v_min_partition_timestamp + v_part_interval; LOOP IF v_start_control >= v_min_partition_timestamp AND v_start_control < v_max_partition_timestamp THEN EXIT; ELSE v_max_partition_timestamp := v_min_partition_timestamp; BEGIN v_min_partition_timestamp := v_min_partition_timestamp - v_part_interval; 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_interval; END; END IF; END LOOP; END IF; v_partition_timestamp := ARRAY[v_min_partition_timestamp]; IF p_order = 'ASC' THEN IF (v_start_control + p_batch_interval) >= (v_min_partition_timestamp + v_part_interval) THEN v_max_partition_timestamp := v_min_partition_timestamp + v_part_interval; ELSE v_max_partition_timestamp := v_start_control + p_batch_interval; END IF; ELSIF p_order = 'DESC' THEN -- Must be greater than max value still in parent table since query below grabs < max v_max_partition_timestamp := v_min_partition_timestamp + v_part_interval; -- Make sure minimum doesn't underflow current partition minimum IF (v_start_control - p_batch_interval) >= v_min_partition_timestamp THEN v_min_partition_timestamp = v_start_control - p_batch_interval; END IF; ELSE RAISE EXCEPTION 'Invalid value for p_order. Must be ASC or DESC'; END IF; -- do some locking with timeout, if required IF p_lock_wait > 0 THEN v_lock_iter := 0; WHILE v_lock_iter <= 5 LOOP v_lock_iter := v_lock_iter + 1; BEGIN v_sql := 'SELECT * FROM ONLY ' || p_parent_table || ' WHERE '||v_control||' >= '||quote_literal(v_min_partition_timestamp)|| ' AND '||v_control||' < '||quote_literal(v_max_partition_timestamp) ||' FOR UPDATE NOWAIT'; EXECUTE v_sql; 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 RETURN -1; END IF; END IF; PERFORM @extschema@.create_partition_time(p_parent_table, v_partition_timestamp); -- This suffix generation code is in create_partition_time() as well v_partition_suffix := to_char(v_min_partition_timestamp, 'YYYY'); IF v_part_interval < '1 year' AND v_part_interval <> '1 week' THEN v_partition_suffix := v_partition_suffix ||'_'|| to_char(v_min_partition_timestamp, 'MM'); IF v_part_interval < '1 month' AND v_part_interval <> '1 week' THEN v_partition_suffix := v_partition_suffix ||'_'|| to_char(v_min_partition_timestamp, 'DD'); IF v_part_interval < '1 day' THEN v_partition_suffix := v_partition_suffix || '_' || to_char(v_min_partition_timestamp, 'HH24MI'); IF v_part_interval < '1 minute' THEN v_partition_suffix := v_partition_suffix || to_char(v_min_partition_timestamp, 'SS'); END IF; -- end < minute IF END IF; -- end < day IF END IF; -- end < month IF END IF; -- end < year IF IF v_part_interval = '1 week' THEN v_partition_suffix := to_char(v_min_partition_timestamp, 'IYYY') || 'w' || to_char(v_min_partition_timestamp, 'IW'); END IF; -- "Q" is ignored in to_timestamp, so handle special case IF v_part_interval = '3 months' AND (v_type = 'time-static' OR v_type = 'time-dynamic') THEN v_year := to_char(v_min_partition_timestamp, 'YYYY'); v_quarter := to_char(v_min_partition_timestamp, 'Q'); v_partition_suffix := v_year || 'q' || v_quarter; END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname||'.'||tablename = p_parent_table; v_current_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_partition_suffix, TRUE); EXECUTE 'WITH partition_data AS ( DELETE FROM ONLY '||p_parent_table||' WHERE '||v_control||' >= '||quote_literal(v_min_partition_timestamp)|| ' AND '||v_control||' < '||quote_literal(v_max_partition_timestamp)||' RETURNING *) INSERT INTO '||v_current_partition_name||' SELECT * FROM partition_data'; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total_rows := v_total_rows + v_rowcount; IF v_rowcount = 0 THEN EXIT; END IF; END LOOP; IF v_type = 'time-static' THEN PERFORM @extschema@.create_function_time(p_parent_table); END IF; RETURN v_total_rows; END $$; /* * Populate the child table(s) of an id-based partition set with old data from the original parent */ CREATE OR REPLACE FUNCTION partition_data_id(p_parent_table text, p_batch_count int DEFAULT 1, p_batch_interval int DEFAULT NULL, p_lock_wait numeric DEFAULT 0, p_order text DEFAULT 'ASC') RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_control text; v_current_partition_name text; 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_part_interval bigint; v_partition_id bigint[]; v_rowcount bigint; v_sql text; v_start_control bigint; v_total_rows bigint := 0; v_type text; BEGIN SELECT type , part_interval::bigint , control INTO v_type , v_part_interval , v_control FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF p_batch_interval IS NULL OR p_batch_interval > v_part_interval THEN p_batch_interval := v_part_interval; END IF; FOR i IN 1..p_batch_count LOOP IF p_order = 'ASC' THEN EXECUTE 'SELECT min('||v_control||') FROM ONLY '||p_parent_table INTO v_start_control; IF v_start_control IS NULL THEN EXIT; END IF; v_min_partition_id = v_start_control - (v_start_control % v_part_interval); v_partition_id := ARRAY[v_min_partition_id]; -- Check if custom batch interval overflows current partition maximum IF (v_start_control + p_batch_interval) >= (v_min_partition_id + v_part_interval) THEN v_max_partition_id := v_min_partition_id + v_part_interval; ELSE v_max_partition_id := v_start_control + p_batch_interval; END IF; ELSIF p_order = 'DESC' THEN EXECUTE 'SELECT max('||v_control||') FROM ONLY '||p_parent_table INTO v_start_control; IF v_start_control IS NULL THEN EXIT; END IF; v_min_partition_id = v_start_control - (v_start_control % v_part_interval); -- Must be greater than max value still in parent table since query below grabs < max v_max_partition_id := v_min_partition_id + v_part_interval; v_partition_id := ARRAY[v_min_partition_id]; -- Make sure minimum doesn't underflow current partition minimum IF (v_start_control - p_batch_interval) >= v_min_partition_id THEN v_min_partition_id = v_start_control - p_batch_interval; END IF; ELSE RAISE EXCEPTION 'Invalid value for p_order. Must be ASC or DESC'; END IF; -- do some locking with timeout, if required IF p_lock_wait > 0 THEN v_lock_iter := 0; WHILE v_lock_iter <= 5 LOOP v_lock_iter := v_lock_iter + 1; BEGIN v_sql := 'SELECT * FROM ONLY ' || p_parent_table || ' WHERE '||v_control||' >= '||quote_literal(v_min_partition_id)|| ' AND '||v_control||' < '||quote_literal(v_max_partition_id) ||' FOR UPDATE NOWAIT'; EXECUTE v_sql; 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 RETURN -1; END IF; END IF; PERFORM @extschema@.create_partition_id(p_parent_table, v_partition_id); SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname||'.'||tablename = p_parent_table; v_current_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_min_partition_id::text, TRUE); EXECUTE 'WITH partition_data AS ( DELETE FROM ONLY '||p_parent_table||' WHERE '||v_control||' >= '||v_min_partition_id|| ' AND '||v_control||' < '||v_max_partition_id||' RETURNING *) INSERT INTO '||v_current_partition_name||' SELECT * FROM partition_data'; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total_rows := v_total_rows + v_rowcount; IF v_rowcount = 0 THEN EXIT; END IF; END LOOP; IF v_type = 'id-static' THEN PERFORM @extschema@.create_function_id(p_parent_table); END IF; RETURN v_total_rows; END $$; /* * Function to manage pre-creation of the next partitions in a set. * 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_run_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. * Be aware that constraint exclusion may not work properly until an analyze on the partition set is run. */ CREATE FUNCTION run_maintenance(p_parent_table text DEFAULT NULL, p_analyze boolean DEFAULT true, p_jobmon boolean DEFAULT true) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_create_count int := 0; v_current_partition text; v_current_partition_id bigint; v_current_partition_timestamp timestamp; v_datetime_string text; v_drop_count int := 0; v_id_position int; 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 timestamp; v_next_partition_id bigint; v_next_partition_timestamp timestamp; v_old_search_path text; v_premade_count int; v_quarter text; v_step_id bigint; v_step_overflow_id bigint; v_step_serial_id bigint; v_sub_parent text; v_row record; v_row_sub record; v_tablename text; v_tables_list_sql text; v_time_position int; v_year text; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman run_maintenance')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'Partman maintenance already running.'; RETURN; END IF; IF p_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN RUN MAINTENANCE'); v_step_id := add_step(v_job_id, 'Running maintenance loop'); END IF; v_tables_list_sql := 'SELECT parent_table , type , part_interval , control , premake , datetime_string , undo_in_progress FROM @extschema@.part_config'; IF p_parent_table IS NULL THEN v_tables_list_sql := v_tables_list_sql || ' WHERE use_run_maintenance = true'; ELSE v_tables_list_sql := v_tables_list_sql || format(' WHERE parent_table = %L', p_parent_table); END IF; FOR v_row IN EXECUTE v_tables_list_sql LOOP CONTINUE WHEN v_row.undo_in_progress; SELECT show_partitions INTO v_last_partition FROM @extschema@.show_partitions(v_row.parent_table, 'DESC') LIMIT 1; IF v_row.type = 'time-static' OR v_row.type = 'time-dynamic' OR v_row.type = 'time-custom' THEN IF v_row.type = 'time-static' OR v_row.type = 'time-dynamic' THEN CASE WHEN v_row.part_interval::interval = '15 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0); WHEN v_row.part_interval::interval = '30 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0); WHEN v_row.part_interval::interval = '1 hour' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP); WHEN v_row.part_interval::interval = '1 day' THEN v_current_partition_timestamp := date_trunc('day', CURRENT_TIMESTAMP); WHEN v_row.part_interval::interval = '1 week' THEN v_current_partition_timestamp := date_trunc('week', CURRENT_TIMESTAMP); WHEN v_row.part_interval::interval = '1 month' THEN v_current_partition_timestamp := date_trunc('month', CURRENT_TIMESTAMP); WHEN v_row.part_interval::interval = '3 months' THEN v_current_partition_timestamp := date_trunc('quarter', CURRENT_TIMESTAMP); WHEN v_row.part_interval::interval = '1 year' THEN v_current_partition_timestamp := date_trunc('year', CURRENT_TIMESTAMP); END CASE; ELSIF v_row.type = 'time-custom' THEN SELECT child_table INTO v_current_partition FROM @extschema@.custom_time_partitions WHERE parent_table = v_row.parent_table AND partition_range @> CURRENT_TIMESTAMP; IF v_current_partition IS NULL THEN RAISE EXCEPTION 'Current time partition missing from custom_time_partitions config table for table % and timestamp %', CURRENT_TIMESTAMP, v_row.parent_table; END IF; v_time_position := (length(v_current_partition) - position('p_' in reverse(v_current_partition))) + 2; v_current_partition_timestamp := to_timestamp(substring(v_current_partition from v_time_position), v_row.datetime_string); END IF; v_time_position := (length(v_last_partition) - position('p_' in reverse(v_last_partition))) + 2; IF v_row.part_interval::interval <> '3 months' OR (v_row.part_interval::interval = '3 months' AND v_row.type = 'time-custom') THEN v_last_partition_timestamp := to_timestamp(substring(v_last_partition from v_time_position), v_row.datetime_string); ELSE -- to_timestamp doesn't recognize 'Q' date string formater. Handle it v_year := split_part(substring(v_last_partition from v_time_position), 'q', 1); v_quarter := split_part(substring(v_last_partition from v_time_position), 'q', 2); CASE WHEN v_quarter = '1' THEN v_last_partition_timestamp := to_timestamp(v_year || '-01-01', 'YYYY-MM-DD'); WHEN v_quarter = '2' THEN v_last_partition_timestamp := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); WHEN v_quarter = '3' THEN v_last_partition_timestamp := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); WHEN v_quarter = '4' THEN v_last_partition_timestamp := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); END CASE; END IF; -- Check and see how many premade partitions there are. -- Can be negative when subpartitioning and there are parent partitions in the past compared to current timestamp value. -- abs() prevents run_maintenence from running on those old parent tables v_premade_count = abs(round(EXTRACT('epoch' FROM age(v_last_partition_timestamp, v_current_partition_timestamp)) / EXTRACT('epoch' FROM v_row.part_interval::interval))); v_next_partition_timestamp := v_last_partition_timestamp; -- 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 BEGIN v_next_partition_timestamp := v_next_partition_timestamp + v_row.part_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 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', 'Child partition creation skippd for parent table '||v_partition_time); END IF; RAISE WARNING 'Attempted partition time interval is outside PostgreSQL''s supported time range. Child partition creation skipped for parent table %', v_row.parent_table; CONTINUE; END; v_last_partition_created := @extschema@.create_partition_time(v_row.parent_table, ARRAY[v_next_partition_timestamp], p_analyze); v_create_count := v_create_count + 1; IF v_row.type = 'time-static' AND v_last_partition_created THEN PERFORM @extschema@.create_function_time(v_row.parent_table); END IF; -- Manage additonal constraints if set PERFORM @extschema@.apply_constraints(v_row.parent_table); -- Can be negative when subpartitioning and there are parent partitions in the past compared to current timestamp value. -- abs() prevents run_maintenence from running on those old parent tables v_premade_count = abs(round(EXTRACT('epoch' FROM age(v_next_partition_timestamp, v_current_partition_timestamp)) / EXTRACT('epoch' FROM v_row.part_interval::interval))); END LOOP; ELSIF v_row.type = 'id-static' OR v_row.type ='id-dynamic' THEN -- This doesn't need the overall max of a full subpartition set, just the max of the current partition set EXECUTE 'SELECT '||v_row.control||' - ('||v_row.control||' % '||v_row.part_interval::int||') FROM '||v_row.parent_table||' WHERE '||v_row.control||' = (SELECT max('||v_row.control||') FROM '||v_row.parent_table||')' INTO v_current_partition_id; v_id_position := (length(v_last_partition) - position('p_' in reverse(v_last_partition))) + 2; v_last_partition_id = substring(v_last_partition from v_id_position)::bigint; -- This catches if there's invalid data in a parent table set that's outside all child table ranges. IF v_last_partition_id < v_current_partition_id THEN IF v_jobmon_schema IS NOT NULL THEN v_step_serial_id := add_step(v_job_id, 'Found inconsistent data in serial partition set.'); PERFORM update_step(v_step_serial_id, 'CRITICAL', 'Child partition creation skipped for parent table '||v_row.parent_table||'. Current max serial id value ('||v_current_partition_id||') is greater than the id range covered by the last partition created ('||v_last_partition||'). Run check_parent() to find possible cause.'); END IF; RAISE WARNING 'Child partition creation skipped for parent table %. Found inconsistent data in serial partition set. Current max serial id value (%) is greater than the id range covered by the last partition created (%). Run check_parent() to find possible cause.', v_row.parent_table, v_current_partition_id, v_last_partition; CONTINUE; END IF; v_next_partition_id := v_last_partition_id + v_row.part_interval::bigint; -- Can be negative when subpartitioning and there are parent partitions with lower values compared to current id value. -- abs() prevents run_maintenence from running on those old parent tables WHILE (abs((v_next_partition_id - v_current_partition_id) / v_row.part_interval::bigint)) <= v_row.premake LOOP v_last_partition_created := @extschema@.create_partition_id(v_row.parent_table, ARRAY[v_next_partition_id], p_analyze); IF v_last_partition_created THEN PERFORM @extschema@.create_function_id(v_row.parent_table); PERFORM @extschema@.apply_constraints(v_row.parent_table); END IF; v_next_partition_id := v_next_partition_id + v_row.part_interval::bigint; END LOOP; END IF; -- end main IF check for time or id END LOOP; -- end of creation loop -- Manage dropping old partitions if retention option is set FOR v_row IN SELECT parent_table FROM @extschema@.part_config WHERE retention IS NOT NULL AND undo_in_progress = false AND (type = 'time-static' OR type = 'time-dynamic' OR type = 'time-custom') LOOP IF p_parent_table IS NULL THEN v_drop_count := v_drop_count + @extschema@.drop_partition_time(v_row.parent_table); ELSE -- Only run retention on table given in parameter IF p_parent_table <> v_row.parent_table THEN CONTINUE; ELSE v_drop_count := v_drop_count + @extschema@.drop_partition_time(v_row.parent_table); END IF; END IF; END LOOP; FOR v_row IN SELECT parent_table FROM @extschema@.part_config WHERE retention IS NOT NULL AND undo_in_progress = false AND (type = 'id-static' OR type = 'id-dynamic') LOOP IF p_parent_table IS NULL THEN v_drop_count := v_drop_count + @extschema@.drop_partition_id(v_row.parent_table); ELSE -- Only run retention on table given in parameter IF p_parent_table <> v_row.parent_table THEN CONTINUE; ELSE v_drop_count := v_drop_count + @extschema@.drop_partition_id(v_row.parent_table); END IF; END IF; END LOOP; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Partition maintenance finished. '||v_create_count||' partitons made. '||v_drop_count||' partitions dropped.'); IF v_step_overflow_id IS NOT NULL OR v_step_serial_id IS NOT NULL THEN PERFORM fail_job(v_job_id); ELSE PERFORM close_job(v_job_id); END IF; EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN RUN MAINTENANCE'')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * 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. */ CREATE OR REPLACE FUNCTION 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 SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_child_table text; v_control text; v_drop_count int := 0; v_id_position int; v_index record; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_max bigint; v_old_search_path text; v_part_interval bigint; v_partition_id bigint; v_retention bigint; v_retention_keep_index boolean; v_retention_keep_table boolean; v_retention_schema text; v_step_id bigint; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman drop_partition_id')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'drop_partition_id already running.'; RETURN 0; END IF; -- Allow override of configuration options IF p_retention IS NULL THEN SELECT part_interval::bigint , control , retention::bigint , retention_keep_table , retention_keep_index , retention_schema , jobmon INTO v_part_interval , v_control , v_retention , v_retention_keep_table , v_retention_keep_index , v_retention_schema , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic') AND retention IS NOT NULL; IF v_part_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table with a retention period not found: %', p_parent_table; END IF; ELSE SELECT part_interval::bigint , control , retention_keep_table , retention_keep_index , retention_schema , jobmon INTO v_part_interval , v_control , v_retention_keep_table , v_retention_keep_index , v_retention_schema , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic'); v_retention := p_retention; IF v_part_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF p_keep_table IS NOT NULL THEN v_retention_keep_table = p_keep_table; END IF; IF p_keep_index IS NOT NULL THEN v_retention_keep_index = p_keep_index; END IF; IF p_retention_schema IS NOT NULL THEN v_retention_schema = p_retention_schema; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN DROP ID PARTITION: '|| p_parent_table); END IF; EXECUTE 'SELECT max('||v_control||') FROM '||p_parent_table INTO v_max; -- Loop through child tables of the given parent FOR v_child_table IN SELECT n.nspname||'.'||c.relname FROM pg_inherits i join pg_class c ON i.inhrelid = c.oid join pg_namespace n ON c.relnamespace = n.oid WHERE i.inhparent::regclass = p_parent_table::regclass ORDER BY i.inhrelid ASC LOOP v_id_position := (length(v_child_table) - position('p_' in reverse(v_child_table))) + 2; v_partition_id := substring(v_child_table from v_id_position)::bigint; -- Add one interval since partition names contain the start of the constraint period IF v_retention <= (v_max - (v_partition_id + v_part_interval)) THEN IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Uninherit table '||v_child_table||' from '||p_parent_table); END IF; EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; 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, 'Drop table '||v_child_table); END IF; EXECUTE 'DROP TABLE '||v_child_table||' CASCADE'; 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 FOR v_index IN SELECT i.indexrelid::regclass AS name , c.conname FROM pg_catalog.pg_index i LEFT JOIN pg_catalog.pg_constraint c ON i.indexrelid = c.conindid WHERE i.indrelid = v_child_table::regclass LOOP IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Drop index '||v_index.name||' from '||v_child_table); END IF; IF v_index.conname IS NOT NULL THEN EXECUTE 'ALTER TABLE '||v_child_table||' DROP CONSTRAINT '||v_index.conname; ELSE EXECUTE 'DROP INDEX '||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; ELSE -- Move to new schema IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Moving table '||v_child_table||' to schema '||v_retention_schema); END IF; EXECUTE 'ALTER TABLE '||v_child_table||' SET SCHEMA '||v_retention_schema; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; -- End retention schema if -- If child table is a subpartition, remove it from part_config & part_config_sub (should cascade due to FK) DELETE FROM @extschema@.part_config WHERE parent_table = v_child_table; v_drop_count := v_drop_count + 1; END IF; -- End retention check IF END LOOP; -- End child table loop IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Finished partition drop maintenance'); PERFORM update_step(v_step_id, 'OK', v_drop_count||' partitions dropped.'); PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_drop_count; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN DROP ID PARTITION: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to drop child tables from a time-based partition set. * Options to move table to different schema, drop only indexes or actually drop the table from the database. */ CREATE OR REPLACE FUNCTION 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) RETURNS int LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_child_table text; v_datetime_string text; v_drop_count int := 0; v_index record; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_part_interval interval; v_partition_timestamp timestamp; v_quarter text; v_retention interval; v_retention_keep_index boolean; v_retention_keep_table boolean; v_retention_schema text; v_step_id bigint; v_time_position int; v_type text; v_year text; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman drop_partition_time')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'drop_partition_time already running.'; RETURN 0; END IF; -- Allow override of configuration options IF p_retention IS NULL THEN SELECT type , part_interval::interval , retention::interval , retention_keep_table , retention_keep_index , datetime_string , retention_schema , jobmon INTO v_type , v_part_interval , v_retention , v_retention_keep_table , v_retention_keep_index , v_datetime_string , v_retention_schema , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic' OR type = 'time-custom') AND retention IS NOT NULL; IF v_part_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table with a retention period not found: %', p_parent_table; END IF; ELSE SELECT type , part_interval::interval , retention_keep_table , retention_keep_index , datetime_string , retention_schema , jobmon INTO v_type , v_part_interval , v_retention_keep_table , v_retention_keep_index , v_datetime_string , v_retention_schema , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic' OR type = 'time-custom'); v_retention := p_retention; IF v_part_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF p_keep_table IS NOT NULL THEN v_retention_keep_table = p_keep_table; END IF; IF p_keep_index IS NOT NULL THEN v_retention_keep_index = p_keep_index; END IF; IF p_retention_schema IS NOT NULL THEN v_retention_schema = p_retention_schema; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN DROP TIME PARTITION: '|| p_parent_table); END IF; -- Loop through child tables of the given parent FOR v_child_table IN SELECT n.nspname||'.'||c.relname FROM pg_inherits i join pg_class c ON i.inhrelid = c.oid join pg_namespace n ON c.relnamespace = n.oid WHERE i.inhparent::regclass = p_parent_table::regclass ORDER BY i.inhrelid ASC LOOP -- pull out datetime portion of partition's tablename to make the next one v_time_position := (length(v_child_table) - position('p_' in reverse(v_child_table))) + 2; IF v_part_interval <> '3 months' OR (v_part_interval = '3 months' AND v_type = 'time-custom') THEN v_partition_timestamp := to_timestamp(substring(v_child_table from v_time_position), v_datetime_string); ELSE -- to_timestamp doesn't recognize 'Q' date string formater. Handle it v_year := split_part(substring(v_child_table from v_time_position), 'q', 1); v_quarter := split_part(substring(v_child_table from v_time_position), 'q', 2); CASE WHEN v_quarter = '1' THEN v_partition_timestamp := to_timestamp(v_year || '-01-01', 'YYYY-MM-DD'); WHEN v_quarter = '2' THEN v_partition_timestamp := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); WHEN v_quarter = '3' THEN v_partition_timestamp := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); WHEN v_quarter = '4' THEN v_partition_timestamp := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); END CASE; END IF; -- Add one interval since partition names contain the start of the constraint period IF v_retention < (CURRENT_TIMESTAMP - (v_partition_timestamp + v_part_interval)) THEN IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Uninherit table '||v_child_table||' from '||p_parent_table); END IF; EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; 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, 'Drop table '||v_child_table); END IF; EXECUTE 'DROP TABLE '||v_child_table||' CASCADE'; 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 FOR v_index IN SELECT i.indexrelid::regclass AS name , c.conname FROM pg_catalog.pg_index i LEFT JOIN pg_catalog.pg_constraint c ON i.indexrelid = c.conindid WHERE i.indrelid = v_child_table::regclass LOOP IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Drop index '||v_index.name||' from '||v_child_table); END IF; IF v_index.conname IS NOT NULL THEN EXECUTE 'ALTER TABLE '||v_child_table||' DROP CONSTRAINT '||v_index.conname; ELSE EXECUTE 'DROP INDEX '||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; ELSE -- Move to new schema IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Moving table '||v_child_table||' to schema '||v_retention_schema); END IF; EXECUTE 'ALTER TABLE '||v_child_table||' SET SCHEMA '||v_retention_schema; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; -- End retention schema if -- If child table is a subpartition, remove it from part_config & part_config_sub (should cascade due to FK) DELETE FROM @extschema@.part_config WHERE parent_table = v_child_table; v_drop_count := v_drop_count + 1; END IF; -- End retention check IF END LOOP; -- End child table loop IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Finished partition drop maintenance'); PERFORM update_step(v_step_id, 'OK', v_drop_count||' partitions dropped.'); PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_drop_count; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN DROP TIME PARTITION: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to list all child partitions in a set. */ CREATE OR REPLACE FUNCTION show_partitions (p_parent_table text, p_order text DEFAULT 'ASC') RETURNS SETOF text LANGUAGE plpgsql STABLE SECURITY DEFINER AS $$ DECLARE v_datetime_string text; v_part_interval text; v_type text; BEGIN IF p_order NOT IN ('ASC', 'DESC') THEN RAISE EXCEPTION 'p_order paramter must be one of the following values: ASC, DESC'; END IF; SELECT type , part_interval , datetime_string INTO v_type , v_part_interval , v_datetime_string FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_type IN ('time-static', 'time-dynamic', 'time-custom') THEN RETURN QUERY EXECUTE ' SELECT n.nspname::text ||''.''|| 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 = '||quote_literal(p_parent_table)||'::regclass ORDER BY to_timestamp(substring(c.relname from ((length(c.relname) - position(''p_'' in reverse(c.relname))) + 2) ), '||quote_literal(v_datetime_string)||') ' || p_order; ELSIF v_type IN ('id-static', 'id-dynamic') THEN RETURN QUERY EXECUTE ' SELECT n.nspname::text ||''.''|| 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 = '||quote_literal(p_parent_table)||'::regclass ORDER BY substring(c.relname from ((length(c.relname) - position(''p_'' in reverse(c.relname))) + 2) )::bigint ' || p_order; END IF; END $$; /* * Apply constraints managed by partman extension */ CREATE OR REPLACE FUNCTION apply_constraints(p_parent_table text, p_child_table text DEFAULT NULL, p_analyze boolean DEFAULT TRUE, p_debug boolean DEFAULT FALSE) RETURNS void LANGUAGE plpgsql AS $$ DECLARE v_child_table text; v_child_tablename text; v_col text; v_constraint_cols text[]; v_constraint_col_type text; v_constraint_name text; v_datetime_string text; v_existing_constraint_name text; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_last_partition text; v_last_partition_id int; v_last_partition_timestamp timestamp; v_constraint_values record; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_part_interval text; v_partition_suffix text; v_premake int; v_sql text; v_step_id bigint; v_suffix_position int; v_type text; BEGIN SELECT type , part_interval , premake , datetime_string , constraint_cols , jobmon INTO v_type , v_part_interval , v_premake , v_datetime_string , v_constraint_cols , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_constraint_cols IS NULL THEN IF p_debug THEN RAISE NOTICE 'Given parent table (%) not set up for constraint management (constraint_cols is NULL)', p_parent_table; END IF; -- Returns silently to allow this function to be simply called by maintenance processes without having to check if config options are set. RETURN; END IF; SELECT show_partitions INTO v_last_partition FROM @extschema@.show_partitions(p_parent_table, 'DESC') LIMIT 1; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE CONSTRAINT: '||p_parent_table); END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; -- If p_child_table is null, figure out the partition that is the one right before the premake value backwards. IF p_child_table IS NULL THEN IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Automatically determining most recent child on which to apply constraints'); END IF; v_suffix_position := (length(v_last_partition) - position('p_' in reverse(v_last_partition))) + 2; IF v_type IN ('time-static', 'time-dynamic') THEN v_last_partition_timestamp := to_timestamp(substring(v_last_partition from v_suffix_position), v_datetime_string); v_partition_suffix := to_char(v_last_partition_timestamp - (v_part_interval::interval * ((v_premake * 2)+1) ), v_datetime_string); ELSIF v_type IN ('id-static', 'id-dynamic') THEN v_last_partition_id := substring(v_last_partition from v_suffix_position)::int; v_partition_suffix := (v_last_partition_id - (v_part_interval::int * ((v_premake * 2)+1) ))::text; END IF; v_child_table := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_partition_suffix, TRUE); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Target child table: '||v_child_table); END IF; ELSE v_child_table := p_child_table; END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Checking if target child table exists'); END IF; SELECT tablename INTO v_child_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_child_table; IF v_child_tablename IS NULL THEN IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'NOTICE', 'Target child table ('||v_child_table||') does not exist. Skipping constraint creation.'); PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; IF p_debug THEN RAISE NOTICE 'Target child table (%) does not exist. Skipping constraint creation.', v_child_table; END IF; RETURN; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; FOREACH v_col IN ARRAY v_constraint_cols LOOP SELECT c.conname INTO v_existing_constraint_name FROM pg_catalog.pg_constraint c JOIN pg_catalog.pg_attribute a ON c.conrelid = a.attrelid WHERE conrelid = v_child_table::regclass AND c.conname LIKE 'partmanconstr_%' AND c.contype = 'c' AND a.attname = v_col AND ARRAY[a.attnum] <@ c.conkey AND a.attisdropped = false; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Applying new constraint on column: '||v_col); END IF; IF v_existing_constraint_name IS NOT NULL THEN IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'NOTICE', 'Partman managed constraint already exists on this table ('||v_child_table||') and column ('||v_col||'). Skipping creation.'); END IF; RAISE WARNING 'Partman managed constraint already exists on this table (%) and column (%). Skipping creation.', v_child_table, v_col ; CONTINUE; END IF; -- 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 'SELECT min('||v_col||')::text AS min, max('||v_col||')::text AS max FROM '||v_child_table INTO v_constraint_values; IF v_constraint_values IS NOT NULL THEN v_sql := concat('ALTER TABLE ', v_child_table, ' ADD CONSTRAINT ', v_constraint_name , ' CHECK (', v_col, ' >= ', quote_literal(v_constraint_values.min), ' AND ' , v_col, ' <= ', quote_literal(v_constraint_values.max), ')' ); IF p_debug THEN RAISE NOTICE 'Constraint creation query: %', v_sql; END IF; EXECUTE v_sql; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'New constraint created: '||v_sql); END IF; ELSE IF p_debug THEN RAISE NOTICE 'Given column (%) contains all NULLs. No constraint created', v_col; END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'NOTICE', 'Given column ('||v_col||') contains all NULLs. No constraint created'); END IF; END IF; END LOOP; IF p_analyze THEN EXECUTE 'ANALYZE '||p_parent_table; END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN CREATE CONSTRAINT: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to undo time-based partitioning created by this extension */ CREATE OR REPLACE FUNCTION undo_partition_time(p_parent_table text, p_batch_count int DEFAULT 1, p_batch_interval interval DEFAULT NULL, p_keep_table boolean DEFAULT true, p_lock_wait numeric DEFAULT 0) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_batch_loop_count int := 0; v_child_min timestamptz; v_child_loop_total bigint := 0; v_child_table text; v_control text; v_function_name text; v_inner_loop_count int; v_lock_iter int := 1; v_lock_obtained boolean := FALSE; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_move_sql text; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_part_interval interval; v_row record; v_rowcount bigint; v_step_id bigint; v_sub_count int; v_total bigint := 0; v_trig_name text; v_undo_count int := 0; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman undo_time_partition')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'undo_time_partition already running.'; RETURN 0; END IF; SELECT part_interval::interval , control , jobmon INTO v_part_interval , v_control , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic' OR type = 'time-custom'); IF v_part_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; -- Check if any child tables are themselves partitioned or part of an inheritance tree. Prevent undo at this level if so. -- Need to either lock child tables at all levels or handle the proper removal of triggers on all child tables first -- before multi-level undo can be performed safely. FOR v_row IN SELECT show_partitions AS child_table FROM @extschema@.show_partitions(p_parent_table) LOOP SELECT count(*) INTO v_sub_count FROM pg_catalog.pg_inherits WHERE inhparent::regclass = v_row.child_table::regclass; 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.child_table; END IF; END LOOP; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN UNDO PARTITIONING: '||p_parent_table); v_step_id := add_step(v_job_id, 'Undoing partitioning for table '||p_parent_table); END IF; IF p_batch_interval IS NULL THEN p_batch_interval := v_part_interval; END IF; -- Stops new time partitons 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; -- Stop data going into child tables. SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; 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, v_parent_schema, '_part_trig_func', FALSE); SELECT tgname INTO v_trig_name FROM pg_catalog.pg_trigger t WHERE tgname = v_trig_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 'LOCK TABLE ONLY '||p_parent_table||' IN ACCESS EXCLUSIVE MODE NOWAIT'; 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'; RETURN -1; END IF; END IF; -- END p_lock_wait IF EXECUTE 'DROP TRIGGER IF EXISTS '||v_trig_name||' ON '||p_parent_table; END IF; -- END trigger IF v_lock_obtained := FALSE; -- reset for reuse later EXECUTE 'DROP FUNCTION IF EXISTS '||v_function_name||'()'; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Stopped partition creation process. Removed trigger & trigger function'); END IF; <> WHILE v_batch_loop_count < p_batch_count LOOP SELECT n.nspname||'.'||c.relname INTO v_child_table FROM pg_inherits i JOIN pg_class c ON i.inhrelid = c.oid JOIN pg_namespace n ON c.relnamespace = n.oid WHERE i.inhparent::regclass = p_parent_table::regclass ORDER BY i.inhrelid ASC; EXIT WHEN v_child_table IS NULL; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing child partition: '||v_child_table); END IF; EXECUTE 'SELECT min('||v_control||') FROM '||v_child_table INTO v_child_min; IF v_child_min IS NULL THEN -- No rows left in this child table. Remove from partition set. -- lockwait timeout for table 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 'LOCK TABLE ONLY '||v_child_table||' IN ACCESS EXCLUSIVE MODE NOWAIT'; 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 child table for removal from partition set'; RETURN -1; END IF; END IF; -- END p_lock_wait IF v_lock_obtained := FALSE; -- reset for reuse later EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; IF p_keep_table = false THEN EXECUTE 'DROP TABLE '||v_child_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table DROPPED. Moved '||v_child_loop_total||' rows to parent'); END IF; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table UNINHERITED, not DROPPED. Moved '||v_child_loop_total||' rows to parent'); END IF; END IF; v_undo_count := v_undo_count + 1; CONTINUE outer_child_loop; END IF; v_inner_loop_count := 1; v_child_loop_total := 0; <> LOOP -- do some locking with timeout, if required 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 'SELECT * FROM ' || v_child_table || ' WHERE '||v_control||' <= '||quote_literal(v_child_min + (p_batch_interval * v_inner_loop_count)) ||' FOR UPDATE NOWAIT'; 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 batch of rows to move'; RETURN -1; END IF; END IF; -- Get everything from the current child minimum up to the multiples of the given interval v_move_sql := 'WITH move_data AS (DELETE FROM '||v_child_table|| ' WHERE '||v_control||' <= '||quote_literal(v_child_min + (p_batch_interval * v_inner_loop_count))||' RETURNING *) INSERT INTO '||p_parent_table||' SELECT * FROM move_data'; EXECUTE v_move_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total := v_total + v_rowcount; v_child_loop_total := v_child_loop_total + v_rowcount; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Moved '||v_child_loop_total||' rows to parent.'); END IF; EXIT inner_child_loop WHEN v_rowcount = 0; -- exit before loop incr if table is empty v_inner_loop_count := v_inner_loop_count + 1; v_batch_loop_count := v_batch_loop_count + 1; EXIT outer_child_loop WHEN v_batch_loop_count >= p_batch_count; -- Exit outer FOR loop if p_batch_count is reached END LOOP inner_child_loop; END LOOP outer_child_loop; IF v_batch_loop_count < p_batch_count THEN -- FOR loop never ran, so there's no child tables left. DELETE FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing config from pg_partman'); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; RAISE NOTICE 'Copied % row(s) to the parent. Removed % partitions.', v_total, v_undo_count; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Final stats'); PERFORM update_step(v_step_id, 'OK', 'Copied '||v_total||' row(s) to the parent. Removed '||v_undo_count||' partitions.'); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_total; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN UNDO PARTITIONING: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to undo id-based partitioning created by this extension */ CREATE OR REPLACE FUNCTION undo_partition_id(p_parent_table text, p_batch_count int DEFAULT 1, p_batch_interval bigint DEFAULT NULL, p_keep_table boolean DEFAULT true, p_lock_wait numeric DEFAULT 0) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_batch_loop_count int := 0; v_child_loop_total bigint := 0; v_child_min bigint; v_child_table text; v_control text; v_exists int; v_function_name text; v_inner_loop_count int; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_lock_iter int := 1; v_lock_obtained boolean := FALSE; v_move_sql text; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_part_interval bigint; v_row record; v_rowcount bigint; v_step_id bigint; v_sub_count int; v_trig_name text; v_total bigint := 0; v_undo_count int := 0; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman undo_id_partition')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'undo_id_partition already running.'; RETURN 0; END IF; SELECT part_interval::bigint , control , jobmon INTO v_part_interval , v_control , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic'); IF v_part_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; -- Check if any child tables are themselves partitioned or part of an inheritance tree. Prevent undo at this level if so. -- Need to either lock child tables at all levels or handle the proper removal of triggers on all child tables first -- before multi-level undo can be performed safely. FOR v_row IN SELECT show_partitions AS child_table FROM @extschema@.show_partitions(p_parent_table) LOOP SELECT count(*) INTO v_sub_count FROM pg_catalog.pg_inherits WHERE inhparent::regclass = v_row.child_table::regclass; 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.child_table; END IF; END LOOP; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN UNDO PARTITIONING: '||p_parent_table); v_step_id := add_step(v_job_id, 'Undoing partitioning for table '||p_parent_table); END IF; IF p_batch_interval IS NULL THEN p_batch_interval := v_part_interval; END IF; -- Stops new time partitons 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; -- Stop data going into child tables and stop new id partitions from being made. SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; 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, v_parent_schema, '_part_trig_func', FALSE); SELECT tgname INTO v_trig_name FROM pg_catalog.pg_trigger t WHERE tgname = v_trig_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 'LOCK TABLE ONLY '||p_parent_table||' IN ACCESS EXCLUSIVE MODE NOWAIT'; 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'; RETURN -1; END IF; END IF; -- END p_lock_wait IF EXECUTE 'DROP TRIGGER IF EXISTS '||v_trig_name||' ON '||p_parent_table; END IF; -- END trigger IF v_lock_obtained := FALSE; -- reset for reuse later EXECUTE 'DROP FUNCTION IF EXISTS '||v_function_name||'()'; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Stopped partition creation process. Removed trigger & trigger function'); END IF; <> WHILE v_batch_loop_count < p_batch_count LOOP SELECT n.nspname||'.'||c.relname INTO v_child_table FROM pg_inherits i JOIN pg_class c ON i.inhrelid = c.oid JOIN pg_namespace n ON c.relnamespace = n.oid WHERE i.inhparent::regclass = p_parent_table::regclass ORDER BY i.inhrelid ASC; EXIT WHEN v_child_table IS NULL; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing child partition: '||v_child_table); END IF; EXECUTE 'SELECT min('||v_control||') FROM '||v_child_table INTO v_child_min; IF v_child_min IS NULL THEN -- No rows left in this child table. Remove from partition set. -- lockwait timeout for table 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 'LOCK TABLE ONLY '||v_child_table||' IN ACCESS EXCLUSIVE MODE NOWAIT'; 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 child table for removal from partition set'; RETURN -1; END IF; END IF; -- END p_lock_wait IF v_lock_obtained := FALSE; -- reset for reuse later EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; IF p_keep_table = false THEN EXECUTE 'DROP TABLE '||v_child_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table DROPPED. Moved '||v_child_loop_total||' rows to parent'); END IF; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table UNINHERITED, not DROPPED. Moved '||v_child_loop_total||' rows to parent'); END IF; END IF; v_undo_count := v_undo_count + 1; CONTINUE outer_child_loop; END IF; v_inner_loop_count := 1; v_child_loop_total := 0; <> LOOP -- lockwait timeout for row batches 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 'SELECT * FROM ' || v_child_table || ' WHERE '||v_control||' <= '||quote_literal(v_child_min + (p_batch_interval * v_inner_loop_count)) ||' FOR UPDATE NOWAIT'; 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 batch of rows to move'; RETURN -1; END IF; END IF; -- Get everything from the current child minimum up to the multiples of the given interval v_move_sql := 'WITH move_data AS (DELETE FROM '||v_child_table|| ' WHERE '||v_control||' <= '||quote_literal(v_child_min + (p_batch_interval * v_inner_loop_count))||' RETURNING *) INSERT INTO '||p_parent_table||' SELECT * FROM move_data'; EXECUTE v_move_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total := v_total + v_rowcount; v_child_loop_total := v_child_loop_total + v_rowcount; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Moved '||v_child_loop_total||' rows to parent.'); END IF; EXIT inner_child_loop WHEN v_rowcount = 0; -- exit before loop incr if table is empty v_inner_loop_count := v_inner_loop_count + 1; v_batch_loop_count := v_batch_loop_count + 1; EXIT outer_child_loop WHEN v_batch_loop_count >= p_batch_count; -- Exit outer FOR loop if p_batch_count is reached END LOOP inner_child_loop; END LOOP outer_child_loop; IF v_batch_loop_count < p_batch_count THEN -- FOR loop never ran, so there's no child tables left. DELETE FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing config from pg_partman'); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; RAISE NOTICE 'Copied % row(s) to the parent. Removed % partitions.', v_total, v_undo_count; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Final stats'); PERFORM update_step(v_step_id, 'OK', 'Copied '||v_total||' row(s) to the parent. Removed '||v_undo_count||' partitions.'); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_total; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN UNDO PARTITIONING: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; -- Restore dropped object privileges DO $$ DECLARE v_row record; BEGIN FOR v_row IN SELECT statement FROM partman_preserve_privs_temp LOOP IF v_row.statement IS NOT NULL THEN EXECUTE v_row.statement; END IF; END LOOP; END $$; DROP TABLE IF EXISTS partman_preserve_privs_temp; pg_partman-2.2.2/updates/pg_partman--1.8.0--1.8.1.sql000066400000000000000000001063571262146621700214450ustar00rootroot00000000000000-- 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. /* * Apply constraints managed by partman extension */ CREATE OR REPLACE FUNCTION apply_constraints(p_parent_table text, p_child_table text DEFAULT NULL, p_analyze boolean DEFAULT FALSE, p_debug boolean DEFAULT FALSE) RETURNS void LANGUAGE plpgsql AS $$ DECLARE v_child_table text; v_child_tablename text; v_col text; v_constraint_cols text[]; v_constraint_col_type text; v_constraint_name text; v_datetime_string text; v_existing_constraint_name text; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_last_partition text; v_last_partition_id int; v_last_partition_timestamp timestamp; v_constraint_values record; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_part_interval text; v_partition_suffix text; v_premake int; v_sql text; v_step_id bigint; v_suffix_position int; v_type text; BEGIN SELECT type , part_interval , premake , datetime_string , constraint_cols , jobmon INTO v_type , v_part_interval , v_premake , v_datetime_string , v_constraint_cols , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_constraint_cols IS NULL THEN IF p_debug THEN RAISE NOTICE 'Given parent table (%) not set up for constraint management (constraint_cols is NULL)', p_parent_table; END IF; -- Returns silently to allow this function to be simply called by maintenance processes without having to check if config options are set. RETURN; END IF; SELECT show_partitions INTO v_last_partition FROM @extschema@.show_partitions(p_parent_table, 'DESC') LIMIT 1; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE CONSTRAINT: '||p_parent_table); END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; -- If p_child_table is null, figure out the partition that is the one right before the premake value backwards. IF p_child_table IS NULL THEN IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Automatically determining most recent child on which to apply constraints'); END IF; v_suffix_position := (length(v_last_partition) - position('p_' in reverse(v_last_partition))) + 2; IF v_type IN ('time-static', 'time-dynamic') THEN v_last_partition_timestamp := to_timestamp(substring(v_last_partition from v_suffix_position), v_datetime_string); v_partition_suffix := to_char(v_last_partition_timestamp - (v_part_interval::interval * ((v_premake * 2)+1) ), v_datetime_string); ELSIF v_type IN ('id-static', 'id-dynamic') THEN v_last_partition_id := substring(v_last_partition from v_suffix_position)::int; v_partition_suffix := (v_last_partition_id - (v_part_interval::int * ((v_premake * 2)+1) ))::text; END IF; v_child_table := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_partition_suffix, TRUE); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Target child table: '||v_child_table); END IF; ELSE v_child_table := p_child_table; END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Checking if target child table exists'); END IF; SELECT tablename INTO v_child_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_child_table; IF v_child_tablename IS NULL THEN IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'NOTICE', 'Target child table ('||v_child_table||') does not exist. Skipping constraint creation.'); PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; IF p_debug THEN RAISE NOTICE 'Target child table (%) does not exist. Skipping constraint creation.', v_child_table; END IF; RETURN; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; FOREACH v_col IN ARRAY v_constraint_cols LOOP SELECT c.conname INTO v_existing_constraint_name FROM pg_catalog.pg_constraint c JOIN pg_catalog.pg_attribute a ON c.conrelid = a.attrelid WHERE conrelid = v_child_table::regclass AND c.conname LIKE 'partmanconstr_%' AND c.contype = 'c' AND a.attname = v_col AND ARRAY[a.attnum] <@ c.conkey AND a.attisdropped = false; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Applying new constraint on column: '||v_col); END IF; IF v_existing_constraint_name IS NOT NULL THEN IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'NOTICE', 'Partman managed constraint already exists on this table ('||v_child_table||') and column ('||v_col||'). Skipping creation.'); END IF; RAISE WARNING 'Partman managed constraint already exists on this table (%) and column (%). Skipping creation.', v_child_table, v_col ; CONTINUE; END IF; -- 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 'SELECT min('||v_col||')::text AS min, max('||v_col||')::text AS max FROM '||v_child_table INTO v_constraint_values; IF v_constraint_values IS NOT NULL THEN v_sql := concat('ALTER TABLE ', v_child_table, ' ADD CONSTRAINT ', v_constraint_name , ' CHECK (', v_col, ' >= ', quote_literal(v_constraint_values.min), ' AND ' , v_col, ' <= ', quote_literal(v_constraint_values.max), ')' ); IF p_debug THEN RAISE NOTICE 'Constraint creation query: %', v_sql; END IF; EXECUTE v_sql; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'New constraint created: '||v_sql); END IF; ELSE IF p_debug THEN RAISE NOTICE 'Given column (%) contains all NULLs. No constraint created', v_col; END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'NOTICE', 'Given column ('||v_col||') contains all NULLs. No constraint created'); END IF; END IF; END LOOP; IF p_analyze THEN IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Running analyze on partition set: '||p_parent_table); END IF; IF p_debug THEN RAISE NOTICE 'Running analyze on partition set: %', p_parent_table; END IF; EXECUTE 'ANALYZE '||p_parent_table; 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 PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN CREATE CONSTRAINT: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to create id partitions */ CREATE OR REPLACE FUNCTION create_partition_id(p_parent_table text, p_partition_ids bigint[], p_analyze boolean DEFAULT true) RETURNS boolean LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_all text[] := ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER']; v_analyze boolean := FALSE; v_control text; v_grantees text[]; v_hasoids boolean; v_id bigint; v_id_position int; v_inherit_fk boolean; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_parent_grant record; v_parent_owner text; v_parent_schema text; v_parent_tablename text; v_parent_tablespace text; v_part_interval bigint; v_partition_created boolean := false; v_partition_name text; v_revoke text[]; v_row record; v_sql text; v_step_id bigint; v_sub_id_max bigint; v_sub_id_min bigint; v_tablename text; v_top_interval bigint; v_top_parent text; v_unlogged char; BEGIN SELECT control , part_interval , inherit_fk , jobmon INTO v_control , v_part_interval , v_inherit_fk , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; -- Check if parent table is a subpartition of an already existing id based partition set managed by pg_partman -- If so, limit what child tables can be created based on parent suffix WITH top_oid AS ( SELECT i.inhparent AS top_parent_oid FROM pg_catalog.pg_class c JOIN pg_catalog.pg_inherits i ON c.oid = i.inhrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname||'.'||c.relname = p_parent_table ) SELECT n.nspname||'.'||c.relname INTO v_top_parent FROM pg_catalog.pg_class c JOIN top_oid t ON c.oid = t.top_parent_oid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace JOIN @extschema@.part_config p ON p.parent_table = n.nspname||'.'||c.relname WHERE c.oid = t.top_parent_oid AND p.type = 'id-static' OR p.type = 'id-dynamic'; IF v_top_parent IS NOT NULL THEN SELECT part_interval::bigint INTO v_top_interval FROM @extschema@.part_config WHERE parent_table = v_top_parent; v_id_position := (length(p_parent_table) - position('p_' in reverse(p_parent_table))) + 2; v_sub_id_min = substring(p_parent_table from v_id_position)::bigint; v_sub_id_max = (v_sub_id_min + v_top_interval) - 1; END IF; SELECT tableowner, schemaname, tablename, tablespace INTO v_parent_owner, v_parent_schema, v_parent_tablename, v_parent_tablespace FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); 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. 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; END IF; END IF; v_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_id::text, TRUE); -- If child table already exists, skip creation SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_tablename IS NOT NULL 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, 'Creating new partition '||v_partition_name||' with interval from '||v_id||' to '||(v_id + v_part_interval)-1); END IF; SELECT relpersistence INTO v_unlogged FROM pg_catalog.pg_class WHERE oid::regclass = p_parent_table::regclass; v_sql := 'CREATE'; IF v_unlogged = 'u' THEN v_sql := v_sql || ' UNLOGGED'; END IF; v_sql := v_sql || ' TABLE '||v_partition_name||' (LIKE '||p_parent_table||' INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING STORAGE INCLUDING COMMENTS)'; SELECT relhasoids INTO v_hasoids FROM pg_catalog.pg_class WHERE oid::regclass = p_parent_table::regclass; IF v_hasoids IS TRUE THEN v_sql := v_sql || ' WITH (OIDS)'; END IF; EXECUTE v_sql; SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_parent_tablespace IS NOT NULL THEN EXECUTE 'ALTER TABLE '||v_partition_name||' SET TABLESPACE '||v_parent_tablespace; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' ADD CONSTRAINT '||v_tablename||'_partition_check CHECK ('||v_control||'>='||quote_literal(v_id)||' AND '||v_control||'<'||quote_literal(v_id + v_part_interval)||')'; EXECUTE 'ALTER TABLE '||v_partition_name||' INHERIT '||p_parent_table; FOR v_parent_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = p_parent_table GROUP BY grantee LOOP EXECUTE 'GRANT '||array_to_string(v_parent_grant.types, ',')||' ON '||v_partition_name||' TO '||v_parent_grant.grantee; SELECT array_agg(r) INTO v_revoke FROM (SELECT unnest(v_all) AS r EXCEPT SELECT unnest(v_parent_grant.types)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE '||array_to_string(v_revoke, ',')||' ON '||v_partition_name||' FROM '||v_parent_grant.grantee||' CASCADE'; END IF; v_grantees := array_append(v_grantees, v_parent_grant.grantee::text); END LOOP; -- Revoke all privileges from roles that have none on the parent IF v_grantees IS NOT NULL THEN SELECT array_agg(r) INTO v_revoke FROM ( SELECT DISTINCT grantee::text AS r FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = v_partition_name EXCEPT SELECT unnest(v_grantees)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE ALL ON '||v_partition_name||' FROM '||array_to_string(v_revoke, ','); END IF; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' OWNER TO '||v_parent_owner; IF v_inherit_fk THEN PERFORM @extschema@.apply_foreign_keys(quote_ident(v_parent_schema)||'.'||quote_ident(v_parent_tablename), v_partition_name); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); 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 FOR v_row IN SELECT sub_parent , sub_control , sub_type , sub_part_interval , sub_constraint_cols , sub_premake , sub_inherit_fk , sub_retention , sub_retention_schema , sub_retention_keep_table , sub_retention_keep_index , sub_use_run_maintenance , sub_jobmon FROM @extschema@.part_config_sub WHERE sub_parent = p_parent_table LOOP IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Subpartitioning '||v_partition_name); END IF; v_sql := format('SELECT @extschema@.create_parent( p_parent_table := %L , p_control := %L , p_type := %L , p_interval := %L , p_constraint_cols := %L , p_premake := %L , p_use_run_maintenance := %L , p_inherit_fk := %L , p_jobmon := %L )' , v_partition_name , v_row.sub_control , v_row.sub_type , v_row.sub_part_interval , v_row.sub_constraint_cols , v_row.sub_premake , v_row.sub_inherit_fk , v_row.sub_use_run_maintenance , v_row.sub_jobmon); EXECUTE v_sql; 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 WHERE parent_table = v_partition_name; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END LOOP; -- end sub partitioning LOOP 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, 'Analyzing partition set: '||p_parent_table); END IF; EXECUTE 'ANALYZE '||p_parent_table; 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 v_step_id := add_step(v_job_id, 'No partitions created for partition set: '||p_parent_table); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; PERFORM close_job(v_job_id); END IF; IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_partition_created; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN CREATE TABLE: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to create a child table in a time-based partition set */ CREATE OR REPLACE FUNCTION create_partition_time (p_parent_table text, p_partition_times timestamp[], p_analyze boolean DEFAULT true) RETURNS boolean LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_all text[] := ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER']; v_analyze boolean := FALSE; v_control text; v_datetime_string text; v_grantees text[]; v_hasoids boolean; v_inherit_fk boolean; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_parent_grant record; v_parent_owner text; v_parent_schema text; v_parent_tablename text; v_partition_created boolean := false; v_partition_name text; v_partition_suffix text; v_parent_tablespace text; v_part_interval interval; v_partition_timestamp_end timestamp; v_partition_timestamp_start timestamp; v_quarter text; v_revoke text[]; v_row record; v_sql text; v_step_id bigint; v_step_overflow_id bigint; v_sub_timestamp_max timestamp; v_sub_timestamp_min timestamp; v_tablename text; v_time_position int; v_top_interval interval; v_top_parent text; v_trunc_value text; v_time timestamp; v_type text; v_unlogged char; v_year text; BEGIN SELECT type , control , part_interval , inherit_fk , jobmon , datetime_string INTO v_type , v_control , v_part_interval , v_inherit_fk , v_jobmon , v_datetime_string FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic' OR type = 'time-custom'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; -- Check if parent table is a subpartition of an already existing time-based partition set managed by pg_partman -- If so, limit what child tables can be created based on parent suffix WITH top_oid AS ( SELECT i.inhparent AS top_parent_oid FROM pg_catalog.pg_class c JOIN pg_catalog.pg_inherits i ON c.oid = i.inhrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname||'.'||c.relname = p_parent_table ) SELECT n.nspname||'.'||c.relname INTO v_top_parent FROM pg_catalog.pg_class c JOIN top_oid t ON c.oid = t.top_parent_oid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace JOIN @extschema@.part_config p ON p.parent_table = n.nspname||'.'||c.relname WHERE c.oid = t.top_parent_oid AND p.type = 'time-static' OR p.type = 'time-dynamic'; IF v_top_parent IS NOT NULL THEN SELECT part_interval::interval INTO v_top_interval FROM @extschema@.part_config WHERE parent_table = v_top_parent; v_time_position := (length(p_parent_table) - position('p_' in reverse(p_parent_table))) + 2; IF v_part_interval::interval <> '3 months' OR (v_part_interval::interval = '3 months' AND v_type = 'time-custom') THEN v_sub_timestamp_min := to_timestamp(substring(p_parent_table from v_time_position), v_datetime_string); ELSE -- to_timestamp doesn't recognize 'Q' date string formater. Handle it v_year := split_part(substring(p_parent_table from v_time_position), 'q', 1); v_quarter := split_part(substring(p_parent_table from v_time_position), 'q', 2); CASE WHEN v_quarter = '1' THEN v_sub_timestamp_min := to_timestamp(v_year || '-01-01', 'YYYY-MM-DD'); WHEN v_quarter = '2' THEN v_sub_timestamp_min := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); WHEN v_quarter = '3' THEN v_sub_timestamp_min := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); WHEN v_quarter = '4' THEN v_sub_timestamp_min := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); END CASE; END IF; v_sub_timestamp_max = (v_sub_timestamp_min + v_top_interval::interval) - '1 sec'::interval; END IF; SELECT tableowner, schemaname, tablename, tablespace INTO v_parent_owner, v_parent_schema, v_parent_tablename, v_parent_tablespace FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); END IF; FOREACH v_time IN ARRAY p_partition_times LOOP v_partition_timestamp_start := v_time; BEGIN v_partition_timestamp_end := v_time + v_part_interval; EXCEPTION WHEN datetime_field_overflow THEN 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'); CONTINUE; END; -- This suffix generation code is in partition_data_time() as well v_partition_suffix := to_char(v_time, 'YYYY'); IF v_part_interval < '1 year' AND v_part_interval <> '1 week' THEN v_partition_suffix := v_partition_suffix ||'_'|| to_char(v_time, 'MM'); IF v_part_interval < '1 month' AND v_part_interval <> '1 week' THEN v_partition_suffix := v_partition_suffix ||'_'|| to_char(v_time, 'DD'); IF v_part_interval < '1 day' THEN v_partition_suffix := v_partition_suffix || '_' || to_char(v_time, 'HH24MI'); IF v_part_interval < '1 minute' THEN v_partition_suffix := v_partition_suffix || to_char(v_time, 'SS'); END IF; -- end < minute IF END IF; -- end < day IF END IF; -- end < month IF END IF; -- end < year IF IF v_part_interval = '1 week' THEN v_partition_suffix := to_char(v_time, 'IYYY') || 'w' || to_char(v_time, 'IW'); END IF; -- "Q" is ignored in to_timestamp, so handle special case IF v_part_interval = '3 months' AND (v_type = 'time-static' OR v_type = 'time-dynamic') THEN v_year := to_char(v_time, 'YYYY'); v_quarter := to_char(v_time, 'Q'); v_partition_suffix := v_year || 'q' || v_quarter; END IF; -- 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 CONTINUE; END IF; END IF; v_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_partition_suffix, TRUE); SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_tablename IS NOT NULL 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, 'Creating new partition '||v_partition_name||' with interval from '||v_partition_timestamp_start||' to '||(v_partition_timestamp_end-'1sec'::interval)); END IF; SELECT relpersistence INTO v_unlogged FROM pg_catalog.pg_class WHERE oid::regclass = p_parent_table::regclass; v_sql := 'CREATE'; IF v_unlogged = 'u' THEN v_sql := v_sql || ' UNLOGGED'; END IF; v_sql := v_sql || ' TABLE '||v_partition_name||' (LIKE '||p_parent_table||' INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING STORAGE INCLUDING COMMENTS)'; SELECT relhasoids INTO v_hasoids FROM pg_catalog.pg_class WHERE oid::regclass = p_parent_table::regclass; IF v_hasoids IS TRUE THEN v_sql := v_sql || ' WITH (OIDS)'; END IF; EXECUTE v_sql; SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_parent_tablespace IS NOT NULL THEN EXECUTE 'ALTER TABLE '||v_partition_name||' SET TABLESPACE '||v_parent_tablespace; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' ADD CONSTRAINT '||v_tablename||'_partition_check CHECK ('||v_control||'>='||quote_literal(v_partition_timestamp_start)||' AND '||v_control||'<'||quote_literal(v_partition_timestamp_end)||')'; EXECUTE 'ALTER TABLE '||v_partition_name||' INHERIT '||p_parent_table; -- If custom time, set extra config options. IF v_type = 'time-custom' THEN INSERT INTO @extschema@.custom_time_partitions (parent_table, child_table, partition_range) VALUES ( p_parent_table, v_partition_name, tstzrange(v_partition_timestamp_start, v_partition_timestamp_end, '[)') ); END IF; FOR v_parent_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = p_parent_table GROUP BY grantee LOOP EXECUTE 'GRANT '||array_to_string(v_parent_grant.types, ',')||' ON '||v_partition_name||' TO '||v_parent_grant.grantee; SELECT array_agg(r) INTO v_revoke FROM (SELECT unnest(v_all) AS r EXCEPT SELECT unnest(v_parent_grant.types)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE '||array_to_string(v_revoke, ',')||' ON '||v_partition_name||' FROM '||v_parent_grant.grantee||' CASCADE'; END IF; v_grantees := array_append(v_grantees, v_parent_grant.grantee::text); END LOOP; -- Revoke all privileges from roles that have none on the parent IF v_grantees IS NOT NULL THEN SELECT array_agg(r) INTO v_revoke FROM ( SELECT DISTINCT grantee::text AS r FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = v_partition_name EXCEPT SELECT unnest(v_grantees)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE ALL ON '||v_partition_name||' FROM '||array_to_string(v_revoke, ','); END IF; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' OWNER TO '||v_parent_owner; IF v_inherit_fk THEN PERFORM @extschema@.apply_foreign_keys(quote_ident(v_parent_schema)||'.'||quote_ident(v_parent_tablename), v_partition_name); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); 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 FOR v_row IN SELECT sub_parent , sub_control , sub_type , sub_part_interval , sub_constraint_cols , sub_premake , sub_inherit_fk , sub_retention , sub_retention_schema , sub_retention_keep_table , sub_retention_keep_index , sub_use_run_maintenance , sub_jobmon FROM @extschema@.part_config_sub WHERE sub_parent = p_parent_table LOOP IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Subpartitioning '||v_partition_name); END IF; v_sql := format('SELECT @extschema@.create_parent( p_parent_table := %L , p_control := %L , p_type := %L , p_interval := %L , p_constraint_cols := %L , p_premake := %L , p_use_run_maintenance := %L , p_inherit_fk := %L , p_jobmon := %L )' , v_partition_name , v_row.sub_control , v_row.sub_type , v_row.sub_part_interval , v_row.sub_constraint_cols , v_row.sub_premake , v_row.sub_inherit_fk , v_row.sub_use_run_maintenance , v_row.sub_jobmon); EXECUTE v_sql; 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 WHERE parent_table = v_partition_name; END LOOP; -- end sub partitioning LOOP 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, 'Analyzing partition set: '||p_parent_table); END IF; EXECUTE 'ANALYZE '||p_parent_table; 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 v_step_id := add_step(v_job_id, 'No partitions created for partition set: '||p_parent_table); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; IF v_step_overflow_id IS NOT NULL THEN PERFORM fail_job(v_job_id); ELSE PERFORM close_job(v_job_id); END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_partition_created; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN CREATE TABLE: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; pg_partman-2.2.2/updates/pg_partman--1.8.1--1.8.2.sql000066400000000000000000002427261262146621700214500ustar00rootroot00000000000000-- 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. ALTER TABLE @extschema@.part_config_sub ADD CONSTRAINT part_config_sub_type_check CHECK (@extschema@.check_partition_type(sub_type)); /* * Function to turn a table into the parent of a partition set */ CREATE OR REPLACE FUNCTION create_parent( p_parent_table text , p_control text , p_type text , p_interval text , p_constraint_cols text[] DEFAULT NULL , p_premake int DEFAULT 4 , p_use_run_maintenance boolean DEFAULT NULL , p_start_partition text DEFAULT NULL , p_inherit_fk boolean DEFAULT true , p_jobmon boolean DEFAULT true , p_debug boolean DEFAULT false) RETURNS boolean LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_base_timestamp timestamp; v_count int := 1; v_datetime_string text; v_higher_parent text := p_parent_table; v_id_interval bigint; v_id_position int; v_job_id bigint; v_jobmon_schema text; v_last_partition_created boolean; v_max bigint; v_notnull boolean; v_old_search_path text; v_parent_partition_id bigint; v_parent_partition_timestamp timestamp; v_partition_time timestamp; v_partition_time_array timestamp[]; v_partition_id_array bigint[]; v_row record; v_run_maint boolean; v_sql text; v_start_time timestamp; v_starting_partition_id bigint; v_step_id bigint; v_step_overflow_id bigint; v_sub_parent text; v_success boolean := false; v_tablename text; v_time_interval interval; v_time_position int; v_top_datetime_string text; v_top_parent text := p_parent_table; BEGIN IF position('.' in p_parent_table) = 0 THEN RAISE EXCEPTION 'Parent table must be schema qualified'; END IF; SELECT tablename INTO v_tablename FROM pg_tables WHERE schemaname || '.' || tablename = p_parent_table; IF v_tablename IS NULL THEN RAISE EXCEPTION 'Please create given parent table first: %', p_parent_table; END IF; SELECT attnotnull INTO v_notnull FROM pg_attribute WHERE attrelid = p_parent_table::regclass AND attname = p_control; IF v_notnull = false OR v_notnull IS NULL THEN RAISE EXCEPTION 'Control column (%) for parent table (%) must be NOT NULL', p_control, p_parent_table; END IF; IF NOT @extschema@.check_partition_type(p_type) THEN RAISE EXCEPTION '% is not a valid partitioning type', p_type; END IF; IF p_type = 'time-custom' AND @extschema@.check_version('9.2.0') IS FALSE THEN RAISE EXCEPTION 'The "time-custom" type requires a minimum PostgreSQL version of 9.2.0'; END IF; EXECUTE 'LOCK TABLE '||p_parent_table||' IN ACCESS EXCLUSIVE MODE'; IF p_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF p_use_run_maintenance IS NOT NULL THEN IF p_use_run_maintenance IS FALSE AND (p_type = 'time-static' OR p_type = 'time-dynamic' OR p_type = 'time-custom') THEN RAISE EXCEPTION 'p_run_maintenance cannot be set to false for time based partitioning'; END IF; v_run_maint := p_use_run_maintenance; ELSIF p_type = 'time-static' OR p_type = 'time-dynamic' OR p_type = 'time-custom' THEN v_run_maint := TRUE; ELSIF p_type = 'id-static' OR p_type ='id-dynamic' THEN v_run_maint := FALSE; ELSE RAISE EXCEPTION 'use_run_maintenance value cannot be set NULL'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN SETUP PARENT: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating initial partitions on new parent table: '||p_parent_table); END IF; -- If this parent table has siblings that are also partitioned (subpartitions), ensure it 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 WITH parent_table AS ( SELECT h.inhparent as parent_oid from pg_inherits h where h.inhrelid::regclass = p_parent_table::regclass ), sibling_children as ( select i.inhrelid::regclass::text as tablename from pg_inherits i join parent_table p on i.inhparent = p.parent_oid ) SELECT DISTINCT sub_type , sub_control , sub_part_interval , sub_constraint_cols , sub_premake , sub_inherit_fk , sub_retention , sub_retention_schema , sub_retention_keep_table , sub_retention_keep_index , sub_use_run_maintenance , sub_jobmon FROM @extschema@.part_config_sub a JOIN sibling_children b on a.sub_parent = b.tablename LIMIT 1 LOOP INSERT INTO @extschema@.part_config_sub ( sub_parent , sub_type , sub_control , sub_part_interval , sub_constraint_cols , sub_premake , sub_inherit_fk , sub_retention , sub_retention_schema , sub_retention_keep_table , sub_retention_keep_index , sub_use_run_maintenance , sub_jobmon) VALUES ( p_parent_table , v_row.sub_type , v_row.sub_control , v_row.sub_part_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_use_run_maintenance , v_row.sub_jobmon); END LOOP; IF p_type = 'time-static' OR p_type = 'time-dynamic' OR p_type = 'time-custom' 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 <> 'time-custom' THEN RAISE EXCEPTION 'Must use a predefined time interval if not using type "time-custom". See documentation.'; 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; -- First partition is either the min premake or p_start_partition v_start_time := COALESCE(p_start_partition::timestamp, CURRENT_TIMESTAMP - (v_time_interval * p_premake)); 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 v_datetime_string := 'YYYY'; 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 v_partition_time_array := array_append(v_partition_time_array, v_base_timestamp); LOOP -- If current loop value is less than or equal to the value of the max premake, add time to array. IF (v_base_timestamp + (v_time_interval * v_count)) < (CURRENT_TIMESTAMP + (v_time_interval * p_premake)) THEN BEGIN v_partition_time := (v_base_timestamp + (v_time_interval * v_count))::timestamp; 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. 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'); CONTINUE; END; ELSE EXIT; -- all needed partitions added to array. Exit the loop. END IF; v_count := v_count + 1; END LOOP; INSERT INTO @extschema@.part_config ( parent_table , type , part_interval , control , premake , constraint_cols , datetime_string , use_run_maintenance , inherit_fk , jobmon) VALUES ( p_parent_table , p_type , v_time_interval , p_control , p_premake , p_constraint_cols , v_datetime_string , v_run_maint , p_inherit_fk , p_jobmon); v_last_partition_created := @extschema@.create_partition_time(p_parent_table, v_partition_time_array, false); 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 ( SELECT i.inhparent AS top_parent_oid FROM pg_catalog.pg_inherits i JOIN pg_catalog.pg_class c ON c.oid = i.inhrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname||'.'||c.relname = p_parent_table ) SELECT n.nspname||'.'||c.relname, p.datetime_string INTO v_top_parent, v_top_datetime_string 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 JOIN @extschema@.part_config p ON p.parent_table = n.nspname||'.'||c.relname; IF v_top_parent IS NOT NULL THEN -- If so create the lowest possible partition that is within the boundary of the parent v_time_position := (length(p_parent_table) - position('p_' in reverse(p_parent_table))) + 2; v_parent_partition_timestamp := to_timestamp(substring(p_parent_table from v_time_position), v_top_datetime_string); IF v_base_timestamp >= v_parent_partition_timestamp THEN WHILE v_base_timestamp >= v_parent_partition_timestamp LOOP v_base_timestamp := v_base_timestamp - v_time_interval; END LOOP; v_base_timestamp := v_base_timestamp + v_time_interval; -- add one back since while loop set it one lower than is needed ELSIF v_base_timestamp < v_parent_partition_timestamp THEN WHILE v_base_timestamp < v_parent_partition_timestamp LOOP v_base_timestamp := v_base_timestamp + v_time_interval; END LOOP; -- Don't need to remove one since new starting time will fit in top parent interval 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); ELSE -- Currently unknown edge case if code gets here RAISE EXCEPTION 'No child tables created. Unexpected edge case encountered. Please report this error to author with conditions that led to it.'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Time partitions premade: '||p_premake); END IF; END IF; IF p_type = 'id-static' OR p_type = 'id-dynamic' THEN v_id_interval := p_interval::bigint; IF v_id_interval < 10 THEN RAISE EXCEPTION 'Interval for serial partitioning must be greater than or equal to 10'; END IF; -- Check if parent table is a subpartition of an already existing id partition set managed by pg_partman. WHILE v_higher_parent IS NOT NULL LOOP -- initially set in DECLARE WITH top_oid AS ( SELECT i.inhparent AS top_parent_oid FROM pg_catalog.pg_inherits i JOIN pg_catalog.pg_class c ON c.oid = i.inhrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname||'.'||c.relname = v_higher_parent ) SELECT n.nspname||'.'||c.relname INTO v_higher_parent 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 JOIN @extschema@.part_config p ON p.parent_table = n.nspname||'.'||c.relname WHERE p.type = 'id-static' OR p.type = 'id-dynamic'; IF v_higher_parent IS NOT NULL THEN -- v_top_parent initially set in DECLARE v_top_parent := v_higher_parent; END IF; END LOOP; -- If custom start partition is set, use that. -- If custom start is not set and there is already data, start partitioning with the highest current value and ensure it's grabbed from highest top parent table v_sql := 'SELECT COALESCE('||quote_nullable(p_start_partition::bigint)||', max('||p_control||')::bigint, 0) FROM '||v_top_parent||' LIMIT 1'; EXECUTE v_sql INTO v_max; 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 THEN v_partition_id_array = array_append(v_partition_id_array, (v_starting_partition_id - v_id_interval*i)); END IF; v_partition_id_array = array_append(v_partition_id_array, (v_id_interval*i) + v_starting_partition_id); END LOOP; INSERT INTO @extschema@.part_config ( parent_table , type , part_interval , control , premake , constraint_cols , use_run_maintenance , inherit_fk , jobmon) VALUES ( p_parent_table , p_type , v_id_interval , p_control , p_premake , p_constraint_cols , v_run_maint , p_inherit_fk , p_jobmon); v_last_partition_created := @extschema@.create_partition_id(p_parent_table, v_partition_id_array, false); 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 -- See if it's actually a subpartition of a parent id partition WITH top_oid AS ( SELECT i.inhparent AS top_parent_oid FROM pg_catalog.pg_inherits i JOIN pg_catalog.pg_class c ON c.oid = i.inhrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname||'.'||c.relname = p_parent_table ) SELECT n.nspname||'.'||c.relname INTO v_top_parent 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 JOIN @extschema@.part_config p ON p.parent_table = n.nspname||'.'||c.relname WHERE p.type = 'id-static' OR p.type = 'id-dynamic'; IF v_top_parent IS NOT NULL THEN -- Create the lowest possible partition that is within the boundary of the parent v_id_position := (length(p_parent_table) - position('p_' in reverse(p_parent_table))) + 2; v_parent_partition_id = substring(p_parent_table from v_id_position)::bigint; IF v_starting_partition_id >= v_parent_partition_id THEN WHILE v_starting_partition_id >= v_parent_partition_id LOOP v_starting_partition_id := v_starting_partition_id - v_id_interval; END LOOP; v_starting_partition_id := v_starting_partition_id + v_id_interval; -- add one back since while loop set it one lower than is needed ELSIF v_starting_partition_id < v_parent_partition_id THEN WHILE v_starting_partition_id < v_parent_partition_id LOOP v_starting_partition_id := v_starting_partition_id + v_id_interval; END LOOP; -- Don't need to remove one since new starting id will fit in top parent interval 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); ELSE -- Currently unknown edge case if code gets here RAISE EXCEPTION 'No child tables created. Unexpected edge case encountered. Please report this error to author with conditions that led to it.'; END IF; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Creating partition function'); END IF; IF p_type = 'time-static' OR p_type = 'time-dynamic' OR p_type = 'time-custom' THEN PERFORM @extschema@.create_function_time(p_parent_table); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Time function created'); END IF; ELSIF p_type = 'id-static' OR p_type = 'id-dynamic' THEN PERFORM @extschema@.create_function_id(p_parent_table); 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); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); IF v_step_overflow_id IS NOT NULL THEN PERFORM fail_job(v_job_id); ELSE PERFORM close_job(v_job_id); END IF; EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; v_success := true; RETURN v_success; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN CREATE PARENT: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''Partition creation for table '||p_parent_table||' failed'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Create the trigger function for the parent table of an id-based partition set */ CREATE OR REPLACE FUNCTION create_function_id(p_parent_table text) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_control text; v_count int; v_current_partition_name text; v_current_partition_id bigint; v_datetime_string text; v_final_partition_id bigint; v_function_name text; v_higher_parent text := p_parent_table; v_id_position int; v_job_id bigint; v_jobmon text; v_jobmon_schema text; v_last_partition text; v_max bigint; v_next_partition_id bigint; v_next_partition_name text; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_part_interval bigint; v_premake int; v_prev_partition_id bigint; v_prev_partition_name text; v_row_max_id record; v_run_maint boolean; v_step_id bigint; v_top_parent text := p_parent_table; v_trig_func text; v_type text; BEGIN SELECT type , part_interval::bigint , control , premake , use_run_maintenance , jobmon INTO v_type , v_part_interval , v_control , v_premake , v_run_maint , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; SELECT show_partitions INTO v_last_partition FROM @extschema@.show_partitions(p_parent_table, 'DESC') LIMIT 1; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE FUNCTION: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating partition function for table '||p_parent_table); END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; v_function_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, '_part_trig_func', FALSE); IF v_type = 'id-static' THEN -- Get the highest level top parent if multi-level partitioned in order to get proper max() value below WHILE v_higher_parent IS NOT NULL LOOP -- initially set in DECLARE WITH top_oid AS ( SELECT i.inhparent AS top_parent_oid FROM pg_catalog.pg_inherits i JOIN pg_catalog.pg_class c ON c.oid = i.inhrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname||'.'||c.relname = v_higher_parent ) SELECT n.nspname||'.'||c.relname INTO v_higher_parent 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 JOIN @extschema@.part_config p ON p.parent_table = n.nspname||'.'||c.relname WHERE p.type = 'id-static' OR p.type = 'id-dynamic'; IF v_higher_parent IS NOT NULL THEN -- initially set in DECLARE v_top_parent := v_higher_parent; END IF; END 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 parent. FOR v_row_max_id IN SELECT show_partitions FROM @extschema@.show_partitions(v_top_parent, 'DESC') LOOP EXECUTE 'SELECT max('||v_control||') FROM '||v_row_max_id.show_partitions INTO v_max; IF v_max IS NOT NULL THEN EXIT; END IF; END LOOP; IF v_max IS NULL THEN v_max := 0; END IF; v_current_partition_id = v_max - (v_max % v_part_interval); v_next_partition_id := v_current_partition_id + v_part_interval; v_current_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_current_partition_id::text, TRUE); v_trig_func := 'CREATE OR REPLACE FUNCTION '||v_function_name||'() RETURNS trigger LANGUAGE plpgsql AS $t$ DECLARE v_current_partition_id bigint; v_last_partition text := '||quote_literal(v_last_partition)||'; v_id_position int; v_next_partition_id bigint; v_next_partition_name text; v_partition_created boolean; BEGIN IF TG_OP = ''INSERT'' THEN IF NEW.'||v_control||' >= '||v_current_partition_id||' AND NEW.'||v_control||' < '||v_next_partition_id|| ' THEN '; SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname ||'.'||tablename = v_current_partition_name; IF v_count > 0 THEN v_trig_func := v_trig_func || ' INSERT INTO '||v_current_partition_name||' VALUES (NEW.*); '; ELSE v_trig_func := v_trig_func || ' -- Child table for current values does not exist in this partition set, so write to parent RETURN NEW;'; END IF; FOR i IN 1..v_premake LOOP v_prev_partition_id := v_current_partition_id - (v_part_interval * i); v_next_partition_id := v_current_partition_id + (v_part_interval * i); v_final_partition_id := v_next_partition_id + v_part_interval; v_prev_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_prev_partition_id::text, TRUE); v_next_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_next_partition_id::text, TRUE); -- Check that child table exist before making a rule to insert to them. -- Handles edge case of changing premake immediately after running create_parent(). SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname ||'.'||tablename = v_prev_partition_name; IF v_count > 0 THEN -- Only handle previous partitions if they're starting above zero IF v_prev_partition_id >= 0 THEN v_trig_func := v_trig_func ||' ELSIF NEW.'||v_control||' >= '||v_prev_partition_id||' AND NEW.'||v_control||' < '||v_prev_partition_id + v_part_interval|| ' THEN INSERT INTO '||v_prev_partition_name||' VALUES (NEW.*); '; END IF; END IF; SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname ||'.'||tablename = v_next_partition_name; IF v_count > 0 THEN v_trig_func := v_trig_func ||' ELSIF NEW.'||v_control||' >= '||v_next_partition_id||' AND NEW.'||v_control||' < '||v_final_partition_id|| ' THEN INSERT INTO '||v_next_partition_name||' VALUES (NEW.*);'; END IF; END LOOP; v_trig_func := v_trig_func ||' ELSE RETURN NEW; END IF;'; IF v_run_maint IS FALSE THEN v_trig_func := v_trig_func ||' v_current_partition_id := NEW.'||v_control||' - (NEW.'||v_control||' % '||v_part_interval||'); IF (NEW.'||v_control||' % '||v_part_interval||') > ('||v_part_interval||' / 2) THEN v_id_position := (length(v_last_partition) - position(''p_'' in reverse(v_last_partition))) + 2; v_next_partition_id := (substring(v_last_partition from v_id_position)::bigint) + '||v_part_interval||'; WHILE ((v_next_partition_id - v_current_partition_id) / '||v_part_interval||') <= '||v_premake||' LOOP v_partition_created := @extschema@.create_partition_id('||quote_literal(p_parent_table)||', ARRAY[v_next_partition_id]); IF v_partition_created THEN PERFORM @extschema@.create_function_id('||quote_literal(p_parent_table)||'); PERFORM @extschema@.apply_constraints('||quote_literal(p_parent_table)||'); END IF; v_next_partition_id := v_next_partition_id + '||v_part_interval||'; END LOOP; END IF;'; END IF; v_trig_func := v_trig_func ||' END IF; RETURN NULL; END $t$;'; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Added function for current id interval: '||v_current_partition_id||' to '||v_final_partition_id-1); END IF; ELSIF v_type = 'id-dynamic' THEN -- The return inside the partition creation check is there to keep really high ID values from creating new partitions. v_trig_func := 'CREATE OR REPLACE FUNCTION '||v_function_name||'() RETURNS trigger LANGUAGE plpgsql AS $t$ DECLARE v_count int; v_current_partition_id bigint; v_current_partition_name text; v_id_position int; v_last_partition text := '||quote_literal(v_last_partition)||'; v_last_partition_id bigint; v_next_partition_id bigint; v_partition_created boolean; BEGIN IF TG_OP = ''INSERT'' THEN v_current_partition_id := NEW.'||v_control||' - (NEW.'||v_control||' % '||v_part_interval||'); v_current_partition_name := @extschema@.check_name_length('''||v_parent_tablename||''', '''||v_parent_schema||''', v_current_partition_id::text, TRUE); SELECT count(*) INTO v_count FROM pg_tables WHERE schemaname ||''.''|| tablename = v_current_partition_name; IF v_count > 0 THEN EXECUTE ''INSERT INTO ''||v_current_partition_name||'' VALUES($1.*)'' USING NEW; ELSE RETURN NEW; END IF;'; IF v_run_maint IS FALSE THEN v_trig_func := v_trig_func ||' IF (NEW.'||v_control||' % '||v_part_interval||') > ('||v_part_interval||' / 2) THEN v_id_position := (length(v_last_partition) - position(''p_'' in reverse(v_last_partition))) + 2; v_last_partition_id = substring(v_last_partition from v_id_position)::bigint; v_next_partition_id := v_last_partition_id + '||v_part_interval||'; IF NEW.'||v_control||' >= v_next_partition_id THEN RETURN NEW; END IF; WHILE ((v_next_partition_id - v_current_partition_id) / '||v_part_interval||') <= '||v_premake||' LOOP v_partition_created := @extschema@.create_partition_id('||quote_literal(p_parent_table)||', ARRAY[v_next_partition_id]); IF v_partition_created THEN PERFORM @extschema@.create_function_id('||quote_literal(p_parent_table)||'); PERFORM @extschema@.apply_constraints('||quote_literal(p_parent_table)||'); END IF; v_next_partition_id := v_next_partition_id + '||v_part_interval||'; END LOOP; END IF;'; END IF; v_trig_func := v_trig_func ||' END IF; RETURN NULL; END $t$;'; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Added function for dynamic id table: '||p_parent_table); END IF; ELSE RAISE EXCEPTION 'ERROR: Invalid id partitioning type given: %', v_type; END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN CREATE FUNCTION: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''Partition function maintenance for table '||p_parent_table||' failed'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to manage pre-creation of the next partitions in a set. * 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_run_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. * Be aware that constraint exclusion may not work properly until an analyze on the partition set is run. */ CREATE OR REPLACE FUNCTION run_maintenance(p_parent_table text DEFAULT NULL, p_analyze boolean DEFAULT true, p_jobmon boolean DEFAULT true) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_create_count int := 0; v_current_partition text; v_current_partition_id bigint; v_current_partition_timestamp timestamp; v_datetime_string text; v_drop_count int := 0; v_id_position int; 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 timestamp; v_next_partition_id bigint; v_next_partition_timestamp timestamp; v_old_search_path text; v_premade_count int; v_quarter text; v_step_id bigint; v_step_overflow_id bigint; v_step_serial_id bigint; v_sub_parent text; v_row record; v_row_max_id record; v_row_sub record; v_tablename text; v_tables_list_sql text; v_time_position int; v_year text; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman run_maintenance')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'Partman maintenance already running.'; RETURN; END IF; IF p_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN RUN MAINTENANCE'); v_step_id := add_step(v_job_id, 'Running maintenance loop'); END IF; v_tables_list_sql := 'SELECT parent_table , type , part_interval , control , premake , datetime_string , undo_in_progress FROM @extschema@.part_config'; IF p_parent_table IS NULL THEN v_tables_list_sql := v_tables_list_sql || ' WHERE use_run_maintenance = true'; ELSE v_tables_list_sql := v_tables_list_sql || format(' WHERE parent_table = %L', p_parent_table); END IF; FOR v_row IN EXECUTE v_tables_list_sql LOOP CONTINUE WHEN v_row.undo_in_progress; SELECT show_partitions INTO v_last_partition FROM @extschema@.show_partitions(v_row.parent_table, 'DESC') LIMIT 1; IF v_row.type = 'time-static' OR v_row.type = 'time-dynamic' OR v_row.type = 'time-custom' THEN IF v_row.type = 'time-static' OR v_row.type = 'time-dynamic' THEN CASE WHEN v_row.part_interval::interval = '15 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0); WHEN v_row.part_interval::interval = '30 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0); WHEN v_row.part_interval::interval = '1 hour' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP); WHEN v_row.part_interval::interval = '1 day' THEN v_current_partition_timestamp := date_trunc('day', CURRENT_TIMESTAMP); WHEN v_row.part_interval::interval = '1 week' THEN v_current_partition_timestamp := date_trunc('week', CURRENT_TIMESTAMP); WHEN v_row.part_interval::interval = '1 month' THEN v_current_partition_timestamp := date_trunc('month', CURRENT_TIMESTAMP); WHEN v_row.part_interval::interval = '3 months' THEN v_current_partition_timestamp := date_trunc('quarter', CURRENT_TIMESTAMP); WHEN v_row.part_interval::interval = '1 year' THEN v_current_partition_timestamp := date_trunc('year', CURRENT_TIMESTAMP); END CASE; ELSIF v_row.type = 'time-custom' THEN SELECT child_table INTO v_current_partition FROM @extschema@.custom_time_partitions WHERE parent_table = v_row.parent_table AND partition_range @> CURRENT_TIMESTAMP; IF v_current_partition IS NULL THEN RAISE EXCEPTION 'Current time partition missing from custom_time_partitions config table for table % and timestamp %', CURRENT_TIMESTAMP, v_row.parent_table; END IF; v_time_position := (length(v_current_partition) - position('p_' in reverse(v_current_partition))) + 2; v_current_partition_timestamp := to_timestamp(substring(v_current_partition from v_time_position), v_row.datetime_string); END IF; v_time_position := (length(v_last_partition) - position('p_' in reverse(v_last_partition))) + 2; IF v_row.part_interval::interval <> '3 months' OR (v_row.part_interval::interval = '3 months' AND v_row.type = 'time-custom') THEN v_last_partition_timestamp := to_timestamp(substring(v_last_partition from v_time_position), v_row.datetime_string); ELSE -- to_timestamp doesn't recognize 'Q' date string formater. Handle it v_year := split_part(substring(v_last_partition from v_time_position), 'q', 1); v_quarter := split_part(substring(v_last_partition from v_time_position), 'q', 2); CASE WHEN v_quarter = '1' THEN v_last_partition_timestamp := to_timestamp(v_year || '-01-01', 'YYYY-MM-DD'); WHEN v_quarter = '2' THEN v_last_partition_timestamp := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); WHEN v_quarter = '3' THEN v_last_partition_timestamp := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); WHEN v_quarter = '4' THEN v_last_partition_timestamp := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); END CASE; END IF; -- Check and see how many premade partitions there are. -- Can be negative when subpartitioning and there are parent partitions in the past compared to current timestamp value. -- abs() prevents run_maintenence from running on those old parent tables v_premade_count = abs(round(EXTRACT('epoch' FROM age(v_last_partition_timestamp, v_current_partition_timestamp)) / EXTRACT('epoch' FROM v_row.part_interval::interval))); v_next_partition_timestamp := v_last_partition_timestamp; -- 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 BEGIN v_next_partition_timestamp := v_next_partition_timestamp + v_row.part_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 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', 'Child partition creation skippd for parent table '||v_partition_time); END IF; RAISE WARNING 'Attempted partition time interval is outside PostgreSQL''s supported time range. Child partition creation skipped for parent table %', v_row.parent_table; CONTINUE; END; v_last_partition_created := @extschema@.create_partition_time(v_row.parent_table, ARRAY[v_next_partition_timestamp], p_analyze); v_create_count := v_create_count + 1; IF v_row.type = 'time-static' AND v_last_partition_created THEN PERFORM @extschema@.create_function_time(v_row.parent_table); END IF; -- Manage additonal constraints if set PERFORM @extschema@.apply_constraints(v_row.parent_table); -- Can be negative when subpartitioning and there are parent partitions in the past compared to current timestamp value. -- abs() prevents run_maintenence from running on those old parent tables v_premade_count = abs(round(EXTRACT('epoch' FROM age(v_next_partition_timestamp, v_current_partition_timestamp)) / EXTRACT('epoch' FROM v_row.part_interval::interval))); END LOOP; ELSIF v_row.type = 'id-static' OR v_row.type ='id-dynamic' THEN -- 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 show_partitions FROM @extschema@.show_partitions(v_row.parent_table, 'DESC') LOOP EXECUTE 'SELECT '||v_row.control||' - ('||v_row.control||' % '||v_row.part_interval::int||') FROM '||v_row_max_id.show_partitions||' WHERE '||v_row.control||' = (SELECT max('||v_row.control||') FROM '||v_row_max_id.show_partitions||')' INTO v_current_partition_id; IF v_current_partition_id IS NOT NULL THEN EXIT; END IF; END LOOP; v_id_position := (length(v_last_partition) - position('p_' in reverse(v_last_partition))) + 2; v_last_partition_id = substring(v_last_partition from v_id_position)::bigint; v_next_partition_id := v_last_partition_id + v_row.part_interval::bigint; -- Can be negative when subpartitioning and there are parent partitions with lower values compared to current id value. -- abs() prevents run_maintenence from running on those old parent tables WHILE (abs((v_next_partition_id - v_current_partition_id) / v_row.part_interval::bigint)) <= v_row.premake LOOP v_last_partition_created := @extschema@.create_partition_id(v_row.parent_table, ARRAY[v_next_partition_id], p_analyze); IF v_last_partition_created THEN PERFORM @extschema@.create_function_id(v_row.parent_table); PERFORM @extschema@.apply_constraints(v_row.parent_table); END IF; v_next_partition_id := v_next_partition_id + v_row.part_interval::bigint; END LOOP; END IF; -- end main IF check for time or id END LOOP; -- end of creation loop -- Manage dropping old partitions if retention option is set FOR v_row IN SELECT parent_table FROM @extschema@.part_config WHERE retention IS NOT NULL AND undo_in_progress = false AND (type = 'time-static' OR type = 'time-dynamic' OR type = 'time-custom') LOOP IF p_parent_table IS NULL THEN v_drop_count := v_drop_count + @extschema@.drop_partition_time(v_row.parent_table); ELSE -- Only run retention on table given in parameter IF p_parent_table <> v_row.parent_table THEN CONTINUE; ELSE v_drop_count := v_drop_count + @extschema@.drop_partition_time(v_row.parent_table); END IF; END IF; END LOOP; FOR v_row IN SELECT parent_table FROM @extschema@.part_config WHERE retention IS NOT NULL AND undo_in_progress = false AND (type = 'id-static' OR type = 'id-dynamic') LOOP IF p_parent_table IS NULL THEN v_drop_count := v_drop_count + @extschema@.drop_partition_id(v_row.parent_table); ELSE -- Only run retention on table given in parameter IF p_parent_table <> v_row.parent_table THEN CONTINUE; ELSE v_drop_count := v_drop_count + @extschema@.drop_partition_id(v_row.parent_table); END IF; END IF; END LOOP; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Partition maintenance finished. '||v_create_count||' partitons made. '||v_drop_count||' partitions dropped.'); IF v_step_overflow_id IS NOT NULL OR v_step_serial_id IS NOT NULL THEN PERFORM fail_job(v_job_id); ELSE PERFORM close_job(v_job_id); END IF; EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN RUN MAINTENANCE'')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * 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. */ CREATE OR REPLACE FUNCTION 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 SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_child_table text; v_control text; v_drop_count int := 0; v_id_position int; v_index record; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_max bigint; v_old_search_path text; v_part_interval bigint; v_partition_id bigint; v_retention bigint; v_retention_keep_index boolean; v_retention_keep_table boolean; v_retention_schema text; v_row_max_id record; v_step_id bigint; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman drop_partition_id')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'drop_partition_id already running.'; RETURN 0; END IF; -- Allow override of configuration options IF p_retention IS NULL THEN SELECT part_interval::bigint , control , retention::bigint , retention_keep_table , retention_keep_index , retention_schema , jobmon INTO v_part_interval , v_control , v_retention , v_retention_keep_table , v_retention_keep_index , v_retention_schema , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic') AND retention IS NOT NULL; IF v_part_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table with a retention period not found: %', p_parent_table; END IF; ELSE SELECT part_interval::bigint , control , retention_keep_table , retention_keep_index , retention_schema , jobmon INTO v_part_interval , v_control , v_retention_keep_table , v_retention_keep_index , v_retention_schema , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic'); v_retention := p_retention; IF v_part_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF p_keep_table IS NOT NULL THEN v_retention_keep_table = p_keep_table; END IF; IF p_keep_index IS NOT NULL THEN v_retention_keep_index = p_keep_index; END IF; IF p_retention_schema IS NOT NULL THEN v_retention_schema = p_retention_schema; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN DROP ID PARTITION: '|| p_parent_table); 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 show_partitions FROM @extschema@.show_partitions(p_parent_table, 'DESC') LOOP EXECUTE 'SELECT max('||v_control||') FROM '||v_row_max_id.show_partitions INTO v_max; IF v_max IS NOT NULL THEN EXIT; END IF; END LOOP; -- Loop through child tables of the given parent FOR v_child_table IN SELECT n.nspname||'.'||c.relname FROM pg_inherits i join pg_class c ON i.inhrelid = c.oid join pg_namespace n ON c.relnamespace = n.oid WHERE i.inhparent::regclass = p_parent_table::regclass ORDER BY i.inhrelid ASC LOOP v_id_position := (length(v_child_table) - position('p_' in reverse(v_child_table))) + 2; v_partition_id := substring(v_child_table from v_id_position)::bigint; -- Add one interval since partition names contain the start of the constraint period IF v_retention <= (v_max - (v_partition_id + v_part_interval)) THEN IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Uninherit table '||v_child_table||' from '||p_parent_table); END IF; EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; 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, 'Drop table '||v_child_table); END IF; EXECUTE 'DROP TABLE '||v_child_table||' CASCADE'; 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 FOR v_index IN SELECT i.indexrelid::regclass AS name , c.conname FROM pg_catalog.pg_index i LEFT JOIN pg_catalog.pg_constraint c ON i.indexrelid = c.conindid WHERE i.indrelid = v_child_table::regclass LOOP IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Drop index '||v_index.name||' from '||v_child_table); END IF; IF v_index.conname IS NOT NULL THEN EXECUTE 'ALTER TABLE '||v_child_table||' DROP CONSTRAINT '||v_index.conname; ELSE EXECUTE 'DROP INDEX '||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; ELSE -- Move to new schema IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Moving table '||v_child_table||' to schema '||v_retention_schema); END IF; EXECUTE 'ALTER TABLE '||v_child_table||' SET SCHEMA '||v_retention_schema; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; -- End retention schema if -- If child table is a subpartition, remove it from part_config & part_config_sub (should cascade due to FK) DELETE FROM @extschema@.part_config WHERE parent_table = v_child_table; v_drop_count := v_drop_count + 1; END IF; -- End retention check IF END LOOP; -- End child table loop IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Finished partition drop maintenance'); PERFORM update_step(v_step_id, 'OK', v_drop_count||' partitions dropped.'); PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_drop_count; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN DROP ID PARTITION: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to create a child table in a time-based partition set */ CREATE OR REPLACE FUNCTION create_partition_time (p_parent_table text, p_partition_times timestamp[], p_analyze boolean DEFAULT true) RETURNS boolean LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_all text[] := ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER']; v_analyze boolean := FALSE; v_control text; v_datetime_string text; v_grantees text[]; v_hasoids boolean; v_inherit_fk boolean; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_parent_grant record; v_parent_owner text; v_parent_schema text; v_parent_tablename text; v_partition_created boolean := false; v_partition_name text; v_partition_suffix text; v_parent_tablespace text; v_part_interval interval; v_partition_timestamp_end timestamp; v_partition_timestamp_start timestamp; v_quarter text; v_revoke text[]; v_row record; v_sql text; v_step_id bigint; v_step_overflow_id bigint; v_sub_timestamp_max timestamp; v_sub_timestamp_min timestamp; v_tablename text; v_time_position int; v_top_datetime_string text; v_top_interval interval; v_top_parent text; v_trunc_value text; v_time timestamp; v_type text; v_unlogged char; v_year text; BEGIN SELECT type , control , part_interval , inherit_fk , jobmon , datetime_string INTO v_type , v_control , v_part_interval , v_inherit_fk , v_jobmon , v_datetime_string FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic' OR type = 'time-custom'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; -- Check if parent table is a subpartition of an already existing time-based partition set managed by pg_partman -- If so, limit what child tables can be created based on parent suffix WITH top_oid AS ( SELECT i.inhparent AS top_parent_oid FROM pg_catalog.pg_class c JOIN pg_catalog.pg_inherits i ON c.oid = i.inhrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname||'.'||c.relname = p_parent_table ) SELECT n.nspname||'.'||c.relname, p.datetime_string INTO v_top_parent, v_top_datetime_string FROM pg_catalog.pg_class c JOIN top_oid t ON c.oid = t.top_parent_oid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace JOIN @extschema@.part_config p ON p.parent_table = n.nspname||'.'||c.relname WHERE c.oid = t.top_parent_oid AND p.type = 'time-static' OR p.type = 'time-dynamic' OR p.type = 'time-custom'; IF v_top_parent IS NOT NULL THEN SELECT part_interval::interval INTO v_top_interval FROM @extschema@.part_config WHERE parent_table = v_top_parent; v_time_position := (length(p_parent_table) - position('p_' in reverse(p_parent_table))) + 2; IF v_part_interval::interval <> '3 months' OR (v_part_interval::interval = '3 months' AND v_type = 'time-custom') THEN v_sub_timestamp_min := to_timestamp(substring(p_parent_table from v_time_position), v_top_datetime_string); ELSE -- to_timestamp doesn't recognize 'Q' date string formater. Handle it v_year := split_part(substring(p_parent_table from v_time_position), 'q', 1); v_quarter := split_part(substring(p_parent_table from v_time_position), 'q', 2); CASE WHEN v_quarter = '1' THEN v_sub_timestamp_min := to_timestamp(v_year || '-01-01', 'YYYY-MM-DD'); WHEN v_quarter = '2' THEN v_sub_timestamp_min := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); WHEN v_quarter = '3' THEN v_sub_timestamp_min := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); WHEN v_quarter = '4' THEN v_sub_timestamp_min := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); END CASE; END IF; v_sub_timestamp_max = (v_sub_timestamp_min + v_top_interval::interval) - '1 sec'::interval; END IF; SELECT tableowner, schemaname, tablename, tablespace INTO v_parent_owner, v_parent_schema, v_parent_tablename, v_parent_tablespace FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); END IF; FOREACH v_time IN ARRAY p_partition_times LOOP v_partition_timestamp_start := v_time; BEGIN v_partition_timestamp_end := v_time + v_part_interval; EXCEPTION WHEN datetime_field_overflow THEN 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'); CONTINUE; END; -- This suffix generation code is in partition_data_time() as well v_partition_suffix := to_char(v_time, 'YYYY'); IF v_part_interval < '1 year' AND v_part_interval <> '1 week' THEN v_partition_suffix := v_partition_suffix ||'_'|| to_char(v_time, 'MM'); IF v_part_interval < '1 month' AND v_part_interval <> '1 week' THEN v_partition_suffix := v_partition_suffix ||'_'|| to_char(v_time, 'DD'); IF v_part_interval < '1 day' THEN v_partition_suffix := v_partition_suffix || '_' || to_char(v_time, 'HH24MI'); IF v_part_interval < '1 minute' THEN v_partition_suffix := v_partition_suffix || to_char(v_time, 'SS'); END IF; -- end < minute IF END IF; -- end < day IF END IF; -- end < month IF END IF; -- end < year IF IF v_part_interval = '1 week' THEN v_partition_suffix := to_char(v_time, 'IYYY') || 'w' || to_char(v_time, 'IW'); END IF; -- "Q" is ignored in to_timestamp, so handle special case IF v_part_interval = '3 months' AND (v_type = 'time-static' OR v_type = 'time-dynamic') THEN v_year := to_char(v_time, 'YYYY'); v_quarter := to_char(v_time, 'Q'); v_partition_suffix := v_year || 'q' || v_quarter; END IF; -- 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 CONTINUE; END IF; END IF; v_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_partition_suffix, TRUE); SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_tablename IS NOT NULL 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, 'Creating new partition '||v_partition_name||' with interval from '||v_partition_timestamp_start||' to '||(v_partition_timestamp_end-'1sec'::interval)); END IF; SELECT relpersistence INTO v_unlogged FROM pg_catalog.pg_class WHERE oid::regclass = p_parent_table::regclass; v_sql := 'CREATE'; IF v_unlogged = 'u' THEN v_sql := v_sql || ' UNLOGGED'; END IF; v_sql := v_sql || ' TABLE '||v_partition_name||' (LIKE '||p_parent_table||' INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING STORAGE INCLUDING COMMENTS)'; SELECT relhasoids INTO v_hasoids FROM pg_catalog.pg_class WHERE oid::regclass = p_parent_table::regclass; IF v_hasoids IS TRUE THEN v_sql := v_sql || ' WITH (OIDS)'; END IF; EXECUTE v_sql; SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_parent_tablespace IS NOT NULL THEN EXECUTE 'ALTER TABLE '||v_partition_name||' SET TABLESPACE '||v_parent_tablespace; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' ADD CONSTRAINT '||v_tablename||'_partition_check CHECK ('||v_control||'>='||quote_literal(v_partition_timestamp_start)||' AND '||v_control||'<'||quote_literal(v_partition_timestamp_end)||')'; EXECUTE 'ALTER TABLE '||v_partition_name||' INHERIT '||p_parent_table; -- If custom time, set extra config options. IF v_type = 'time-custom' THEN INSERT INTO @extschema@.custom_time_partitions (parent_table, child_table, partition_range) VALUES ( p_parent_table, v_partition_name, tstzrange(v_partition_timestamp_start, v_partition_timestamp_end, '[)') ); END IF; FOR v_parent_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = p_parent_table GROUP BY grantee LOOP EXECUTE 'GRANT '||array_to_string(v_parent_grant.types, ',')||' ON '||v_partition_name||' TO '||v_parent_grant.grantee; SELECT array_agg(r) INTO v_revoke FROM (SELECT unnest(v_all) AS r EXCEPT SELECT unnest(v_parent_grant.types)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE '||array_to_string(v_revoke, ',')||' ON '||v_partition_name||' FROM '||v_parent_grant.grantee||' CASCADE'; END IF; v_grantees := array_append(v_grantees, v_parent_grant.grantee::text); END LOOP; -- Revoke all privileges from roles that have none on the parent IF v_grantees IS NOT NULL THEN SELECT array_agg(r) INTO v_revoke FROM ( SELECT DISTINCT grantee::text AS r FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = v_partition_name EXCEPT SELECT unnest(v_grantees)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE ALL ON '||v_partition_name||' FROM '||array_to_string(v_revoke, ','); END IF; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' OWNER TO '||v_parent_owner; IF v_inherit_fk THEN PERFORM @extschema@.apply_foreign_keys(quote_ident(v_parent_schema)||'.'||quote_ident(v_parent_tablename), v_partition_name); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); 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 FOR v_row IN SELECT sub_parent , sub_control , sub_type , sub_part_interval , sub_constraint_cols , sub_premake , sub_inherit_fk , sub_retention , sub_retention_schema , sub_retention_keep_table , sub_retention_keep_index , sub_use_run_maintenance , sub_jobmon FROM @extschema@.part_config_sub WHERE sub_parent = p_parent_table LOOP IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Subpartitioning '||v_partition_name); END IF; v_sql := format('SELECT @extschema@.create_parent( p_parent_table := %L , p_control := %L , p_type := %L , p_interval := %L , p_constraint_cols := %L , p_premake := %L , p_use_run_maintenance := %L , p_inherit_fk := %L , p_jobmon := %L )' , v_partition_name , v_row.sub_control , v_row.sub_type , v_row.sub_part_interval , v_row.sub_constraint_cols , v_row.sub_premake , v_row.sub_inherit_fk , v_row.sub_use_run_maintenance , v_row.sub_jobmon); EXECUTE v_sql; 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 WHERE parent_table = v_partition_name; END LOOP; -- end sub partitioning LOOP 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, 'Analyzing partition set: '||p_parent_table); END IF; EXECUTE 'ANALYZE '||p_parent_table; 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 v_step_id := add_step(v_job_id, 'No partitions created for partition set: '||p_parent_table); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; IF v_step_overflow_id IS NOT NULL THEN PERFORM fail_job(v_job_id); ELSE PERFORM close_job(v_job_id); END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_partition_created; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN CREATE TABLE: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Apply constraints managed by partman extension */ CREATE OR REPLACE FUNCTION apply_constraints(p_parent_table text, p_child_table text DEFAULT NULL, p_analyze boolean DEFAULT FALSE, p_debug boolean DEFAULT FALSE) RETURNS void LANGUAGE plpgsql AS $$ DECLARE v_child_table text; v_child_tablename text; v_col text; v_constraint_cols text[]; v_constraint_col_type text; v_constraint_name text; v_datetime_string text; v_existing_constraint_name text; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_last_partition text; v_last_partition_id int; v_last_partition_timestamp timestamp; v_constraint_values record; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_part_interval text; v_partition_suffix text; v_premake int; v_sql text; v_step_id bigint; v_suffix_position int; v_type text; BEGIN SELECT type , part_interval , premake , datetime_string , constraint_cols , jobmon INTO v_type , v_part_interval , v_premake , v_datetime_string , v_constraint_cols , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_constraint_cols IS NULL THEN IF p_debug THEN RAISE NOTICE 'Given parent table (%) not set up for constraint management (constraint_cols is NULL)', p_parent_table; END IF; -- Returns silently to allow this function to be simply called by maintenance processes without having to check if config options are set. RETURN; END IF; SELECT show_partitions INTO v_last_partition FROM @extschema@.show_partitions(p_parent_table, 'DESC') LIMIT 1; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE CONSTRAINT: '||p_parent_table); END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; -- If p_child_table is null, figure out the partition that is the one right before the premake value backwards. IF p_child_table IS NULL THEN IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Automatically determining most recent child on which to apply constraints'); END IF; v_suffix_position := (length(v_last_partition) - position('p_' in reverse(v_last_partition))) + 2; IF v_type IN ('time-static', 'time-dynamic', 'time-custom') THEN v_last_partition_timestamp := to_timestamp(substring(v_last_partition from v_suffix_position), v_datetime_string); v_partition_suffix := to_char(v_last_partition_timestamp - (v_part_interval::interval * ((v_premake * 2)+1) ), v_datetime_string); ELSIF v_type IN ('id-static', 'id-dynamic') THEN v_last_partition_id := substring(v_last_partition from v_suffix_position)::int; v_partition_suffix := (v_last_partition_id - (v_part_interval::int * ((v_premake * 2)+1) ))::text; END IF; v_child_table := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_partition_suffix, TRUE); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Target child table: '||v_child_table); END IF; ELSE v_child_table := p_child_table; END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Checking if target child table exists'); END IF; SELECT tablename INTO v_child_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_child_table; IF v_child_tablename IS NULL THEN IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'NOTICE', 'Target child table ('||v_child_table||') does not exist. Skipping constraint creation.'); PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; IF p_debug THEN RAISE NOTICE 'Target child table (%) does not exist. Skipping constraint creation.', v_child_table; END IF; RETURN; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; FOREACH v_col IN ARRAY v_constraint_cols LOOP SELECT c.conname INTO v_existing_constraint_name FROM pg_catalog.pg_constraint c JOIN pg_catalog.pg_attribute a ON c.conrelid = a.attrelid WHERE conrelid = v_child_table::regclass AND c.conname LIKE 'partmanconstr_%' AND c.contype = 'c' AND a.attname = v_col AND ARRAY[a.attnum] <@ c.conkey AND a.attisdropped = false; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Applying new constraint on column: '||v_col); END IF; IF v_existing_constraint_name IS NOT NULL THEN IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'NOTICE', 'Partman managed constraint already exists on this table ('||v_child_table||') and column ('||v_col||'). Skipping creation.'); END IF; RAISE WARNING 'Partman managed constraint already exists on this table (%) and column (%). Skipping creation.', v_child_table, v_col ; CONTINUE; END IF; -- 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 'SELECT min('||v_col||')::text AS min, max('||v_col||')::text AS max FROM '||v_child_table INTO v_constraint_values; IF v_constraint_values IS NOT NULL THEN v_sql := concat('ALTER TABLE ', v_child_table, ' ADD CONSTRAINT ', v_constraint_name , ' CHECK (', v_col, ' >= ', quote_literal(v_constraint_values.min), ' AND ' , v_col, ' <= ', quote_literal(v_constraint_values.max), ')' ); IF p_debug THEN RAISE NOTICE 'Constraint creation query: %', v_sql; END IF; EXECUTE v_sql; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'New constraint created: '||v_sql); END IF; ELSE IF p_debug THEN RAISE NOTICE 'Given column (%) contains all NULLs. No constraint created', v_col; END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'NOTICE', 'Given column ('||v_col||') contains all NULLs. No constraint created'); END IF; END IF; END LOOP; IF p_analyze THEN IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Running analyze on partition set: '||p_parent_table); END IF; IF p_debug THEN RAISE NOTICE 'Running analyze on partition set: %', p_parent_table; END IF; EXECUTE 'ANALYZE '||p_parent_table; 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 PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN CREATE CONSTRAINT: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; pg_partman-2.2.2/updates/pg_partman--1.8.2--1.8.3.sql000066400000000000000000001376651262146621700214570ustar00rootroot00000000000000-- 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 partitons 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. /* * Function to undo time-based partitioning created by this extension */ CREATE OR REPLACE FUNCTION undo_partition_time(p_parent_table text, p_batch_count int DEFAULT 1, p_batch_interval interval DEFAULT NULL, p_keep_table boolean DEFAULT true, p_lock_wait numeric DEFAULT 0) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_batch_loop_count int := 0; v_child_min timestamptz; v_child_loop_total bigint := 0; v_child_table text; v_control text; v_function_name text; v_inner_loop_count int; v_lock_iter int := 1; v_lock_obtained boolean := FALSE; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_move_sql text; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_part_interval interval; v_row record; v_rowcount bigint; v_step_id bigint; v_sub_count int; v_total bigint := 0; v_trig_name text; v_type text; v_undo_count int := 0; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman undo_time_partition')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'undo_time_partition already running.'; RETURN 0; END IF; SELECT type , part_interval::interval , control , jobmon INTO v_type , v_part_interval , v_control , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic' OR type = 'time-custom'); IF v_part_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; -- Check if any child tables are themselves partitioned or part of an inheritance tree. Prevent undo at this level if so. -- Need to either lock child tables at all levels or handle the proper removal of triggers on all child tables first -- before multi-level undo can be performed safely. FOR v_row IN SELECT show_partitions AS child_table FROM @extschema@.show_partitions(p_parent_table) LOOP SELECT count(*) INTO v_sub_count FROM pg_catalog.pg_inherits WHERE inhparent::regclass = v_row.child_table::regclass; 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.child_table; END IF; END LOOP; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN UNDO PARTITIONING: '||p_parent_table); v_step_id := add_step(v_job_id, 'Undoing partitioning for table '||p_parent_table); END IF; IF p_batch_interval IS NULL THEN p_batch_interval := v_part_interval; END IF; -- Stops new time partitons 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; -- Stop data going into child tables. SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; 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, v_parent_schema, '_part_trig_func', FALSE); SELECT tgname INTO v_trig_name FROM pg_catalog.pg_trigger t WHERE tgname = v_trig_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 'LOCK TABLE ONLY '||p_parent_table||' IN ACCESS EXCLUSIVE MODE NOWAIT'; 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'; RETURN -1; END IF; END IF; -- END p_lock_wait IF EXECUTE 'DROP TRIGGER IF EXISTS '||v_trig_name||' ON '||p_parent_table; END IF; -- END trigger IF v_lock_obtained := FALSE; -- reset for reuse later EXECUTE 'DROP FUNCTION IF EXISTS '||v_function_name||'()'; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Stopped partition creation process. Removed trigger & trigger function'); END IF; <> WHILE v_batch_loop_count < p_batch_count LOOP SELECT n.nspname||'.'||c.relname INTO v_child_table FROM pg_inherits i JOIN pg_class c ON i.inhrelid = c.oid JOIN pg_namespace n ON c.relnamespace = n.oid WHERE i.inhparent::regclass = p_parent_table::regclass ORDER BY i.inhrelid ASC; EXIT WHEN v_child_table IS NULL; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing child partition: '||v_child_table); END IF; EXECUTE 'SELECT min('||v_control||') FROM '||v_child_table INTO v_child_min; IF v_child_min IS NULL THEN -- No rows left in this child table. Remove from partition set. -- lockwait timeout for table 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 'LOCK TABLE ONLY '||v_child_table||' IN ACCESS EXCLUSIVE MODE NOWAIT'; 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 child table for removal from partition set'; RETURN -1; END IF; END IF; -- END p_lock_wait IF v_lock_obtained := FALSE; -- reset for reuse later EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; IF p_keep_table = false THEN EXECUTE 'DROP TABLE '||v_child_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table DROPPED. Moved '||v_child_loop_total||' rows to parent'); END IF; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table UNINHERITED, not DROPPED. Moved '||v_child_loop_total||' rows to parent'); END IF; END IF; IF v_type = 'time-custom' THEN DELETE FROM @extschema@.custom_time_partitions WHERE parent_table = p_parent_table AND child_table = v_child_table; END IF; v_undo_count := v_undo_count + 1; CONTINUE outer_child_loop; END IF; v_inner_loop_count := 1; v_child_loop_total := 0; <> LOOP -- do some locking with timeout, if required 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 'SELECT * FROM ' || v_child_table || ' WHERE '||v_control||' <= '||quote_literal(v_child_min + (p_batch_interval * v_inner_loop_count)) ||' FOR UPDATE NOWAIT'; 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 batch of rows to move'; RETURN -1; END IF; END IF; -- Get everything from the current child minimum up to the multiples of the given interval v_move_sql := 'WITH move_data AS (DELETE FROM '||v_child_table|| ' WHERE '||v_control||' <= '||quote_literal(v_child_min + (p_batch_interval * v_inner_loop_count))||' RETURNING *) INSERT INTO '||p_parent_table||' SELECT * FROM move_data'; EXECUTE v_move_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total := v_total + v_rowcount; v_child_loop_total := v_child_loop_total + v_rowcount; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Moved '||v_child_loop_total||' rows to parent.'); END IF; EXIT inner_child_loop WHEN v_rowcount = 0; -- exit before loop incr if table is empty v_inner_loop_count := v_inner_loop_count + 1; v_batch_loop_count := v_batch_loop_count + 1; EXIT outer_child_loop WHEN v_batch_loop_count >= p_batch_count; -- Exit outer FOR loop if p_batch_count is reached END LOOP inner_child_loop; END LOOP outer_child_loop; IF v_batch_loop_count < p_batch_count THEN -- FOR loop never ran, so there's no child tables left. DELETE FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing config from pg_partman'); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; RAISE NOTICE 'Copied % row(s) to the parent. Removed % partitions.', v_total, v_undo_count; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Final stats'); PERFORM update_step(v_step_id, 'OK', 'Copied '||v_total||' row(s) to the parent. Removed '||v_undo_count||' partitions.'); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_total; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN UNDO PARTITIONING: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to drop child tables from a time-based partition set. * Options to move table to different schema, drop only indexes or actually drop the table from the database. */ CREATE OR REPLACE FUNCTION 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) RETURNS int LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_child_table text; v_datetime_string text; v_drop_count int := 0; v_index record; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_part_interval interval; v_partition_timestamp timestamp; v_quarter text; v_retention interval; v_retention_keep_index boolean; v_retention_keep_table boolean; v_retention_schema text; v_step_id bigint; v_time_position int; v_type text; v_year text; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman drop_partition_time')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'drop_partition_time already running.'; RETURN 0; END IF; -- Allow override of configuration options IF p_retention IS NULL THEN SELECT type , part_interval::interval , retention::interval , retention_keep_table , retention_keep_index , datetime_string , retention_schema , jobmon INTO v_type , v_part_interval , v_retention , v_retention_keep_table , v_retention_keep_index , v_datetime_string , v_retention_schema , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic' OR type = 'time-custom') AND retention IS NOT NULL; IF v_part_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table with a retention period not found: %', p_parent_table; END IF; ELSE SELECT type , part_interval::interval , retention_keep_table , retention_keep_index , datetime_string , retention_schema , jobmon INTO v_type , v_part_interval , v_retention_keep_table , v_retention_keep_index , v_datetime_string , v_retention_schema , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic' OR type = 'time-custom'); v_retention := p_retention; IF v_part_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF p_keep_table IS NOT NULL THEN v_retention_keep_table = p_keep_table; END IF; IF p_keep_index IS NOT NULL THEN v_retention_keep_index = p_keep_index; END IF; IF p_retention_schema IS NOT NULL THEN v_retention_schema = p_retention_schema; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN DROP TIME PARTITION: '|| p_parent_table); END IF; -- Loop through child tables of the given parent FOR v_child_table IN SELECT n.nspname||'.'||c.relname FROM pg_inherits i join pg_class c ON i.inhrelid = c.oid join pg_namespace n ON c.relnamespace = n.oid WHERE i.inhparent::regclass = p_parent_table::regclass ORDER BY i.inhrelid ASC LOOP -- pull out datetime portion of partition's tablename to make the next one v_time_position := (length(v_child_table) - position('p_' in reverse(v_child_table))) + 2; IF v_part_interval <> '3 months' OR (v_part_interval = '3 months' AND v_type = 'time-custom') THEN v_partition_timestamp := to_timestamp(substring(v_child_table from v_time_position), v_datetime_string); ELSE -- to_timestamp doesn't recognize 'Q' date string formater. Handle it v_year := split_part(substring(v_child_table from v_time_position), 'q', 1); v_quarter := split_part(substring(v_child_table from v_time_position), 'q', 2); CASE WHEN v_quarter = '1' THEN v_partition_timestamp := to_timestamp(v_year || '-01-01', 'YYYY-MM-DD'); WHEN v_quarter = '2' THEN v_partition_timestamp := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); WHEN v_quarter = '3' THEN v_partition_timestamp := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); WHEN v_quarter = '4' THEN v_partition_timestamp := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); END CASE; END IF; -- Add one interval since partition names contain the start of the constraint period IF v_retention < (CURRENT_TIMESTAMP - (v_partition_timestamp + v_part_interval)) THEN IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Uninherit table '||v_child_table||' from '||p_parent_table); END IF; IF v_type = 'time-custom' THEN DELETE FROM @extschema@.custom_time_partitions WHERE parent_table = p_parent_table AND child_table = v_child_table; END IF; EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; 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, 'Drop table '||v_child_table); END IF; EXECUTE 'DROP TABLE '||v_child_table||' CASCADE'; 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 FOR v_index IN SELECT i.indexrelid::regclass AS name , c.conname FROM pg_catalog.pg_index i LEFT JOIN pg_catalog.pg_constraint c ON i.indexrelid = c.conindid WHERE i.indrelid = v_child_table::regclass LOOP IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Drop index '||v_index.name||' from '||v_child_table); END IF; IF v_index.conname IS NOT NULL THEN EXECUTE 'ALTER TABLE '||v_child_table||' DROP CONSTRAINT '||v_index.conname; ELSE EXECUTE 'DROP INDEX '||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; ELSE -- Move to new schema IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Moving table '||v_child_table||' to schema '||v_retention_schema); END IF; EXECUTE 'ALTER TABLE '||v_child_table||' SET SCHEMA '||v_retention_schema; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; -- End retention schema if -- If child table is a subpartition, remove it from part_config & part_config_sub (should cascade due to FK) DELETE FROM @extschema@.part_config WHERE parent_table = v_child_table; v_drop_count := v_drop_count + 1; END IF; -- End retention check IF END LOOP; -- End child table loop IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Finished partition drop maintenance'); PERFORM update_step(v_step_id, 'OK', v_drop_count||' partitions dropped.'); PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_drop_count; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN DROP TIME PARTITION: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to create id partitions */ CREATE OR REPLACE FUNCTION create_partition_id(p_parent_table text, p_partition_ids bigint[], p_analyze boolean DEFAULT true) RETURNS boolean LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_all text[] := ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER']; v_analyze boolean := FALSE; v_control text; v_grantees text[]; v_hasoids boolean; v_id bigint; v_id_position int; v_inherit_fk boolean; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_parent_grant record; v_parent_owner text; v_parent_schema text; v_parent_tablename text; v_parent_tablespace text; v_part_interval bigint; v_partition_created boolean := false; v_partition_name text; v_revoke text[]; v_row record; v_sql text; v_step_id bigint; v_sub_id_max bigint; v_sub_id_min bigint; v_tablename text; v_top_interval bigint; v_top_parent text; v_unlogged char; BEGIN SELECT control , part_interval , inherit_fk , jobmon INTO v_control , v_part_interval , v_inherit_fk , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; -- Check if parent table is a subpartition of an already existing id based partition set managed by pg_partman -- If so, limit what child tables can be created based on parent suffix WITH top_oid AS ( SELECT i.inhparent AS top_parent_oid FROM pg_catalog.pg_class c JOIN pg_catalog.pg_inherits i ON c.oid = i.inhrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname||'.'||c.relname = p_parent_table ) SELECT n.nspname||'.'||c.relname INTO v_top_parent FROM pg_catalog.pg_class c JOIN top_oid t ON c.oid = t.top_parent_oid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace JOIN @extschema@.part_config p ON p.parent_table = n.nspname||'.'||c.relname WHERE c.oid = t.top_parent_oid AND p.type = 'id-static' OR p.type = 'id-dynamic'; IF v_top_parent IS NOT NULL THEN SELECT part_interval::bigint INTO v_top_interval FROM @extschema@.part_config WHERE parent_table = v_top_parent; v_id_position := (length(p_parent_table) - position('p_' in reverse(p_parent_table))) + 2; v_sub_id_min = substring(p_parent_table from v_id_position)::bigint; v_sub_id_max = (v_sub_id_min + v_top_interval) - 1; END IF; SELECT tableowner, schemaname, tablename, tablespace INTO v_parent_owner, v_parent_schema, v_parent_tablename, v_parent_tablespace FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); 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. 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; END IF; END IF; v_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_id::text, TRUE); -- If child table already exists, skip creation SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_tablename IS NOT NULL 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, 'Creating new partition '||v_partition_name||' with interval from '||v_id||' to '||(v_id + v_part_interval)-1); END IF; SELECT relpersistence INTO v_unlogged FROM pg_catalog.pg_class WHERE oid::regclass = p_parent_table::regclass; v_sql := 'CREATE'; IF v_unlogged = 'u' THEN v_sql := v_sql || ' UNLOGGED'; END IF; v_sql := v_sql || ' TABLE '||v_partition_name||' (LIKE '||p_parent_table||' INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING STORAGE INCLUDING COMMENTS)'; SELECT relhasoids INTO v_hasoids FROM pg_catalog.pg_class WHERE oid::regclass = p_parent_table::regclass; IF v_hasoids IS TRUE THEN v_sql := v_sql || ' WITH (OIDS)'; END IF; EXECUTE v_sql; SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_parent_tablespace IS NOT NULL THEN EXECUTE 'ALTER TABLE '||v_partition_name||' SET TABLESPACE '||v_parent_tablespace; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' ADD CONSTRAINT '||v_tablename||'_partition_check CHECK ('||v_control||'>='||quote_literal(v_id)||' AND '||v_control||'<'||quote_literal(v_id + v_part_interval)||')'; EXECUTE 'ALTER TABLE '||v_partition_name||' INHERIT '||p_parent_table; FOR v_parent_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = p_parent_table GROUP BY grantee LOOP EXECUTE 'GRANT '||array_to_string(v_parent_grant.types, ',')||' ON '||v_partition_name||' TO '||v_parent_grant.grantee; SELECT array_agg(r) INTO v_revoke FROM (SELECT unnest(v_all) AS r EXCEPT SELECT unnest(v_parent_grant.types)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE '||array_to_string(v_revoke, ',')||' ON '||v_partition_name||' FROM '||v_parent_grant.grantee||' CASCADE'; END IF; v_grantees := array_append(v_grantees, v_parent_grant.grantee::text); END LOOP; -- Revoke all privileges from roles that have none on the parent IF v_grantees IS NOT NULL THEN SELECT array_agg(r) INTO v_revoke FROM ( SELECT DISTINCT grantee::text AS r FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = v_partition_name EXCEPT SELECT unnest(v_grantees)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE ALL ON '||v_partition_name||' FROM '||array_to_string(v_revoke, ','); END IF; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' OWNER TO '||v_parent_owner; IF v_inherit_fk THEN PERFORM @extschema@.apply_foreign_keys(quote_ident(v_parent_schema)||'.'||quote_ident(v_parent_tablename), v_partition_name); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); 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 FOR v_row IN SELECT sub_parent , sub_control , sub_type , sub_part_interval , sub_constraint_cols , sub_premake , sub_inherit_fk , sub_retention , sub_retention_schema , sub_retention_keep_table , sub_retention_keep_index , sub_use_run_maintenance , sub_jobmon FROM @extschema@.part_config_sub WHERE sub_parent = p_parent_table LOOP IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Subpartitioning '||v_partition_name); END IF; v_sql := format('SELECT @extschema@.create_parent( p_parent_table := %L , p_control := %L , p_type := %L , p_interval := %L , p_constraint_cols := %L , p_premake := %L , p_use_run_maintenance := %L , p_inherit_fk := %L , p_jobmon := %L )' , v_partition_name , v_row.sub_control , v_row.sub_type , v_row.sub_part_interval , v_row.sub_constraint_cols , v_row.sub_premake , v_row.sub_use_run_maintenance , v_row.sub_inherit_fk , v_row.sub_jobmon); EXECUTE v_sql; 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 WHERE parent_table = v_partition_name; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END LOOP; -- end sub partitioning LOOP 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, 'Analyzing partition set: '||p_parent_table); END IF; EXECUTE 'ANALYZE '||p_parent_table; 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 v_step_id := add_step(v_job_id, 'No partitions created for partition set: '||p_parent_table); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; PERFORM close_job(v_job_id); END IF; IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_partition_created; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN CREATE TABLE: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to create a child table in a time-based partition set */ CREATE OR REPLACE FUNCTION create_partition_time (p_parent_table text, p_partition_times timestamp[], p_analyze boolean DEFAULT true) RETURNS boolean LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_all text[] := ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER']; v_analyze boolean := FALSE; v_control text; v_datetime_string text; v_grantees text[]; v_hasoids boolean; v_inherit_fk boolean; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_parent_grant record; v_parent_owner text; v_parent_schema text; v_parent_tablename text; v_partition_created boolean := false; v_partition_name text; v_partition_suffix text; v_parent_tablespace text; v_part_interval interval; v_partition_timestamp_end timestamp; v_partition_timestamp_start timestamp; v_quarter text; v_revoke text[]; v_row record; v_sql text; v_step_id bigint; v_step_overflow_id bigint; v_sub_timestamp_max timestamp; v_sub_timestamp_min timestamp; v_tablename text; v_time_position int; v_top_datetime_string text; v_top_interval interval; v_top_parent text; v_trunc_value text; v_time timestamp; v_type text; v_unlogged char; v_year text; BEGIN SELECT type , control , part_interval , inherit_fk , jobmon , datetime_string INTO v_type , v_control , v_part_interval , v_inherit_fk , v_jobmon , v_datetime_string FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic' OR type = 'time-custom'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; -- Check if parent table is a subpartition of an already existing time-based partition set managed by pg_partman -- If so, limit what child tables can be created based on parent suffix WITH top_oid AS ( SELECT i.inhparent AS top_parent_oid FROM pg_catalog.pg_class c JOIN pg_catalog.pg_inherits i ON c.oid = i.inhrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname||'.'||c.relname = p_parent_table ) SELECT n.nspname||'.'||c.relname, p.datetime_string INTO v_top_parent, v_top_datetime_string FROM pg_catalog.pg_class c JOIN top_oid t ON c.oid = t.top_parent_oid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace JOIN @extschema@.part_config p ON p.parent_table = n.nspname||'.'||c.relname WHERE c.oid = t.top_parent_oid AND p.type = 'time-static' OR p.type = 'time-dynamic' OR p.type = 'time-custom'; IF v_top_parent IS NOT NULL THEN SELECT part_interval::interval INTO v_top_interval FROM @extschema@.part_config WHERE parent_table = v_top_parent; v_time_position := (length(p_parent_table) - position('p_' in reverse(p_parent_table))) + 2; IF v_part_interval::interval <> '3 months' OR (v_part_interval::interval = '3 months' AND v_type = 'time-custom') THEN v_sub_timestamp_min := to_timestamp(substring(p_parent_table from v_time_position), v_top_datetime_string); ELSE -- to_timestamp doesn't recognize 'Q' date string formater. Handle it v_year := split_part(substring(p_parent_table from v_time_position), 'q', 1); v_quarter := split_part(substring(p_parent_table from v_time_position), 'q', 2); CASE WHEN v_quarter = '1' THEN v_sub_timestamp_min := to_timestamp(v_year || '-01-01', 'YYYY-MM-DD'); WHEN v_quarter = '2' THEN v_sub_timestamp_min := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); WHEN v_quarter = '3' THEN v_sub_timestamp_min := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); WHEN v_quarter = '4' THEN v_sub_timestamp_min := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); END CASE; END IF; v_sub_timestamp_max = (v_sub_timestamp_min + v_top_interval::interval) - '1 sec'::interval; END IF; SELECT tableowner, schemaname, tablename, tablespace INTO v_parent_owner, v_parent_schema, v_parent_tablename, v_parent_tablespace FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); END IF; FOREACH v_time IN ARRAY p_partition_times LOOP v_partition_timestamp_start := v_time; BEGIN v_partition_timestamp_end := v_time + v_part_interval; EXCEPTION WHEN datetime_field_overflow THEN 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'); CONTINUE; END; -- This suffix generation code is in partition_data_time() as well v_partition_suffix := to_char(v_time, 'YYYY'); IF v_part_interval < '1 year' AND v_part_interval <> '1 week' THEN v_partition_suffix := v_partition_suffix ||'_'|| to_char(v_time, 'MM'); IF v_part_interval < '1 month' AND v_part_interval <> '1 week' THEN v_partition_suffix := v_partition_suffix ||'_'|| to_char(v_time, 'DD'); IF v_part_interval < '1 day' THEN v_partition_suffix := v_partition_suffix || '_' || to_char(v_time, 'HH24MI'); IF v_part_interval < '1 minute' THEN v_partition_suffix := v_partition_suffix || to_char(v_time, 'SS'); END IF; -- end < minute IF END IF; -- end < day IF END IF; -- end < month IF END IF; -- end < year IF IF v_part_interval = '1 week' THEN v_partition_suffix := to_char(v_time, 'IYYY') || 'w' || to_char(v_time, 'IW'); END IF; -- "Q" is ignored in to_timestamp, so handle special case IF v_part_interval = '3 months' AND (v_type = 'time-static' OR v_type = 'time-dynamic') THEN v_year := to_char(v_time, 'YYYY'); v_quarter := to_char(v_time, 'Q'); v_partition_suffix := v_year || 'q' || v_quarter; END IF; -- 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 CONTINUE; END IF; END IF; v_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_partition_suffix, TRUE); SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_tablename IS NOT NULL 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, 'Creating new partition '||v_partition_name||' with interval from '||v_partition_timestamp_start||' to '||(v_partition_timestamp_end-'1sec'::interval)); END IF; SELECT relpersistence INTO v_unlogged FROM pg_catalog.pg_class WHERE oid::regclass = p_parent_table::regclass; v_sql := 'CREATE'; IF v_unlogged = 'u' THEN v_sql := v_sql || ' UNLOGGED'; END IF; v_sql := v_sql || ' TABLE '||v_partition_name||' (LIKE '||p_parent_table||' INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING STORAGE INCLUDING COMMENTS)'; SELECT relhasoids INTO v_hasoids FROM pg_catalog.pg_class WHERE oid::regclass = p_parent_table::regclass; IF v_hasoids IS TRUE THEN v_sql := v_sql || ' WITH (OIDS)'; END IF; EXECUTE v_sql; SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_parent_tablespace IS NOT NULL THEN EXECUTE 'ALTER TABLE '||v_partition_name||' SET TABLESPACE '||v_parent_tablespace; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' ADD CONSTRAINT '||v_tablename||'_partition_check CHECK ('||v_control||'>='||quote_literal(v_partition_timestamp_start)||' AND '||v_control||'<'||quote_literal(v_partition_timestamp_end)||')'; EXECUTE 'ALTER TABLE '||v_partition_name||' INHERIT '||p_parent_table; -- If custom time, set extra config options. IF v_type = 'time-custom' THEN INSERT INTO @extschema@.custom_time_partitions (parent_table, child_table, partition_range) VALUES ( p_parent_table, v_partition_name, tstzrange(v_partition_timestamp_start, v_partition_timestamp_end, '[)') ); END IF; FOR v_parent_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = p_parent_table GROUP BY grantee LOOP EXECUTE 'GRANT '||array_to_string(v_parent_grant.types, ',')||' ON '||v_partition_name||' TO '||v_parent_grant.grantee; SELECT array_agg(r) INTO v_revoke FROM (SELECT unnest(v_all) AS r EXCEPT SELECT unnest(v_parent_grant.types)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE '||array_to_string(v_revoke, ',')||' ON '||v_partition_name||' FROM '||v_parent_grant.grantee||' CASCADE'; END IF; v_grantees := array_append(v_grantees, v_parent_grant.grantee::text); END LOOP; -- Revoke all privileges from roles that have none on the parent IF v_grantees IS NOT NULL THEN SELECT array_agg(r) INTO v_revoke FROM ( SELECT DISTINCT grantee::text AS r FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = v_partition_name EXCEPT SELECT unnest(v_grantees)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE ALL ON '||v_partition_name||' FROM '||array_to_string(v_revoke, ','); END IF; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' OWNER TO '||v_parent_owner; IF v_inherit_fk THEN PERFORM @extschema@.apply_foreign_keys(quote_ident(v_parent_schema)||'.'||quote_ident(v_parent_tablename), v_partition_name); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); 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 FOR v_row IN SELECT sub_parent , sub_control , sub_type , sub_part_interval , sub_constraint_cols , sub_premake , sub_inherit_fk , sub_retention , sub_retention_schema , sub_retention_keep_table , sub_retention_keep_index , sub_use_run_maintenance , sub_jobmon FROM @extschema@.part_config_sub WHERE sub_parent = p_parent_table LOOP IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Subpartitioning '||v_partition_name); END IF; v_sql := format('SELECT @extschema@.create_parent( p_parent_table := %L , p_control := %L , p_type := %L , p_interval := %L , p_constraint_cols := %L , p_premake := %L , p_use_run_maintenance := %L , p_inherit_fk := %L , p_jobmon := %L )' , v_partition_name , v_row.sub_control , v_row.sub_type , v_row.sub_part_interval , v_row.sub_constraint_cols , v_row.sub_premake , v_row.sub_use_run_maintenance , v_row.sub_inherit_fk , v_row.sub_jobmon); EXECUTE v_sql; 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 WHERE parent_table = v_partition_name; END LOOP; -- end sub partitioning LOOP 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, 'Analyzing partition set: '||p_parent_table); END IF; EXECUTE 'ANALYZE '||p_parent_table; 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 v_step_id := add_step(v_job_id, 'No partitions created for partition set: '||p_parent_table); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; IF v_step_overflow_id IS NOT NULL THEN PERFORM fail_job(v_job_id); ELSE PERFORM close_job(v_job_id); END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_partition_created; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN CREATE TABLE: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; pg_partman-2.2.2/updates/pg_partman--1.8.3--1.8.4.sql000066400000000000000000000166301262146621700214450ustar00rootroot00000000000000-- 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/DEFERRED -- 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. /* * Apply foreign keys that exist on the given parent to the given child table */ CREATE OR REPLACE FUNCTION apply_foreign_keys(p_parent_table text, p_child_table text DEFAULT NULL, p_debug boolean DEFAULT false) RETURNS void LANGUAGE plpgsql AS $$ DECLARE v_job_id bigint; v_jobmon text; v_jobmon_schema text; v_old_search_path text; v_ref_schema text; v_ref_table text; v_row record; v_schemaname text; v_sql text; v_step_id bigint; v_tablename text; BEGIN SELECT jobmon INTO v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN APPLYING FOREIGN KEYS: '||p_parent_table); END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Checking if target child table exists'); END IF; SELECT schemaname, tablename INTO v_schemaname, v_tablename FROM pg_catalog.pg_tables WHERE schemaname||'.'||tablename = p_child_table; IF v_tablename IS NULL THEN IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'CRITICAL', 'Target child table ('||v_child_table||') does not exist.'); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION 'Target child table (%.%) does not exist.', v_schemaname, v_tablename; RETURN; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; FOR v_row IN SELECT n.nspname||'.'||cl.relname AS ref_table , '"'||string_agg(att.attname, '","')||'"' AS ref_column , '"'||string_agg(att2.attname, '","')||'"' AS child_column , keys.condeferred , keys.condeferrable , keys.confupdtype , keys.confdeltype , keys.confmatchtype FROM ( SELECT unnest(con.conkey) as ref , unnest(con.confkey) as child , con.confrelid , con.conrelid , con.condeferred , con.condeferrable , con.confupdtype , con.confdeltype , con.confmatchtype FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid JOIN pg_catalog.pg_constraint con ON c.oid = con.conrelid WHERE n.nspname ||'.'|| c.relname = p_parent_table AND con.contype = 'f' ORDER BY con.conkey ) keys JOIN pg_catalog.pg_class cl ON cl.oid = keys.confrelid JOIN pg_catalog.pg_namespace n ON cl.relnamespace = n.oid JOIN pg_catalog.pg_attribute att ON att.attrelid = keys.confrelid AND att.attnum = keys.child JOIN pg_catalog.pg_attribute att2 ON att2.attrelid = keys.conrelid AND att2.attnum = keys.ref GROUP BY n.nspname, cl.relname, keys.condeferred, keys.condeferrable, keys.confupdtype, keys.confdeltype, keys.confmatchtype LOOP SELECT schemaname, tablename INTO v_ref_schema, v_ref_table FROM pg_tables WHERE schemaname||'.'||tablename = v_row.ref_table; v_sql := format('ALTER TABLE %I.%I ADD FOREIGN KEY (%s) REFERENCES %I.%I (%s)', v_schemaname, v_tablename, v_row.child_column, v_ref_schema, v_ref_table, v_row.ref_column); CASE WHEN v_row.confmatchtype = 'f' THEN v_sql := v_sql || ' MATCH FULL '; WHEN (v_row.confmatchtype = 's' OR v_row.confmatchtype = 'u') THEN v_sql := v_sql || ' MATCH SIMPLE '; WHEN v_row.confmatchtype = 'p' THEN v_sql := v_sql || ' MATCH PARTIAL '; END CASE; CASE WHEN v_row.confupdtype = 'a' THEN v_sql := v_sql || ' ON UPDATE NO ACTION '; WHEN v_row.confupdtype = 'r' THEN v_sql := v_sql || ' ON UPDATE RESTRICT '; WHEN v_row.confupdtype = 'c' THEN v_sql := v_sql || ' ON UPDATE CASCADE '; WHEN v_row.confupdtype = 'n' THEN v_sql := v_sql || ' ON UPDATE SET NULL '; WHEN v_row.confupdtype = 'd' THEN v_sql := v_sql || ' ON UPDATE SET DEFAULT '; END CASE; CASE WHEN v_row.confdeltype = 'a' THEN v_sql := v_sql || ' ON DELETE NO ACTION '; WHEN v_row.confdeltype = 'r' THEN v_sql := v_sql || ' ON DELETE RESTRICT '; WHEN v_row.confdeltype = 'c' THEN v_sql := v_sql || ' ON DELETE CASCADE '; WHEN v_row.confdeltype = 'n' THEN v_sql := v_sql || ' ON DELETE SET NULL '; WHEN v_row.confdeltype = 'd' THEN v_sql := v_sql || ' ON DELETE SET DEFAULT '; END CASE; CASE WHEN v_row.condeferrable = true AND v_row.condeferred = true THEN v_sql := v_sql || ' DEFERRABLE INITIALLY DEFERRED '; WHEN v_row.condeferrable = false AND v_row.condeferred = false THEN v_sql := v_sql || ' NOT DEFERRABLE '; WHEN v_row.condeferrable = true AND v_row.condeferred = false THEN v_sql := v_sql || ' DEFERRABLE INITIALLY IMMEDIATE '; END CASE; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Applying FK: '||v_sql); END IF; EXECUTE v_sql; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'FK applied'); END IF; END LOOP; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN APPLYING FOREIGN KEYS: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; pg_partman-2.2.2/updates/pg_partman--1.8.4--1.8.5.sql000066400000000000000000001253401262146621700214460ustar00rootroot00000000000000-- 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). /* * Check if parent table is a subpartition of an already existing id based partition set managed by pg_partman * If so, limit what child tables can be created based on parent suffix */ CREATE FUNCTION check_subpartition_limits(p_parent_table text, p_type text, OUT sub_min text, OUT sub_max text) RETURNS record LANGUAGE plpgsql AS $$ DECLARE v_datetime_string text; v_id_position int; v_partition_interval interval; v_quarter text; v_sub_id_max bigint; v_sub_id_min bigint; v_sub_timestamp_max timestamp; v_sub_timestamp_min timestamp; v_time_position int; v_top_datetime_string text; v_top_interval text; v_top_parent text; v_top_type text; v_year text; BEGIN -- CTE query is done individually for each type (time, id) because it should return NULL if the top parent is not the same type in a subpartition set (id->time or time->id) IF p_type = 'id' THEN WITH top_oid AS ( SELECT i.inhparent AS top_parent_oid FROM pg_catalog.pg_class c JOIN pg_catalog.pg_inherits i ON c.oid = i.inhrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname||'.'||c.relname = p_parent_table ) SELECT n.nspname||'.'||c.relname, p.datetime_string, p.part_interval, p.type INTO v_top_parent, v_top_datetime_string, v_top_interval, v_top_type FROM pg_catalog.pg_class c JOIN top_oid t ON c.oid = t.top_parent_oid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace JOIN @extschema@.part_config p ON p.parent_table = n.nspname||'.'||c.relname WHERE c.oid = t.top_parent_oid AND p.type = 'id-static' OR p.type = 'id-dynamic'; IF v_top_parent IS NOT NULL THEN v_id_position := (length(p_parent_table) - position('p_' in reverse(p_parent_table))) + 2; v_sub_id_min := substring(p_parent_table from v_id_position)::bigint; v_sub_id_max := (v_sub_id_min + v_top_interval::bigint) - 1; sub_min := v_sub_id_min::text; sub_max := v_sub_id_max::text; END IF; ELSIF p_type = 'time' THEN WITH top_oid AS ( SELECT i.inhparent AS top_parent_oid FROM pg_catalog.pg_class c JOIN pg_catalog.pg_inherits i ON c.oid = i.inhrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname||'.'||c.relname = p_parent_table ) SELECT n.nspname||'.'||c.relname, p.datetime_string, p.part_interval, p.type INTO v_top_parent, v_top_datetime_string, v_top_interval, v_top_type FROM pg_catalog.pg_class c JOIN top_oid t ON c.oid = t.top_parent_oid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace JOIN @extschema@.part_config p ON p.parent_table = n.nspname||'.'||c.relname WHERE c.oid = t.top_parent_oid AND p.type = 'time-static' OR p.type = 'time-dynamic' OR p.type = 'time-custom'; IF v_top_parent IS NOT NULL THEN v_time_position := (length(p_parent_table) - position('p_' in reverse(p_parent_table))) + 2; IF v_top_interval::interval <> '3 months' OR (v_top_interval::interval = '3 months' AND v_top_type = 'time-custom') THEN v_sub_timestamp_min := to_timestamp(substring(p_parent_table from v_time_position), v_top_datetime_string); ELSE -- to_timestamp doesn't recognize 'Q' date string formater. Handle it v_year := split_part(substring(p_parent_table from v_time_position), 'q', 1); v_quarter := split_part(substring(p_parent_table from v_time_position), 'q', 2); CASE WHEN v_quarter = '1' THEN v_sub_timestamp_min := to_timestamp(v_year || '-01-01', 'YYYY-MM-DD'); WHEN v_quarter = '2' THEN v_sub_timestamp_min := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); WHEN v_quarter = '3' THEN v_sub_timestamp_min := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); WHEN v_quarter = '4' THEN v_sub_timestamp_min := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); END CASE; END IF; v_sub_timestamp_max = (v_sub_timestamp_min + v_top_interval::interval) - '1 sec'::interval; sub_min := v_sub_timestamp_min::text; sub_max := v_sub_timestamp_max::text; END IF; ELSE RAISE EXCEPTION 'Invalid type given as parameter to check_subpartition_limits()'; END IF; RETURN; END $$; /* * Function to manage pre-creation of the next partitions in a set. * 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_run_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. * Be aware that constraint exclusion may not work properly until an analyze on the partition set is run. */ CREATE OR REPLACE FUNCTION run_maintenance(p_parent_table text DEFAULT NULL, p_analyze boolean DEFAULT true, p_jobmon boolean DEFAULT true) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_create_count int := 0; v_current_partition text; v_current_partition_id bigint; v_current_partition_timestamp timestamp; v_datetime_string text; v_drop_count int := 0; v_id_position int; 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 timestamp; v_next_partition_id bigint; v_next_partition_timestamp timestamp; v_old_search_path text; v_premade_count int; v_quarter text; v_step_id bigint; v_step_overflow_id bigint; v_step_serial_id bigint; v_sub_id_max bigint; v_sub_id_min bigint; v_sub_parent text; v_sub_timestamp_max timestamp; v_sub_timestamp_min timestamp; v_row record; v_row_max_id record; v_row_sub record; v_tablename text; v_tables_list_sql text; v_time_position int; v_year text; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman run_maintenance')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'Partman maintenance already running.'; RETURN; END IF; IF p_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN RUN MAINTENANCE'); v_step_id := add_step(v_job_id, 'Running maintenance loop'); END IF; v_tables_list_sql := 'SELECT parent_table , type , part_interval , control , premake , datetime_string , undo_in_progress FROM @extschema@.part_config'; IF p_parent_table IS NULL THEN v_tables_list_sql := v_tables_list_sql || ' WHERE use_run_maintenance = true'; ELSE v_tables_list_sql := v_tables_list_sql || format(' WHERE parent_table = %L', p_parent_table); END IF; FOR v_row IN EXECUTE v_tables_list_sql LOOP CONTINUE WHEN v_row.undo_in_progress; SELECT show_partitions INTO v_last_partition FROM @extschema@.show_partitions(v_row.parent_table, 'DESC') LIMIT 1; IF v_row.type = 'time-static' OR v_row.type = 'time-dynamic' OR v_row.type = 'time-custom' THEN IF v_row.type = 'time-static' OR v_row.type = 'time-dynamic' THEN CASE WHEN v_row.part_interval::interval = '15 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0); WHEN v_row.part_interval::interval = '30 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0); WHEN v_row.part_interval::interval = '1 hour' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP); WHEN v_row.part_interval::interval = '1 day' THEN v_current_partition_timestamp := date_trunc('day', CURRENT_TIMESTAMP); WHEN v_row.part_interval::interval = '1 week' THEN v_current_partition_timestamp := date_trunc('week', CURRENT_TIMESTAMP); WHEN v_row.part_interval::interval = '1 month' THEN v_current_partition_timestamp := date_trunc('month', CURRENT_TIMESTAMP); WHEN v_row.part_interval::interval = '3 months' THEN v_current_partition_timestamp := date_trunc('quarter', CURRENT_TIMESTAMP); WHEN v_row.part_interval::interval = '1 year' THEN v_current_partition_timestamp := date_trunc('year', CURRENT_TIMESTAMP); END CASE; ELSIF v_row.type = 'time-custom' THEN SELECT child_table INTO v_current_partition FROM @extschema@.custom_time_partitions WHERE parent_table = v_row.parent_table AND partition_range @> CURRENT_TIMESTAMP; IF v_current_partition IS NULL THEN RAISE EXCEPTION 'Current time partition missing from custom_time_partitions config table for table % and timestamp %', CURRENT_TIMESTAMP, v_row.parent_table; END IF; v_time_position := (length(v_current_partition) - position('p_' in reverse(v_current_partition))) + 2; v_current_partition_timestamp := to_timestamp(substring(v_current_partition from v_time_position), v_row.datetime_string); END IF; -- Determine if this table is a child of a subpartition parent. If so, get limits of what child tables can be created based on parent suffix SELECT sub_min::timestamp, sub_max::timestamp INTO v_sub_timestamp_min, v_sub_timestamp_max FROM @extschema@.check_subpartition_limits(p_parent_table, 'time'); -- No need to run maintenance if it's outside the bounds of the top parent. IF v_sub_timestamp_min IS NOT NULL THEN IF v_current_partition_timestamp < v_sub_timestamp_min OR v_current_partition_timestamp > v_sub_timestamp_max THEN CONTINUE; END IF; END IF; v_time_position := (length(v_last_partition) - position('p_' in reverse(v_last_partition))) + 2; IF v_row.part_interval::interval <> '3 months' OR (v_row.part_interval::interval = '3 months' AND v_row.type = 'time-custom') THEN v_last_partition_timestamp := to_timestamp(substring(v_last_partition from v_time_position), v_row.datetime_string); ELSE -- to_timestamp doesn't recognize 'Q' date string formater. Handle it v_year := split_part(substring(v_last_partition from v_time_position), 'q', 1); v_quarter := split_part(substring(v_last_partition from v_time_position), 'q', 2); CASE WHEN v_quarter = '1' THEN v_last_partition_timestamp := to_timestamp(v_year || '-01-01', 'YYYY-MM-DD'); WHEN v_quarter = '2' THEN v_last_partition_timestamp := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); WHEN v_quarter = '3' THEN v_last_partition_timestamp := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); WHEN v_quarter = '4' THEN v_last_partition_timestamp := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); END CASE; END IF; -- Check and see how many premade partitions there are. v_premade_count = round(EXTRACT('epoch' FROM age(v_last_partition_timestamp, v_current_partition_timestamp)) / EXTRACT('epoch' FROM v_row.part_interval::interval)); v_next_partition_timestamp := v_last_partition_timestamp; -- 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 BEGIN v_next_partition_timestamp := v_next_partition_timestamp + v_row.part_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 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', 'Child partition creation skippd for parent table '||v_partition_time); END IF; RAISE WARNING 'Attempted partition time interval is outside PostgreSQL''s supported time range. Child partition creation skipped for parent table %', v_row.parent_table; CONTINUE; END; v_last_partition_created := @extschema@.create_partition_time(v_row.parent_table, ARRAY[v_next_partition_timestamp], p_analyze); v_create_count := v_create_count + 1; IF v_row.type = 'time-static' AND v_last_partition_created THEN PERFORM @extschema@.create_function_time(v_row.parent_table); END IF; -- Manage additonal constraints if set PERFORM @extschema@.apply_constraints(v_row.parent_table); -- Can be negative when subpartitioning and there are parent partitions in the past compared to current timestamp value. -- abs() prevents run_maintenence from running on those old parent tables v_premade_count = round(EXTRACT('epoch' FROM age(v_next_partition_timestamp, v_current_partition_timestamp)) / EXTRACT('epoch' FROM v_row.part_interval::interval)); END LOOP; ELSIF v_row.type = 'id-static' OR v_row.type ='id-dynamic' THEN -- 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 show_partitions FROM @extschema@.show_partitions(v_row.parent_table, 'DESC') LOOP EXECUTE 'SELECT '||v_row.control||' - ('||v_row.control||' % '||v_row.part_interval::int||') FROM '||v_row_max_id.show_partitions||' WHERE '||v_row.control||' = (SELECT max('||v_row.control||') FROM '||v_row_max_id.show_partitions||')' INTO v_current_partition_id; IF v_current_partition_id IS NOT NULL THEN EXIT; END IF; END LOOP; -- Determine if this table is a child of a subpartition parent. If so, get limits to see if run_maintenance even needs to run for it. SELECT sub_min::bigint, sub_max::bigint INTO v_sub_id_min, v_sub_id_max FROM @extschema@.check_subpartition_limits(p_parent_table, 'id'); -- No need to run maintenance if it's outside the bounds of the top parent. IF v_sub_id_min IS NOT NULL THEN IF v_current_partition_id < v_sub_id_min OR v_current_partition_id > v_sub_id_max THEN CONTINUE; END IF; END IF; v_id_position := (length(v_last_partition) - position('p_' in reverse(v_last_partition))) + 2; v_last_partition_id = substring(v_last_partition from v_id_position)::bigint; v_next_partition_id := v_last_partition_id + v_row.part_interval::bigint; WHILE ((v_next_partition_id - v_current_partition_id) / v_row.part_interval::bigint) <= v_row.premake LOOP v_last_partition_created := @extschema@.create_partition_id(v_row.parent_table, ARRAY[v_next_partition_id], p_analyze); IF v_last_partition_created THEN PERFORM @extschema@.create_function_id(v_row.parent_table); PERFORM @extschema@.apply_constraints(v_row.parent_table); END IF; v_next_partition_id := v_next_partition_id + v_row.part_interval::bigint; END LOOP; END IF; -- end main IF check for time or id END LOOP; -- end of creation loop -- Manage dropping old partitions if retention option is set FOR v_row IN SELECT parent_table FROM @extschema@.part_config WHERE retention IS NOT NULL AND undo_in_progress = false AND (type = 'time-static' OR type = 'time-dynamic' OR type = 'time-custom') LOOP IF p_parent_table IS NULL THEN v_drop_count := v_drop_count + @extschema@.drop_partition_time(v_row.parent_table); ELSE -- Only run retention on table given in parameter IF p_parent_table <> v_row.parent_table THEN CONTINUE; ELSE v_drop_count := v_drop_count + @extschema@.drop_partition_time(v_row.parent_table); END IF; END IF; END LOOP; FOR v_row IN SELECT parent_table FROM @extschema@.part_config WHERE retention IS NOT NULL AND undo_in_progress = false AND (type = 'id-static' OR type = 'id-dynamic') LOOP IF p_parent_table IS NULL THEN v_drop_count := v_drop_count + @extschema@.drop_partition_id(v_row.parent_table); ELSE -- Only run retention on table given in parameter IF p_parent_table <> v_row.parent_table THEN CONTINUE; ELSE v_drop_count := v_drop_count + @extschema@.drop_partition_id(v_row.parent_table); END IF; END IF; END LOOP; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Partition maintenance finished. '||v_create_count||' partitons made. '||v_drop_count||' partitions dropped.'); IF v_step_overflow_id IS NOT NULL OR v_step_serial_id IS NOT NULL THEN PERFORM fail_job(v_job_id); ELSE PERFORM close_job(v_job_id); END IF; EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN RUN MAINTENANCE'')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to create a child table in a time-based partition set */ CREATE OR REPLACE FUNCTION create_partition_time (p_parent_table text, p_partition_times timestamp[], p_analyze boolean DEFAULT true) RETURNS boolean LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_all text[] := ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER']; v_analyze boolean := FALSE; v_control text; v_grantees text[]; v_hasoids boolean; v_inherit_fk boolean; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_parent_grant record; v_parent_owner text; v_parent_schema text; v_parent_tablename text; v_partition_created boolean := false; v_partition_name text; v_partition_suffix text; v_parent_tablespace text; v_part_interval interval; v_partition_timestamp_end timestamp; v_partition_timestamp_start timestamp; v_quarter text; v_revoke text[]; v_row record; v_sql text; v_step_id bigint; v_step_overflow_id bigint; v_sub_timestamp_max timestamp; v_sub_timestamp_min timestamp; v_tablename text; v_trunc_value text; v_time timestamp; v_type text; v_unlogged char; v_year text; BEGIN SELECT type , control , part_interval , inherit_fk , jobmon , datetime_string INTO v_type , v_control , v_part_interval , v_inherit_fk , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'time-static' OR type = 'time-dynamic' OR type = 'time-custom'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; -- Determine if this table is a child of a subpartition parent. If so, get limits of what child tables can be created based on parent suffix SELECT sub_min::timestamp, sub_max::timestamp INTO v_sub_timestamp_min, v_sub_timestamp_max FROM @extschema@.check_subpartition_limits(p_parent_table, 'time'); SELECT tableowner, schemaname, tablename, tablespace INTO v_parent_owner, v_parent_schema, v_parent_tablename, v_parent_tablespace FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); END IF; FOREACH v_time IN ARRAY p_partition_times LOOP v_partition_timestamp_start := v_time; BEGIN v_partition_timestamp_end := v_time + v_part_interval; EXCEPTION WHEN datetime_field_overflow THEN 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'); CONTINUE; END; -- 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 CONTINUE; END IF; END IF; -- This suffix generation code is in partition_data_time() as well v_partition_suffix := to_char(v_time, 'YYYY'); IF v_part_interval < '1 year' AND v_part_interval <> '1 week' THEN v_partition_suffix := v_partition_suffix ||'_'|| to_char(v_time, 'MM'); IF v_part_interval < '1 month' AND v_part_interval <> '1 week' THEN v_partition_suffix := v_partition_suffix ||'_'|| to_char(v_time, 'DD'); IF v_part_interval < '1 day' THEN v_partition_suffix := v_partition_suffix || '_' || to_char(v_time, 'HH24MI'); IF v_part_interval < '1 minute' THEN v_partition_suffix := v_partition_suffix || to_char(v_time, 'SS'); END IF; -- end < minute IF END IF; -- end < day IF END IF; -- end < month IF END IF; -- end < year IF IF v_part_interval = '1 week' THEN v_partition_suffix := to_char(v_time, 'IYYY') || 'w' || to_char(v_time, 'IW'); END IF; -- "Q" is ignored in to_timestamp, so handle special case IF v_part_interval = '3 months' AND (v_type = 'time-static' OR v_type = 'time-dynamic') THEN v_year := to_char(v_time, 'YYYY'); v_quarter := to_char(v_time, 'Q'); v_partition_suffix := v_year || 'q' || v_quarter; END IF; v_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_partition_suffix, TRUE); SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_tablename IS NOT NULL 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, 'Creating new partition '||v_partition_name||' with interval from '||v_partition_timestamp_start||' to '||(v_partition_timestamp_end-'1sec'::interval)); END IF; SELECT relpersistence INTO v_unlogged FROM pg_catalog.pg_class WHERE oid::regclass = p_parent_table::regclass; v_sql := 'CREATE'; IF v_unlogged = 'u' THEN v_sql := v_sql || ' UNLOGGED'; END IF; v_sql := v_sql || ' TABLE '||v_partition_name||' (LIKE '||p_parent_table||' INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING STORAGE INCLUDING COMMENTS)'; SELECT relhasoids INTO v_hasoids FROM pg_catalog.pg_class WHERE oid::regclass = p_parent_table::regclass; IF v_hasoids IS TRUE THEN v_sql := v_sql || ' WITH (OIDS)'; END IF; EXECUTE v_sql; SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_parent_tablespace IS NOT NULL THEN EXECUTE 'ALTER TABLE '||v_partition_name||' SET TABLESPACE '||v_parent_tablespace; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' ADD CONSTRAINT '||v_tablename||'_partition_check CHECK ('||v_control||'>='||quote_literal(v_partition_timestamp_start)||' AND '||v_control||'<'||quote_literal(v_partition_timestamp_end)||')'; EXECUTE 'ALTER TABLE '||v_partition_name||' INHERIT '||p_parent_table; -- If custom time, set extra config options. IF v_type = 'time-custom' THEN INSERT INTO @extschema@.custom_time_partitions (parent_table, child_table, partition_range) VALUES ( p_parent_table, v_partition_name, tstzrange(v_partition_timestamp_start, v_partition_timestamp_end, '[)') ); END IF; FOR v_parent_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = p_parent_table GROUP BY grantee LOOP EXECUTE 'GRANT '||array_to_string(v_parent_grant.types, ',')||' ON '||v_partition_name||' TO '||v_parent_grant.grantee; SELECT array_agg(r) INTO v_revoke FROM (SELECT unnest(v_all) AS r EXCEPT SELECT unnest(v_parent_grant.types)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE '||array_to_string(v_revoke, ',')||' ON '||v_partition_name||' FROM '||v_parent_grant.grantee||' CASCADE'; END IF; v_grantees := array_append(v_grantees, v_parent_grant.grantee::text); END LOOP; -- Revoke all privileges from roles that have none on the parent IF v_grantees IS NOT NULL THEN SELECT array_agg(r) INTO v_revoke FROM ( SELECT DISTINCT grantee::text AS r FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = v_partition_name EXCEPT SELECT unnest(v_grantees)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE ALL ON '||v_partition_name||' FROM '||array_to_string(v_revoke, ','); END IF; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' OWNER TO '||v_parent_owner; IF v_inherit_fk THEN PERFORM @extschema@.apply_foreign_keys(quote_ident(v_parent_schema)||'.'||quote_ident(v_parent_tablename), v_partition_name); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); 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 FOR v_row IN SELECT sub_parent , sub_control , sub_type , sub_part_interval , sub_constraint_cols , sub_premake , sub_inherit_fk , sub_retention , sub_retention_schema , sub_retention_keep_table , sub_retention_keep_index , sub_use_run_maintenance , sub_jobmon FROM @extschema@.part_config_sub WHERE sub_parent = p_parent_table LOOP IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Subpartitioning '||v_partition_name); END IF; v_sql := format('SELECT @extschema@.create_parent( p_parent_table := %L , p_control := %L , p_type := %L , p_interval := %L , p_constraint_cols := %L , p_premake := %L , p_use_run_maintenance := %L , p_inherit_fk := %L , p_jobmon := %L )' , v_partition_name , v_row.sub_control , v_row.sub_type , v_row.sub_part_interval , v_row.sub_constraint_cols , v_row.sub_premake , v_row.sub_use_run_maintenance , v_row.sub_inherit_fk , v_row.sub_jobmon); EXECUTE v_sql; 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 WHERE parent_table = v_partition_name; END LOOP; -- end sub partitioning LOOP 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, 'Analyzing partition set: '||p_parent_table); END IF; EXECUTE 'ANALYZE '||p_parent_table; 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 v_step_id := add_step(v_job_id, 'No partitions created for partition set: '||p_parent_table); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; IF v_step_overflow_id IS NOT NULL THEN PERFORM fail_job(v_job_id); ELSE PERFORM close_job(v_job_id); END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_partition_created; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN CREATE TABLE: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to create id partitions */ CREATE OR REPLACE FUNCTION create_partition_id(p_parent_table text, p_partition_ids bigint[], p_analyze boolean DEFAULT true) RETURNS boolean LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_all text[] := ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER']; v_analyze boolean := FALSE; v_control text; v_grantees text[]; v_hasoids boolean; v_id bigint; v_inherit_fk boolean; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_parent_grant record; v_parent_owner text; v_parent_schema text; v_parent_tablename text; v_parent_tablespace text; v_part_interval bigint; v_partition_created boolean := false; v_partition_name text; v_revoke text[]; v_row record; v_sql text; v_step_id bigint; v_sub_id_max bigint; v_sub_id_min bigint; v_tablename text; v_unlogged char; BEGIN SELECT control , part_interval , inherit_fk , jobmon INTO v_control , v_part_interval , v_inherit_fk , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (type = 'id-static' OR type = 'id-dynamic'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; -- Determine if this table is a child of a subpartition parent. If so, get limits of what child tables can be created based on parent suffix SELECT sub_min::bigint, sub_max::bigint INTO v_sub_id_min, v_sub_id_max FROM @extschema@.check_subpartition_limits(p_parent_table, 'id'); SELECT tableowner, schemaname, tablename, tablespace INTO v_parent_owner, v_parent_schema, v_parent_tablename, v_parent_tablespace FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); 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. 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; END IF; END IF; v_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_id::text, TRUE); -- If child table already exists, skip creation SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_tablename IS NOT NULL 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, 'Creating new partition '||v_partition_name||' with interval from '||v_id||' to '||(v_id + v_part_interval)-1); END IF; SELECT relpersistence INTO v_unlogged FROM pg_catalog.pg_class WHERE oid::regclass = p_parent_table::regclass; v_sql := 'CREATE'; IF v_unlogged = 'u' THEN v_sql := v_sql || ' UNLOGGED'; END IF; v_sql := v_sql || ' TABLE '||v_partition_name||' (LIKE '||p_parent_table||' INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING STORAGE INCLUDING COMMENTS)'; SELECT relhasoids INTO v_hasoids FROM pg_catalog.pg_class WHERE oid::regclass = p_parent_table::regclass; IF v_hasoids IS TRUE THEN v_sql := v_sql || ' WITH (OIDS)'; END IF; EXECUTE v_sql; SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_parent_tablespace IS NOT NULL THEN EXECUTE 'ALTER TABLE '||v_partition_name||' SET TABLESPACE '||v_parent_tablespace; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' ADD CONSTRAINT '||v_tablename||'_partition_check CHECK ('||v_control||'>='||quote_literal(v_id)||' AND '||v_control||'<'||quote_literal(v_id + v_part_interval)||')'; EXECUTE 'ALTER TABLE '||v_partition_name||' INHERIT '||p_parent_table; FOR v_parent_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = p_parent_table GROUP BY grantee LOOP EXECUTE 'GRANT '||array_to_string(v_parent_grant.types, ',')||' ON '||v_partition_name||' TO '||v_parent_grant.grantee; SELECT array_agg(r) INTO v_revoke FROM (SELECT unnest(v_all) AS r EXCEPT SELECT unnest(v_parent_grant.types)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE '||array_to_string(v_revoke, ',')||' ON '||v_partition_name||' FROM '||v_parent_grant.grantee||' CASCADE'; END IF; v_grantees := array_append(v_grantees, v_parent_grant.grantee::text); END LOOP; -- Revoke all privileges from roles that have none on the parent IF v_grantees IS NOT NULL THEN SELECT array_agg(r) INTO v_revoke FROM ( SELECT DISTINCT grantee::text AS r FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = v_partition_name EXCEPT SELECT unnest(v_grantees)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE ALL ON '||v_partition_name||' FROM '||array_to_string(v_revoke, ','); END IF; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' OWNER TO '||v_parent_owner; IF v_inherit_fk THEN PERFORM @extschema@.apply_foreign_keys(quote_ident(v_parent_schema)||'.'||quote_ident(v_parent_tablename), v_partition_name); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); 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 FOR v_row IN SELECT sub_parent , sub_control , sub_type , sub_part_interval , sub_constraint_cols , sub_premake , sub_inherit_fk , sub_retention , sub_retention_schema , sub_retention_keep_table , sub_retention_keep_index , sub_use_run_maintenance , sub_jobmon FROM @extschema@.part_config_sub WHERE sub_parent = p_parent_table LOOP IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Subpartitioning '||v_partition_name); END IF; v_sql := format('SELECT @extschema@.create_parent( p_parent_table := %L , p_control := %L , p_type := %L , p_interval := %L , p_constraint_cols := %L , p_premake := %L , p_use_run_maintenance := %L , p_inherit_fk := %L , p_jobmon := %L )' , v_partition_name , v_row.sub_control , v_row.sub_type , v_row.sub_part_interval , v_row.sub_constraint_cols , v_row.sub_premake , v_row.sub_use_run_maintenance , v_row.sub_inherit_fk , v_row.sub_jobmon); EXECUTE v_sql; 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 WHERE parent_table = v_partition_name; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END LOOP; -- end sub partitioning LOOP 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, 'Analyzing partition set: '||p_parent_table); END IF; EXECUTE 'ANALYZE '||p_parent_table; 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 v_step_id := add_step(v_job_id, 'No partitions created for partition set: '||p_parent_table); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; PERFORM close_job(v_job_id); END IF; IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_partition_created; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN CREATE TABLE: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; pg_partman-2.2.2/updates/pg_partman--1.8.5--1.8.6.sql000066400000000000000000000366041262146621700214540ustar00rootroot00000000000000-- 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 partiton sets. This bug was introduced with the fixed included in v1.8.5. /* * Function to manage pre-creation of the next partitions in a set. * 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_run_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. * Be aware that constraint exclusion may not work properly until an analyze on the partition set is run. */ CREATE OR REPLACE FUNCTION run_maintenance(p_parent_table text DEFAULT NULL, p_analyze boolean DEFAULT true, p_jobmon boolean DEFAULT true) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_create_count int := 0; v_current_partition text; v_current_partition_id bigint; v_current_partition_timestamp timestamp; v_datetime_string text; v_drop_count int := 0; v_id_position int; 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 timestamp; v_next_partition_id bigint; v_next_partition_timestamp timestamp; v_old_search_path text; v_premade_count int; v_quarter text; v_step_id bigint; v_step_overflow_id bigint; v_step_serial_id bigint; v_sub_id_max bigint; v_sub_id_min bigint; v_sub_parent text; v_sub_timestamp_max timestamp; v_sub_timestamp_min timestamp; v_row record; v_row_max_id record; v_row_sub record; v_tablename text; v_tables_list_sql text; v_time_position int; v_year text; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman run_maintenance')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'Partman maintenance already running.'; RETURN; END IF; IF p_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN RUN MAINTENANCE'); v_step_id := add_step(v_job_id, 'Running maintenance loop'); END IF; v_tables_list_sql := 'SELECT parent_table , type , part_interval , control , premake , datetime_string , undo_in_progress FROM @extschema@.part_config'; IF p_parent_table IS NULL THEN v_tables_list_sql := v_tables_list_sql || ' WHERE use_run_maintenance = true'; ELSE v_tables_list_sql := v_tables_list_sql || format(' WHERE parent_table = %L', p_parent_table); END IF; FOR v_row IN EXECUTE v_tables_list_sql LOOP CONTINUE WHEN v_row.undo_in_progress; SELECT show_partitions INTO v_last_partition FROM @extschema@.show_partitions(v_row.parent_table, 'DESC') LIMIT 1; IF v_row.type = 'time-static' OR v_row.type = 'time-dynamic' OR v_row.type = 'time-custom' THEN IF v_row.type = 'time-static' OR v_row.type = 'time-dynamic' THEN CASE WHEN v_row.part_interval::interval = '15 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0); WHEN v_row.part_interval::interval = '30 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0); WHEN v_row.part_interval::interval = '1 hour' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP); WHEN v_row.part_interval::interval = '1 day' THEN v_current_partition_timestamp := date_trunc('day', CURRENT_TIMESTAMP); WHEN v_row.part_interval::interval = '1 week' THEN v_current_partition_timestamp := date_trunc('week', CURRENT_TIMESTAMP); WHEN v_row.part_interval::interval = '1 month' THEN v_current_partition_timestamp := date_trunc('month', CURRENT_TIMESTAMP); WHEN v_row.part_interval::interval = '3 months' THEN v_current_partition_timestamp := date_trunc('quarter', CURRENT_TIMESTAMP); WHEN v_row.part_interval::interval = '1 year' THEN v_current_partition_timestamp := date_trunc('year', CURRENT_TIMESTAMP); END CASE; ELSIF v_row.type = 'time-custom' THEN SELECT child_table INTO v_current_partition FROM @extschema@.custom_time_partitions WHERE parent_table = v_row.parent_table AND partition_range @> CURRENT_TIMESTAMP; IF v_current_partition IS NULL THEN RAISE EXCEPTION 'Current time partition missing from custom_time_partitions config table for table % and timestamp %', CURRENT_TIMESTAMP, v_row.parent_table; END IF; v_time_position := (length(v_current_partition) - position('p_' in reverse(v_current_partition))) + 2; v_current_partition_timestamp := to_timestamp(substring(v_current_partition from v_time_position), v_row.datetime_string); END IF; -- Determine if this table is a child of a subpartition parent. If so, get limits of what child tables can be created based on parent suffix SELECT sub_min::timestamp, sub_max::timestamp INTO v_sub_timestamp_min, v_sub_timestamp_max FROM @extschema@.check_subpartition_limits(v_row.parent_table, 'time'); -- No need to run maintenance if it's outside the bounds of the top parent. IF v_sub_timestamp_min IS NOT NULL THEN IF v_current_partition_timestamp < v_sub_timestamp_min OR v_current_partition_timestamp > v_sub_timestamp_max THEN CONTINUE; END IF; END IF; v_time_position := (length(v_last_partition) - position('p_' in reverse(v_last_partition))) + 2; IF v_row.part_interval::interval <> '3 months' OR (v_row.part_interval::interval = '3 months' AND v_row.type = 'time-custom') THEN v_last_partition_timestamp := to_timestamp(substring(v_last_partition from v_time_position), v_row.datetime_string); ELSE -- to_timestamp doesn't recognize 'Q' date string formater. Handle it v_year := split_part(substring(v_last_partition from v_time_position), 'q', 1); v_quarter := split_part(substring(v_last_partition from v_time_position), 'q', 2); CASE WHEN v_quarter = '1' THEN v_last_partition_timestamp := to_timestamp(v_year || '-01-01', 'YYYY-MM-DD'); WHEN v_quarter = '2' THEN v_last_partition_timestamp := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); WHEN v_quarter = '3' THEN v_last_partition_timestamp := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); WHEN v_quarter = '4' THEN v_last_partition_timestamp := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); END CASE; END IF; -- Check and see how many premade partitions there are. v_premade_count = round(EXTRACT('epoch' FROM age(v_last_partition_timestamp, v_current_partition_timestamp)) / EXTRACT('epoch' FROM v_row.part_interval::interval)); v_next_partition_timestamp := v_last_partition_timestamp; -- 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 BEGIN v_next_partition_timestamp := v_next_partition_timestamp + v_row.part_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 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', 'Child partition creation skippd for parent table '||v_partition_time); END IF; RAISE WARNING 'Attempted partition time interval is outside PostgreSQL''s supported time range. Child partition creation skipped for parent table %', v_row.parent_table; CONTINUE; END; v_last_partition_created := @extschema@.create_partition_time(v_row.parent_table, ARRAY[v_next_partition_timestamp], p_analyze); v_create_count := v_create_count + 1; IF v_row.type = 'time-static' AND v_last_partition_created THEN PERFORM @extschema@.create_function_time(v_row.parent_table); END IF; -- Manage additonal constraints if set PERFORM @extschema@.apply_constraints(v_row.parent_table); -- Can be negative when subpartitioning and there are parent partitions in the past compared to current timestamp value. -- abs() prevents run_maintenence from running on those old parent tables v_premade_count = round(EXTRACT('epoch' FROM age(v_next_partition_timestamp, v_current_partition_timestamp)) / EXTRACT('epoch' FROM v_row.part_interval::interval)); END LOOP; ELSIF v_row.type = 'id-static' OR v_row.type ='id-dynamic' THEN -- 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 show_partitions FROM @extschema@.show_partitions(v_row.parent_table, 'DESC') LOOP EXECUTE 'SELECT '||v_row.control||' - ('||v_row.control||' % '||v_row.part_interval::int||') FROM '||v_row_max_id.show_partitions||' WHERE '||v_row.control||' = (SELECT max('||v_row.control||') FROM '||v_row_max_id.show_partitions||')' INTO v_current_partition_id; IF v_current_partition_id IS NOT NULL THEN EXIT; END IF; END LOOP; -- Determine if this table is a child of a subpartition parent. If so, get limits to see if run_maintenance even needs to run for it. SELECT sub_min::bigint, sub_max::bigint INTO v_sub_id_min, v_sub_id_max FROM @extschema@.check_subpartition_limits(v_row.parent_table, 'id'); -- No need to run maintenance if it's outside the bounds of the top parent. IF v_sub_id_min IS NOT NULL THEN IF v_current_partition_id < v_sub_id_min OR v_current_partition_id > v_sub_id_max THEN CONTINUE; END IF; END IF; v_id_position := (length(v_last_partition) - position('p_' in reverse(v_last_partition))) + 2; v_last_partition_id = substring(v_last_partition from v_id_position)::bigint; v_next_partition_id := v_last_partition_id + v_row.part_interval::bigint; WHILE ((v_next_partition_id - v_current_partition_id) / v_row.part_interval::bigint) <= v_row.premake LOOP v_last_partition_created := @extschema@.create_partition_id(v_row.parent_table, ARRAY[v_next_partition_id], p_analyze); IF v_last_partition_created THEN PERFORM @extschema@.create_function_id(v_row.parent_table); PERFORM @extschema@.apply_constraints(v_row.parent_table); END IF; v_next_partition_id := v_next_partition_id + v_row.part_interval::bigint; END LOOP; END IF; -- end main IF check for time or id END LOOP; -- end of creation loop -- Manage dropping old partitions if retention option is set FOR v_row IN SELECT parent_table FROM @extschema@.part_config WHERE retention IS NOT NULL AND undo_in_progress = false AND (type = 'time-static' OR type = 'time-dynamic' OR type = 'time-custom') LOOP IF p_parent_table IS NULL THEN v_drop_count := v_drop_count + @extschema@.drop_partition_time(v_row.parent_table); ELSE -- Only run retention on table given in parameter IF p_parent_table <> v_row.parent_table THEN CONTINUE; ELSE v_drop_count := v_drop_count + @extschema@.drop_partition_time(v_row.parent_table); END IF; END IF; END LOOP; FOR v_row IN SELECT parent_table FROM @extschema@.part_config WHERE retention IS NOT NULL AND undo_in_progress = false AND (type = 'id-static' OR type = 'id-dynamic') LOOP IF p_parent_table IS NULL THEN v_drop_count := v_drop_count + @extschema@.drop_partition_id(v_row.parent_table); ELSE -- Only run retention on table given in parameter IF p_parent_table <> v_row.parent_table THEN CONTINUE; ELSE v_drop_count := v_drop_count + @extschema@.drop_partition_id(v_row.parent_table); END IF; END IF; END LOOP; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Partition maintenance finished. '||v_create_count||' partitons made. '||v_drop_count||' partitions dropped.'); IF v_step_overflow_id IS NOT NULL OR v_step_serial_id IS NOT NULL THEN PERFORM fail_job(v_job_id); ELSE PERFORM close_job(v_job_id); END IF; EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN RUN MAINTENANCE'')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; pg_partman-2.2.2/updates/pg_partman--1.8.6--1.8.7.sql000066400000000000000000000040231262146621700214440ustar00rootroot00000000000000-- 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 below 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. /*********************************************************** -- Begin code to fix partition functions -- You may need to adjust the setting of the search_path below to point the following commands to the schema that partman is installed to SELECT set_config('search_path','partman, "$user", public',false); CREATE TEMP TABLE partman_trigfix_temp (statement text); INSERT INTO partman_trigfix_temp SELECT format('SELECT create_function_id(%L);', parent_table) FROM part_config WHERE type like 'id%'; INSERT INTO partman_trigfix_temp SELECT format('SELECT create_function_time(%L);', parent_table) FROM part_config WHERE type like 'time%'; DO $$ DECLARE v_row record; BEGIN FOR v_row IN SELECT statement FROM partman_trigfix_temp LOOP IF v_row.statement IS NOT NULL THEN EXECUTE v_row.statement; END IF; END LOOP; END $$; DROP TABLE IF EXISTS partman_trigfix_temp; -- End code to fix partiton functions ************************************************************/ pg_partman-2.2.2/updates/pg_partman--1.8.7--1.8.8.sql000066400000000000000000000474201262146621700214560ustar00rootroot00000000000000-- 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 allow 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 easier to manage code (Github Issue #64). -- Recreate the trigger function when dropping tables as part of retention to ensure those tables aren't used in the trigger condition (Github Issue #62). CREATE TEMP TABLE partman_preserve_privs_temp (statement text); INSERT INTO partman_preserve_privs_temp SELECT 'GRANT EXECUTE ON FUNCTION @extschema@.apply_foreign_keys(text, text, boolean) TO '||array_to_string(array_agg(grantee::text), ',')||';' FROM information_schema.routine_privileges WHERE routine_schema = '@extschema@' AND routine_name = 'apply_foreign_keys'; DROP FUNCTION @extschema@.apply_foreign_keys(text, text, boolean); /* * Apply foreign keys that exist on the given parent to the given child table */ CREATE OR REPLACE FUNCTION apply_foreign_keys(p_parent_table text, p_child_table text, p_debug boolean DEFAULT false) RETURNS void LANGUAGE plpgsql AS $$ DECLARE v_job_id bigint; v_jobmon text; v_jobmon_schema text; v_old_search_path text; v_ref_schema text; v_ref_table text; v_row record; v_schemaname text; v_sql text; v_step_id bigint; v_tablename text; BEGIN SELECT jobmon INTO v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN APPLYING FOREIGN KEYS: '||p_parent_table); END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Checking if target child table exists'); END IF; SELECT schemaname, tablename INTO v_schemaname, v_tablename FROM pg_catalog.pg_tables WHERE schemaname||'.'||tablename = p_child_table; IF v_tablename IS NULL THEN IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'CRITICAL', 'Target child table ('||p_child_table||') does not exist.'); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION 'Target child table (%.%) does not exist.', v_schemaname, v_tablename; RETURN; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; FOR v_row IN SELECT pg_get_constraintdef(oid) AS constraint_def FROM pg_catalog.pg_constraint WHERE conrelid = p_parent_table::regclass AND contype = 'f' LOOP v_sql := format('ALTER TABLE %I.%I ADD %s' , v_schemaname , v_tablename , v_row.constraint_def); EXECUTE v_sql; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'FK applied'); END IF; END LOOP; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN APPLYING FOREIGN KEYS: '||p_parent_table||''')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; /* * Function to manage pre-creation of the next partitions in a set. * 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_run_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. * Be aware that constraint exclusion may not work properly until an analyze on the partition set is run. */ CREATE OR REPLACE FUNCTION run_maintenance(p_parent_table text DEFAULT NULL, p_analyze boolean DEFAULT true, p_jobmon boolean DEFAULT true) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_adv_lock boolean; v_create_count int := 0; v_current_partition text; v_current_partition_id bigint; v_current_partition_timestamp timestamp; v_datetime_string text; v_drop_count int := 0; v_id_position int; 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 timestamp; v_next_partition_id bigint; v_next_partition_timestamp timestamp; v_old_search_path text; v_premade_count int; v_quarter text; v_step_id bigint; v_step_overflow_id bigint; v_step_serial_id bigint; v_sub_id_max bigint; v_sub_id_min bigint; v_sub_parent text; v_sub_timestamp_max timestamp; v_sub_timestamp_min timestamp; v_row record; v_row_max_id record; v_row_sub record; v_tablename text; v_tables_list_sql text; v_time_position int; v_year text; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman run_maintenance')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'Partman maintenance already running.'; RETURN; END IF; IF p_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN RUN MAINTENANCE'); v_step_id := add_step(v_job_id, 'Running maintenance loop'); END IF; v_tables_list_sql := 'SELECT parent_table , type , part_interval , control , premake , datetime_string , undo_in_progress FROM @extschema@.part_config'; IF p_parent_table IS NULL THEN v_tables_list_sql := v_tables_list_sql || ' WHERE use_run_maintenance = true'; ELSE v_tables_list_sql := v_tables_list_sql || format(' WHERE parent_table = %L', p_parent_table); END IF; FOR v_row IN EXECUTE v_tables_list_sql LOOP CONTINUE WHEN v_row.undo_in_progress; SELECT show_partitions INTO v_last_partition FROM @extschema@.show_partitions(v_row.parent_table, 'DESC') LIMIT 1; IF v_row.type = 'time-static' OR v_row.type = 'time-dynamic' OR v_row.type = 'time-custom' THEN IF v_row.type = 'time-static' OR v_row.type = 'time-dynamic' THEN CASE WHEN v_row.part_interval::interval = '15 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0); WHEN v_row.part_interval::interval = '30 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0); WHEN v_row.part_interval::interval = '1 hour' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP); WHEN v_row.part_interval::interval = '1 day' THEN v_current_partition_timestamp := date_trunc('day', CURRENT_TIMESTAMP); WHEN v_row.part_interval::interval = '1 week' THEN v_current_partition_timestamp := date_trunc('week', CURRENT_TIMESTAMP); WHEN v_row.part_interval::interval = '1 month' THEN v_current_partition_timestamp := date_trunc('month', CURRENT_TIMESTAMP); WHEN v_row.part_interval::interval = '3 months' THEN v_current_partition_timestamp := date_trunc('quarter', CURRENT_TIMESTAMP); WHEN v_row.part_interval::interval = '1 year' THEN v_current_partition_timestamp := date_trunc('year', CURRENT_TIMESTAMP); END CASE; ELSIF v_row.type = 'time-custom' THEN SELECT child_table INTO v_current_partition FROM @extschema@.custom_time_partitions WHERE parent_table = v_row.parent_table AND partition_range @> CURRENT_TIMESTAMP; IF v_current_partition IS NULL THEN RAISE EXCEPTION 'Current time partition missing from custom_time_partitions config table for table % and timestamp %', CURRENT_TIMESTAMP, v_row.parent_table; END IF; v_time_position := (length(v_current_partition) - position('p_' in reverse(v_current_partition))) + 2; v_current_partition_timestamp := to_timestamp(substring(v_current_partition from v_time_position), v_row.datetime_string); END IF; -- Determine if this table is a child of a subpartition parent. If so, get limits of what child tables can be created based on parent suffix SELECT sub_min::timestamp, sub_max::timestamp INTO v_sub_timestamp_min, v_sub_timestamp_max FROM @extschema@.check_subpartition_limits(v_row.parent_table, 'time'); -- No need to run maintenance if it's outside the bounds of the top parent. IF v_sub_timestamp_min IS NOT NULL THEN IF v_current_partition_timestamp < v_sub_timestamp_min OR v_current_partition_timestamp > v_sub_timestamp_max THEN CONTINUE; END IF; END IF; v_time_position := (length(v_last_partition) - position('p_' in reverse(v_last_partition))) + 2; IF v_row.part_interval::interval <> '3 months' OR (v_row.part_interval::interval = '3 months' AND v_row.type = 'time-custom') THEN v_last_partition_timestamp := to_timestamp(substring(v_last_partition from v_time_position), v_row.datetime_string); ELSE -- to_timestamp doesn't recognize 'Q' date string formater. Handle it v_year := split_part(substring(v_last_partition from v_time_position), 'q', 1); v_quarter := split_part(substring(v_last_partition from v_time_position), 'q', 2); CASE WHEN v_quarter = '1' THEN v_last_partition_timestamp := to_timestamp(v_year || '-01-01', 'YYYY-MM-DD'); WHEN v_quarter = '2' THEN v_last_partition_timestamp := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); WHEN v_quarter = '3' THEN v_last_partition_timestamp := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); WHEN v_quarter = '4' THEN v_last_partition_timestamp := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); END CASE; END IF; -- Check and see how many premade partitions there are. v_premade_count = round(EXTRACT('epoch' FROM age(v_last_partition_timestamp, v_current_partition_timestamp)) / EXTRACT('epoch' FROM v_row.part_interval::interval)); v_next_partition_timestamp := v_last_partition_timestamp; -- 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 BEGIN v_next_partition_timestamp := v_next_partition_timestamp + v_row.part_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 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', 'Child partition creation skippd for parent table '||v_partition_time); END IF; RAISE WARNING 'Attempted partition time interval is outside PostgreSQL''s supported time range. Child partition creation skipped for parent table %', v_row.parent_table; CONTINUE; END; v_last_partition_created := @extschema@.create_partition_time(v_row.parent_table, ARRAY[v_next_partition_timestamp], p_analyze); v_create_count := v_create_count + 1; IF v_row.type = 'time-static' AND v_last_partition_created THEN PERFORM @extschema@.create_function_time(v_row.parent_table); END IF; -- Manage additonal constraints if set PERFORM @extschema@.apply_constraints(v_row.parent_table); -- Can be negative when subpartitioning and there are parent partitions in the past compared to current timestamp value. -- abs() prevents run_maintenence from running on those old parent tables v_premade_count = round(EXTRACT('epoch' FROM age(v_next_partition_timestamp, v_current_partition_timestamp)) / EXTRACT('epoch' FROM v_row.part_interval::interval)); END LOOP; ELSIF v_row.type = 'id-static' OR v_row.type ='id-dynamic' THEN -- 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 show_partitions FROM @extschema@.show_partitions(v_row.parent_table, 'DESC') LOOP EXECUTE 'SELECT '||v_row.control||' - ('||v_row.control||' % '||v_row.part_interval::int||') FROM '||v_row_max_id.show_partitions||' WHERE '||v_row.control||' = (SELECT max('||v_row.control||') FROM '||v_row_max_id.show_partitions||')' INTO v_current_partition_id; IF v_current_partition_id IS NOT NULL THEN EXIT; END IF; END LOOP; -- Determine if this table is a child of a subpartition parent. If so, get limits to see if run_maintenance even needs to run for it. SELECT sub_min::bigint, sub_max::bigint INTO v_sub_id_min, v_sub_id_max FROM @extschema@.check_subpartition_limits(v_row.parent_table, 'id'); -- No need to run maintenance if it's outside the bounds of the top parent. IF v_sub_id_min IS NOT NULL THEN IF v_current_partition_id < v_sub_id_min OR v_current_partition_id > v_sub_id_max THEN CONTINUE; END IF; END IF; v_id_position := (length(v_last_partition) - position('p_' in reverse(v_last_partition))) + 2; v_last_partition_id = substring(v_last_partition from v_id_position)::bigint; v_next_partition_id := v_last_partition_id + v_row.part_interval::bigint; WHILE ((v_next_partition_id - v_current_partition_id) / v_row.part_interval::bigint) <= v_row.premake LOOP v_last_partition_created := @extschema@.create_partition_id(v_row.parent_table, ARRAY[v_next_partition_id], p_analyze); IF v_last_partition_created THEN PERFORM @extschema@.create_function_id(v_row.parent_table); PERFORM @extschema@.apply_constraints(v_row.parent_table); END IF; v_next_partition_id := v_next_partition_id + v_row.part_interval::bigint; END LOOP; END IF; -- end main IF check for time or id END LOOP; -- end of creation loop -- Manage dropping old partitions if retention option is set FOR v_row IN SELECT parent_table FROM @extschema@.part_config WHERE retention IS NOT NULL AND undo_in_progress = false AND (type = 'time-static' OR type = 'time-dynamic' OR type = 'time-custom') LOOP IF p_parent_table IS NULL THEN v_drop_count := v_drop_count + @extschema@.drop_partition_time(v_row.parent_table); ELSE -- Only run retention on table given in parameter IF p_parent_table <> v_row.parent_table THEN CONTINUE; ELSE v_drop_count := v_drop_count + @extschema@.drop_partition_time(v_row.parent_table); END IF; END IF; IF v_drop_count > 0 THEN PERFORM @extschema@.create_function_time(v_row.parent_table); END IF; END LOOP; FOR v_row IN SELECT parent_table FROM @extschema@.part_config WHERE retention IS NOT NULL AND undo_in_progress = false AND (type = 'id-static' OR type = 'id-dynamic') LOOP IF p_parent_table IS NULL THEN v_drop_count := v_drop_count + @extschema@.drop_partition_id(v_row.parent_table); ELSE -- Only run retention on table given in parameter IF p_parent_table <> v_row.parent_table THEN CONTINUE; ELSE v_drop_count := v_drop_count + @extschema@.drop_partition_id(v_row.parent_table); END IF; END IF; IF v_drop_count > 0 THEN PERFORM @extschema@.create_function_id(v_row.parent_table); END IF; END LOOP; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Partition maintenance finished. '||v_create_count||' partitons made. '||v_drop_count||' partitions dropped.'); IF v_step_overflow_id IS NOT NULL OR v_step_serial_id IS NOT NULL THEN PERFORM fail_job(v_job_id); ELSE PERFORM close_job(v_job_id); END IF; EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_job(''PARTMAN RUN MAINTENANCE'')' INTO v_job_id; EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before job logging started'')' INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE 'SELECT '||v_jobmon_schema||'.add_step('||v_job_id||', ''EXCEPTION before first step logged'')' INTO v_step_id; END IF; EXECUTE 'SELECT '||v_jobmon_schema||'.update_step('||v_step_id||', ''CRITICAL'', ''ERROR: '||coalesce(SQLERRM,'unknown')||''')'; EXECUTE 'SELECT '||v_jobmon_schema||'.fail_job('||v_job_id||')'; END IF; RAISE EXCEPTION '%', SQLERRM; END $$; -- Restore dropped object privileges DO $$ DECLARE v_row record; BEGIN FOR v_row IN SELECT statement FROM partman_preserve_privs_temp LOOP IF v_row.statement IS NOT NULL THEN EXECUTE v_row.statement; END IF; END LOOP; END $$; DROP TABLE IF EXISTS partman_preserve_privs_temp; pg_partman-2.2.2/updates/pg_partman--1.8.7--2.0.0.sql000066400000000000000000005774141262146621700214520ustar00rootroot00000000000000-- 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. /*********************************************************** -- Begin code to migrate existing partition sets to new hybrid function -- You may need to adjust the setting of the search_path below to point the following commands to the schema that partman is installed to SELECT set_config('search_path','partman, public',false); CREATE TEMP TABLE partman_migrate_hybrid_temp (statement text); INSERT INTO partman_migrate_hybrid_temp SELECT format('SELECT create_function_id(%L);', parent_table) FROM part_config WHERE partition_type = 'id'; INSERT INTO partman_migrate_hybrid_temp SELECT format('SELECT create_function_time(%L);', parent_table) FROM part_config WHERE partition_type = 'time'; DO $$ DECLARE v_row record; BEGIN FOR v_row IN SELECT statement FROM partman_migrate_hybrid_temp LOOP IF v_row.statement IS NOT NULL THEN EXECUTE v_row.statement; END IF; END LOOP; END $$; DROP TABLE IF EXISTS partman_migrate_hybrid_temp; -- End code to migrate existing partition sets to new hybrid function ************************************************************/ ALTER TABLE @extschema@.part_config RENAME COLUMN "type" TO partition_type; ALTER TABLE @extschema@.part_config_sub RENAME COLUMN "sub_type" to "sub_partition_type"; ALTER TABLE @extschema@.part_config RENAME COLUMN part_interval TO partition_interval; ALTER TABLE @extschema@.part_config_sub RENAME COLUMN sub_part_interval TO sub_partition_interval; --NOTE This function is in the table sql file /* * Ensure that sub-partitioned tables that are themselves sub-partitions have the same configuration options set when they are part of the same inheritance tree */ CREATE OR REPLACE FUNCTION @extschema@.check_subpart_sameconfig(text) RETURNS boolean LANGUAGE sql STABLE AS $$ WITH child_tables AS ( SELECT n.nspname||'.'||c.relname AS 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::regclass = $1::regclass ) SELECT CASE WHEN count(*) <= 1 THEN true WHEN count(*) > 1 THEN false END FROM ( 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_use_run_maintenance , sub_jobmon FROM @extschema@.part_config_sub a JOIN child_tables b on a.sub_parent = b.tablename) x; $$; /* * Check function for config table partition types */ CREATE OR REPLACE FUNCTION @extschema@.check_partition_type (p_type text) RETURNS boolean LANGUAGE plpgsql IMMUTABLE SECURITY DEFINER AS $$ DECLARE v_result boolean; BEGIN SELECT p_type IN ('time', 'time-custom', 'id') INTO v_result; RETURN v_result; END $$; UPDATE @extschema@.part_config SET partition_type = 'time' WHERE partition_type = 'time-static' OR partition_type = 'time-dynamic'; UPDATE @extschema@.part_config_sub SET sub_partition_type = 'time' WHERE sub_partition_type = 'time-static' OR sub_partition_type = 'time-dynamic'; UPDATE @extschema@.part_config_sub SET sub_partition_type = 'id' WHERE sub_partition_type = 'id-static' OR sub_partition_type = 'id-dynamic'; /* * Function to turn a table into the parent of a partition set */ CREATE OR REPLACE FUNCTION create_parent( p_parent_table text , p_control text , p_type text , p_interval text , p_constraint_cols text[] DEFAULT NULL , p_premake int DEFAULT 4 , p_use_run_maintenance boolean DEFAULT NULL , p_start_partition text DEFAULT NULL , p_inherit_fk boolean DEFAULT true , p_jobmon boolean DEFAULT true , p_debug boolean DEFAULT false) RETURNS boolean LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_base_timestamp timestamp; v_count int := 1; v_datetime_string text; v_higher_parent text := p_parent_table; v_id_interval bigint; v_id_position int; v_job_id bigint; v_jobmon_schema text; v_last_partition_created boolean; v_max bigint; v_notnull boolean; v_old_search_path text; v_parent_partition_id bigint; v_parent_partition_timestamp timestamp; v_partition_time timestamp; v_partition_time_array timestamp[]; v_partition_id_array bigint[]; v_row record; v_run_maint boolean; v_sql text; v_start_time timestamp; v_starting_partition_id bigint; v_step_id bigint; v_step_overflow_id bigint; v_sub_parent text; v_success boolean := false; v_tablename text; v_time_interval interval; v_time_position int; v_top_datetime_string text; v_top_parent text := p_parent_table; BEGIN IF position('.' in p_parent_table) = 0 THEN RAISE EXCEPTION 'Parent table must be schema qualified'; END IF; SELECT tablename INTO v_tablename FROM pg_tables WHERE schemaname || '.' || tablename = p_parent_table; IF v_tablename IS NULL THEN RAISE EXCEPTION 'Please create given parent table first: %', p_parent_table; END IF; SELECT attnotnull INTO v_notnull FROM pg_attribute WHERE attrelid = p_parent_table::regclass AND attname = p_control; IF v_notnull = false OR v_notnull IS NULL THEN RAISE EXCEPTION 'Control column (%) for parent table (%) must be NOT NULL', p_control, p_parent_table; END IF; IF NOT @extschema@.check_partition_type(p_type) THEN RAISE EXCEPTION '% is not a valid partitioning type', p_type; END IF; EXECUTE 'LOCK TABLE '||p_parent_table||' IN ACCESS EXCLUSIVE MODE'; IF p_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF p_use_run_maintenance IS NOT NULL THEN IF p_use_run_maintenance IS FALSE AND (p_type = 'time' OR p_type = 'time-custom') THEN RAISE EXCEPTION 'p_run_maintenance cannot be set to false for time based partitioning'; END IF; v_run_maint := p_use_run_maintenance; ELSIF p_type = 'time' OR p_type = 'time-custom' THEN v_run_maint := TRUE; ELSIF p_type = 'id' THEN v_run_maint := FALSE; ELSE RAISE EXCEPTION 'use_run_maintenance value cannot be set NULL'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN SETUP PARENT: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating initial partitions on new parent table: '||p_parent_table); 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 WITH parent_table AS ( SELECT h.inhparent as parent_oid from pg_inherits h where h.inhrelid::regclass = p_parent_table::regclass ), sibling_children as ( select i.inhrelid::regclass::text as tablename from pg_inherits i join parent_table p on i.inhparent = p.parent_oid ) 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_use_run_maintenance , sub_jobmon FROM @extschema@.part_config_sub a JOIN sibling_children b on a.sub_parent = b.tablename LIMIT 1 LOOP INSERT INTO @extschema@.part_config_sub ( sub_parent , 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_use_run_maintenance , sub_jobmon) VALUES ( p_parent_table , v_row.sub_partition_type , v_row.sub_control , 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_use_run_maintenance , v_row.sub_jobmon); END LOOP; IF p_type = 'time' OR p_type = 'time-custom' 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 <> 'time-custom' THEN RAISE EXCEPTION 'Must use a predefined time interval if not using type "time-custom". See documentation.'; 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; -- First partition is either the min premake or p_start_partition v_start_time := COALESCE(p_start_partition::timestamp, CURRENT_TIMESTAMP - (v_time_interval * p_premake)); 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 v_datetime_string := 'YYYY'; 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 v_partition_time_array := array_append(v_partition_time_array, v_base_timestamp); LOOP -- If current loop value is less than or equal to the value of the max premake, add time to array. IF (v_base_timestamp + (v_time_interval * v_count)) < (CURRENT_TIMESTAMP + (v_time_interval * p_premake)) THEN BEGIN v_partition_time := (v_base_timestamp + (v_time_interval * v_count))::timestamp; 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. 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'); CONTINUE; END; ELSE EXIT; -- all needed partitions added to array. Exit the loop. END IF; v_count := v_count + 1; END LOOP; INSERT INTO @extschema@.part_config ( parent_table , partition_type , partition_interval , control , premake , constraint_cols , datetime_string , use_run_maintenance , inherit_fk , jobmon) VALUES ( p_parent_table , p_type , v_time_interval , p_control , p_premake , p_constraint_cols , v_datetime_string , v_run_maint , p_inherit_fk , p_jobmon); v_last_partition_created := @extschema@.create_partition_time(p_parent_table, v_partition_time_array, false); 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 ( SELECT i.inhparent AS top_parent_oid FROM pg_catalog.pg_inherits i JOIN pg_catalog.pg_class c ON c.oid = i.inhrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname||'.'||c.relname = p_parent_table ) SELECT n.nspname||'.'||c.relname, p.datetime_string INTO v_top_parent, v_top_datetime_string 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 JOIN @extschema@.part_config p ON p.parent_table = n.nspname||'.'||c.relname; IF v_top_parent IS NOT NULL THEN -- If so create the lowest possible partition that is within the boundary of the parent v_time_position := (length(p_parent_table) - position('p_' in reverse(p_parent_table))) + 2; v_parent_partition_timestamp := to_timestamp(substring(p_parent_table from v_time_position), v_top_datetime_string); IF v_base_timestamp >= v_parent_partition_timestamp THEN WHILE v_base_timestamp >= v_parent_partition_timestamp LOOP v_base_timestamp := v_base_timestamp - v_time_interval; END LOOP; v_base_timestamp := v_base_timestamp + v_time_interval; -- add one back since while loop set it one lower than is needed ELSIF v_base_timestamp < v_parent_partition_timestamp THEN WHILE v_base_timestamp < v_parent_partition_timestamp LOOP v_base_timestamp := v_base_timestamp + v_time_interval; END LOOP; -- Don't need to remove one since new starting time will fit in top parent interval 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); ELSE -- Currently unknown edge case if code gets here RAISE EXCEPTION 'No child tables created. Unexpected edge case encountered. Please report this error to author with conditions that led to it.'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Time partitions premade: '||p_premake); END IF; END IF; IF p_type = 'id' THEN v_id_interval := p_interval::bigint; IF v_id_interval < 10 THEN RAISE EXCEPTION 'Interval for serial partitioning must be greater than or equal to 10'; END IF; -- Check if parent table is a subpartition of an already existing id partition set managed by pg_partman. WHILE v_higher_parent IS NOT NULL LOOP -- initially set in DECLARE WITH top_oid AS ( SELECT i.inhparent AS top_parent_oid FROM pg_catalog.pg_inherits i JOIN pg_catalog.pg_class c ON c.oid = i.inhrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname||'.'||c.relname = v_higher_parent ) SELECT n.nspname||'.'||c.relname INTO v_higher_parent 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 JOIN @extschema@.part_config p ON p.parent_table = n.nspname||'.'||c.relname WHERE p.partition_type = 'id'; IF v_higher_parent IS NOT NULL THEN -- v_top_parent initially set in DECLARE v_top_parent := v_higher_parent; END IF; END LOOP; -- If custom start partition is set, use that. -- If custom start is not set and there is already data, start partitioning with the highest current value and ensure it's grabbed from highest top parent table v_sql := 'SELECT COALESCE('||quote_nullable(p_start_partition::bigint)||', max('||p_control||')::bigint, 0) FROM '||v_top_parent||' LIMIT 1'; EXECUTE v_sql INTO v_max; 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 THEN v_partition_id_array = array_append(v_partition_id_array, (v_starting_partition_id - v_id_interval*i)); END IF; v_partition_id_array = array_append(v_partition_id_array, (v_id_interval*i) + v_starting_partition_id); END LOOP; INSERT INTO @extschema@.part_config ( parent_table , partition_type , partition_interval , control , premake , constraint_cols , use_run_maintenance , inherit_fk , jobmon) VALUES ( p_parent_table , p_type , v_id_interval , p_control , p_premake , p_constraint_cols , v_run_maint , p_inherit_fk , p_jobmon); v_last_partition_created := @extschema@.create_partition_id(p_parent_table, v_partition_id_array, false); 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 -- See if it's actually a subpartition of a parent id partition WITH top_oid AS ( SELECT i.inhparent AS top_parent_oid FROM pg_catalog.pg_inherits i JOIN pg_catalog.pg_class c ON c.oid = i.inhrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname||'.'||c.relname = p_parent_table ) SELECT n.nspname||'.'||c.relname INTO v_top_parent 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 JOIN @extschema@.part_config p ON p.parent_table = n.nspname||'.'||c.relname WHERE p.partition_type = 'id'; IF v_top_parent IS NOT NULL THEN -- Create the lowest possible partition that is within the boundary of the parent v_id_position := (length(p_parent_table) - position('p_' in reverse(p_parent_table))) + 2; v_parent_partition_id = substring(p_parent_table from v_id_position)::bigint; IF v_starting_partition_id >= v_parent_partition_id THEN WHILE v_starting_partition_id >= v_parent_partition_id LOOP v_starting_partition_id := v_starting_partition_id - v_id_interval; END LOOP; v_starting_partition_id := v_starting_partition_id + v_id_interval; -- add one back since while loop set it one lower than is needed ELSIF v_starting_partition_id < v_parent_partition_id THEN WHILE v_starting_partition_id < v_parent_partition_id LOOP v_starting_partition_id := v_starting_partition_id + v_id_interval; END LOOP; -- Don't need to remove one since new starting id will fit in top parent interval 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); ELSE -- Currently unknown edge case if code gets here RAISE EXCEPTION 'No child tables created. Unexpected edge case encountered. Please report this error to author with conditions that led to it.'; END IF; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Creating partition function'); END IF; IF p_type = 'time' OR p_type = 'time-custom' THEN PERFORM @extschema@.create_function_time(p_parent_table); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Time function created'); END IF; ELSIF p_type = 'id' THEN PERFORM @extschema@.create_function_id(p_parent_table); 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); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); IF v_step_overflow_id IS NOT NULL THEN PERFORM fail_job(v_job_id); ELSE PERFORM close_job(v_job_id); END IF; EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; v_success := true; RETURN v_success; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN CREATE PARENT: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''Partition creation for table '||p_parent_table||' failed'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Create the trigger function for the parent table of an id-based partition set */ CREATE OR REPLACE FUNCTION create_function_id(p_parent_table text) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_control text; v_count int; v_current_partition_name text; v_current_partition_id bigint; v_datetime_string text; v_final_partition_id bigint; v_function_name text; v_higher_parent text := p_parent_table; v_id_position int; v_job_id bigint; v_jobmon text; v_jobmon_schema text; v_last_partition text; v_max bigint; v_next_partition_id bigint; v_next_partition_name text; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_partition_interval bigint; v_premake int; v_prev_partition_id bigint; v_prev_partition_name text; v_row_max_id record; v_run_maint boolean; v_step_id bigint; v_top_parent text := p_parent_table; v_trig_func text; BEGIN SELECT partition_interval::bigint , control , premake , use_run_maintenance , jobmon INTO v_partition_interval , v_control , v_premake , v_run_maint , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND partition_type = 'id'; IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; SELECT show_partitions INTO v_last_partition FROM @extschema@.show_partitions(p_parent_table, 'DESC') LIMIT 1; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE FUNCTION: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating partition function for table '||p_parent_table); END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; v_function_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, '_part_trig_func', FALSE); -- Get the highest level top parent if multi-level partitioned in order to get proper max() value below WHILE v_higher_parent IS NOT NULL LOOP -- initially set in DECLARE WITH top_oid AS ( SELECT i.inhparent AS top_parent_oid FROM pg_catalog.pg_inherits i JOIN pg_catalog.pg_class c ON c.oid = i.inhrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname||'.'||c.relname = v_higher_parent ) SELECT n.nspname||'.'||c.relname INTO v_higher_parent 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 JOIN @extschema@.part_config p ON p.parent_table = n.nspname||'.'||c.relname WHERE p.partition_type = 'id'; IF v_higher_parent IS NOT NULL THEN -- initially set in DECLARE v_top_parent := v_higher_parent; END IF; END 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 parent. FOR v_row_max_id IN SELECT show_partitions FROM @extschema@.show_partitions(v_top_parent, 'DESC') LOOP EXECUTE 'SELECT max('||v_control||') FROM '||v_row_max_id.show_partitions INTO v_max; IF v_max IS NOT NULL THEN EXIT; END IF; END LOOP; IF v_max IS NULL THEN v_max := 0; END IF; v_current_partition_id = v_max - (v_max % v_partition_interval); v_next_partition_id := v_current_partition_id + v_partition_interval; v_current_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_current_partition_id::text, TRUE); v_trig_func := 'CREATE OR REPLACE FUNCTION '||v_function_name||'() RETURNS trigger LANGUAGE plpgsql AS $t$ DECLARE v_count int; v_current_partition_id bigint; v_current_partition_name text; v_id_position int; v_last_partition text := '||quote_literal(v_last_partition)||'; v_next_partition_id bigint; v_next_partition_name text; v_partition_created boolean; BEGIN IF TG_OP = ''INSERT'' THEN IF NEW.'||v_control||' >= '||v_current_partition_id||' AND NEW.'||v_control||' < '||v_next_partition_id|| ' THEN '; SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname ||'.'||tablename = v_current_partition_name; IF v_count > 0 THEN v_trig_func := v_trig_func || ' INSERT INTO '||v_current_partition_name||' VALUES (NEW.*); '; ELSE v_trig_func := v_trig_func || ' -- Child table for current values does not exist in this partition set, so write to parent RETURN NEW;'; END IF; FOR i IN 1..v_premake LOOP v_prev_partition_id := v_current_partition_id - (v_partition_interval * i); v_next_partition_id := v_current_partition_id + (v_partition_interval * i); v_final_partition_id := v_next_partition_id + v_partition_interval; v_prev_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_prev_partition_id::text, TRUE); v_next_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_next_partition_id::text, TRUE); -- Check that child table exist before making a rule to insert to them. -- Handles edge case of changing premake immediately after running create_parent(). SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname ||'.'||tablename = v_prev_partition_name; IF v_count > 0 THEN -- Only handle previous partitions if they're starting above zero IF v_prev_partition_id >= 0 THEN v_trig_func := v_trig_func ||' ELSIF NEW.'||v_control||' >= '||v_prev_partition_id||' AND NEW.'||v_control||' < '||v_prev_partition_id + v_partition_interval|| ' THEN INSERT INTO '||v_prev_partition_name||' VALUES (NEW.*); '; END IF; END IF; SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname ||'.'||tablename = v_next_partition_name; IF v_count > 0 THEN v_trig_func := v_trig_func ||' ELSIF NEW.'||v_control||' >= '||v_next_partition_id||' AND NEW.'||v_control||' < '||v_final_partition_id|| ' THEN INSERT INTO '||v_next_partition_name||' VALUES (NEW.*);'; END IF; END LOOP; v_trig_func := v_trig_func ||' ELSE v_current_partition_id := NEW.'||v_control||' - (NEW.'||v_control||' % '||v_partition_interval||'); v_current_partition_name := @extschema@.check_name_length('''||v_parent_tablename||''', '''||v_parent_schema||''', v_current_partition_id::text, TRUE); SELECT count(*) INTO v_count FROM pg_tables WHERE schemaname ||''.''|| tablename = v_current_partition_name; IF v_count > 0 THEN EXECUTE ''INSERT INTO ''||v_current_partition_name||'' VALUES($1.*)'' USING NEW; ELSE RETURN NEW; END IF; END IF;'; IF v_run_maint IS FALSE THEN v_trig_func := v_trig_func ||' v_current_partition_id := NEW.'||v_control||' - (NEW.'||v_control||' % '||v_partition_interval||'); IF (NEW.'||v_control||' % '||v_partition_interval||') > ('||v_partition_interval||' / 2) THEN v_id_position := (length(v_last_partition) - position(''p_'' in reverse(v_last_partition))) + 2; v_next_partition_id := (substring(v_last_partition from v_id_position)::bigint) + '||v_partition_interval||'; WHILE ((v_next_partition_id - v_current_partition_id) / '||v_partition_interval||') <= '||v_premake||' LOOP v_partition_created := @extschema@.create_partition_id('||quote_literal(p_parent_table)||', ARRAY[v_next_partition_id]); IF v_partition_created THEN PERFORM @extschema@.create_function_id('||quote_literal(p_parent_table)||'); PERFORM @extschema@.apply_constraints('||quote_literal(p_parent_table)||'); END IF; v_next_partition_id := v_next_partition_id + '||v_partition_interval||'; END LOOP; END IF;'; END IF; v_trig_func := v_trig_func ||' END IF; RETURN NULL; END $t$;'; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Added function for current id interval: '||v_current_partition_id||' to '||v_final_partition_id-1); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN CREATE FUNCTION: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''Partition function maintenance for table %s failed'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Create the trigger function for the parent table of a time-based partition set */ CREATE OR REPLACE FUNCTION create_function_time(p_parent_table text) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_control text; v_count int; v_current_partition_name text; v_current_partition_timestamp timestamptz; v_datetime_string text; v_final_partition_timestamp timestamptz; v_function_name text; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_new_length int; v_next_partition_name text; v_next_partition_timestamp timestamptz; v_parent_schema text; v_parent_tablename text; v_partition_interval interval; v_premake int; v_prev_partition_name text; v_prev_partition_timestamp timestamptz; v_step_id bigint; v_trig_func text; v_type text; BEGIN SELECT partition_type , partition_interval::interval , control , premake , datetime_string , jobmon INTO v_type , v_partition_interval , v_control , v_premake , v_datetime_string , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (partition_type = 'time' OR partition_type = 'time-custom'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE FUNCTION: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating partition function for table '||p_parent_table); END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; v_function_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, '_part_trig_func', FALSE); IF v_type = 'time' THEN v_trig_func := 'CREATE OR REPLACE FUNCTION '||v_function_name||'() RETURNS trigger LANGUAGE plpgsql AS $t$ DECLARE v_count int; v_partition_name text; v_partition_timestamp timestamptz; BEGIN IF TG_OP = ''INSERT'' THEN '; CASE WHEN v_partition_interval = '15 mins' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''hour'', NEW.'||v_control||') + ''15min''::interval * floor(date_part(''minute'', NEW.'||v_control||') / 15.0);'; v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0); WHEN v_partition_interval = '30 mins' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''hour'', NEW.'||v_control||') + ''30min''::interval * floor(date_part(''minute'', NEW.'||v_control||') / 30.0);'; v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0); WHEN v_partition_interval = '1 hour' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''hour'', NEW.'||v_control||');'; v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP); WHEN v_partition_interval = '1 day' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''day'', NEW.'||v_control||');'; v_current_partition_timestamp := date_trunc('day', CURRENT_TIMESTAMP); WHEN v_partition_interval = '1 week' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''week'', NEW.'||v_control||');'; v_current_partition_timestamp := date_trunc('week', CURRENT_TIMESTAMP); WHEN v_partition_interval = '1 month' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''month'', NEW.'||v_control||');'; v_current_partition_timestamp := date_trunc('month', CURRENT_TIMESTAMP); WHEN v_partition_interval = '3 months' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''quarter'', NEW.'||v_control||');'; v_current_partition_timestamp := date_trunc('quarter', CURRENT_TIMESTAMP); WHEN v_partition_interval = '1 year' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''year'', NEW.'||v_control||');'; v_current_partition_timestamp := date_trunc('year', CURRENT_TIMESTAMP); END CASE; v_current_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, to_char(v_current_partition_timestamp, v_datetime_string), TRUE); v_next_partition_timestamp := v_current_partition_timestamp + v_partition_interval::interval; v_trig_func := v_trig_func ||' IF NEW.'||v_control||' >= '||quote_literal(v_current_partition_timestamp)||' AND NEW.'||v_control||' < '||quote_literal(v_next_partition_timestamp)|| ' THEN '; SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname ||'.'||tablename = v_current_partition_name; IF v_count > 0 THEN v_trig_func := v_trig_func || ' INSERT INTO '||v_current_partition_name||' VALUES (NEW.*); '; ELSE v_trig_func := v_trig_func || ' -- Child table for current values does not exist in this partition set, so write to parent RETURN NEW;'; END IF; FOR i IN 1..v_premake LOOP v_prev_partition_timestamp := v_current_partition_timestamp - (v_partition_interval::interval * i); v_next_partition_timestamp := v_current_partition_timestamp + (v_partition_interval::interval * i); v_final_partition_timestamp := v_next_partition_timestamp + (v_partition_interval::interval); v_prev_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, to_char(v_prev_partition_timestamp, v_datetime_string), TRUE); v_next_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, to_char(v_next_partition_timestamp, v_datetime_string), TRUE); -- Check that child table exist before making a rule to insert to them. -- Handles edge case of changing premake immediately after running create_parent(). SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname ||'.'||tablename = v_prev_partition_name; IF v_count > 0 THEN v_trig_func := v_trig_func ||' ELSIF NEW.'||v_control||' >= '||quote_literal(v_prev_partition_timestamp)||' AND NEW.'||v_control||' < '|| quote_literal(v_prev_partition_timestamp + v_partition_interval::interval)|| ' THEN INSERT INTO '||v_prev_partition_name||' VALUES (NEW.*);'; END IF; SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname ||'.'||tablename = v_next_partition_name; IF v_count > 0 THEN v_trig_func := v_trig_func ||' ELSIF NEW.'||v_control||' >= '||quote_literal(v_next_partition_timestamp)||' AND NEW.'||v_control||' < '|| quote_literal(v_final_partition_timestamp)|| ' THEN INSERT INTO '||v_next_partition_name||' VALUES (NEW.*);'; END IF; END LOOP; v_trig_func := v_trig_func||' ELSE v_partition_name := @extschema@.check_name_length('''||v_parent_tablename||''', '''||v_parent_schema||''', to_char(v_partition_timestamp, '||quote_literal(v_datetime_string)||'), TRUE); SELECT count(*) INTO v_count FROM pg_tables WHERE schemaname ||''.''|| tablename = v_partition_name; IF v_count > 0 THEN EXECUTE ''INSERT INTO ''||v_partition_name||'' VALUES($1.*)'' USING NEW; ELSE RETURN NEW; END IF; END IF;'; v_trig_func := v_trig_func ||' END IF; RETURN NULL; END $t$;'; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Added function for current time interval: '|| v_current_partition_timestamp||' to '||(v_final_partition_timestamp-'1sec'::interval)); END IF; ELSIF v_type = 'time-custom' THEN v_trig_func := 'CREATE OR REPLACE FUNCTION '||v_function_name||'() RETURNS trigger LANGUAGE plpgsql AS $t$ DECLARE v_child_table text; v_count int; BEGIN SELECT child_table INTO v_child_table FROM @extschema@.custom_time_partitions WHERE partition_range @> NEW.'||v_control||' AND parent_table = '||quote_literal(p_parent_table)||'; SELECT count(*) INTO v_count FROM pg_tables WHERE schemaname ||''.''|| tablename = v_child_table; IF v_count > 0 THEN EXECUTE ''INSERT INTO ''||v_child_table||'' VALUES ($1.*)'' USING NEW; ELSE RETURN NEW; END IF; RETURN NULL; END $t$;'; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Added function for custom time table: '||p_parent_table); END IF; ELSE RAISE EXCEPTION 'ERROR: Invalid time partitioning type given: %', v_type; END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN CREATE FUNCTION: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''Partition function maintenance for table %s failed'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Function to create id partitions */ CREATE OR REPLACE FUNCTION create_partition_id(p_parent_table text, p_partition_ids bigint[], p_analyze boolean DEFAULT true) RETURNS boolean LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE 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_grantees text[]; v_hasoids boolean; v_id bigint; v_inherit_fk boolean; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_parent_grant record; v_parent_owner text; 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_revoke text[]; v_row record; v_sql text; v_step_id bigint; v_sub_id_max bigint; v_sub_id_min bigint; v_tablename text; v_unlogged char; BEGIN SELECT control , partition_interval , inherit_fk , jobmon INTO v_control , v_partition_interval , v_inherit_fk , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND partition_type = 'id'; IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; -- Determine if this table is a child of a subpartition parent. If so, get limits of what child tables can be created based on parent suffix SELECT sub_min::bigint, sub_max::bigint INTO v_sub_id_min, v_sub_id_max FROM @extschema@.check_subpartition_limits(p_parent_table, 'id'); SELECT tableowner, schemaname, tablename, tablespace INTO v_parent_owner, v_parent_schema, v_parent_tablename, v_parent_tablespace FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); 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. 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; END IF; END IF; v_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_id::text, TRUE); -- If child table already exists, skip creation SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_tablename IS NOT NULL 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, 'Creating new partition '||v_partition_name||' with interval from '||v_id||' to '||(v_id + v_partition_interval)-1); END IF; SELECT relpersistence INTO v_unlogged FROM pg_catalog.pg_class WHERE oid::regclass = p_parent_table::regclass; v_sql := 'CREATE'; IF v_unlogged = 'u' THEN v_sql := v_sql || ' UNLOGGED'; END IF; v_sql := v_sql || ' TABLE '||v_partition_name||' (LIKE '||p_parent_table||' INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING STORAGE INCLUDING COMMENTS)'; SELECT relhasoids INTO v_hasoids FROM pg_catalog.pg_class WHERE oid::regclass = p_parent_table::regclass; IF v_hasoids IS TRUE THEN v_sql := v_sql || ' WITH (OIDS)'; END IF; EXECUTE v_sql; SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_parent_tablespace IS NOT NULL THEN EXECUTE 'ALTER TABLE '||v_partition_name||' SET TABLESPACE '||v_parent_tablespace; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' ADD CONSTRAINT '||v_tablename||'_partition_check CHECK ('||v_control||'>='||quote_literal(v_id)||' AND '||v_control||'<'||quote_literal(v_id + v_partition_interval)||')'; EXECUTE 'ALTER TABLE '||v_partition_name||' INHERIT '||p_parent_table; FOR v_parent_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = p_parent_table GROUP BY grantee LOOP EXECUTE 'GRANT '||array_to_string(v_parent_grant.types, ',')||' ON '||v_partition_name||' TO '||v_parent_grant.grantee; SELECT array_agg(r) INTO v_revoke FROM (SELECT unnest(v_all) AS r EXCEPT SELECT unnest(v_parent_grant.types)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE '||array_to_string(v_revoke, ',')||' ON '||v_partition_name||' FROM '||v_parent_grant.grantee||' CASCADE'; END IF; v_grantees := array_append(v_grantees, v_parent_grant.grantee::text); END LOOP; -- Revoke all privileges from roles that have none on the parent IF v_grantees IS NOT NULL THEN SELECT array_agg(r) INTO v_revoke FROM ( SELECT DISTINCT grantee::text AS r FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = v_partition_name EXCEPT SELECT unnest(v_grantees)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE ALL ON '||v_partition_name||' FROM '||array_to_string(v_revoke, ','); END IF; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' OWNER TO '||v_parent_owner; IF v_inherit_fk THEN PERFORM @extschema@.apply_foreign_keys(quote_ident(v_parent_schema)||'.'||quote_ident(v_parent_tablename), v_partition_name); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); 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 FOR v_row IN SELECT sub_parent , sub_control , sub_partition_type , 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_use_run_maintenance , sub_jobmon FROM @extschema@.part_config_sub WHERE sub_parent = p_parent_table LOOP IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Subpartitioning '||v_partition_name); END IF; v_sql := format('SELECT @extschema@.create_parent( p_parent_table := %L , p_control := %L , p_type := %L , p_interval := %L , p_constraint_cols := %L , p_premake := %L , p_use_run_maintenance := %L , p_inherit_fk := %L , p_jobmon := %L )' , v_partition_name , v_row.sub_control , v_row.sub_partition_type , v_row.sub_partition_interval , v_row.sub_constraint_cols , v_row.sub_premake , v_row.sub_use_run_maintenance , v_row.sub_inherit_fk , v_row.sub_jobmon); EXECUTE v_sql; 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 WHERE parent_table = v_partition_name; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END LOOP; -- end sub partitioning LOOP 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, 'Analyzing partition set: '||p_parent_table); END IF; EXECUTE 'ANALYZE '||p_parent_table; 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 v_step_id := add_step(v_job_id, 'No partitions created for partition set: '||p_parent_table); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; PERFORM close_job(v_job_id); END IF; IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_partition_created; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN CREATE TABLE: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Function to create a child table in a time-based partition set */ CREATE OR REPLACE FUNCTION create_partition_time (p_parent_table text, p_partition_times timestamp[], p_analyze boolean DEFAULT true) RETURNS boolean LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE 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_grantees text[]; v_hasoids boolean; v_inherit_fk boolean; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_parent_grant record; v_parent_owner text; v_parent_schema text; v_parent_tablename text; v_partition_created boolean := false; v_partition_name text; v_partition_suffix text; v_parent_tablespace text; v_partition_interval interval; v_partition_timestamp_end timestamp; v_partition_timestamp_start timestamp; v_quarter text; v_revoke text[]; v_row record; v_sql text; v_step_id bigint; v_step_overflow_id bigint; v_sub_timestamp_max timestamp; v_sub_timestamp_min timestamp; v_tablename text; v_trunc_value text; v_time timestamp; v_type text; v_unlogged char; v_year text; BEGIN SELECT partition_type , control , partition_interval , inherit_fk , jobmon , datetime_string INTO v_type , v_control , v_partition_interval , v_inherit_fk , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND partition_type = 'time' OR partition_type = 'time-custom'; IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; -- Determine if this table is a child of a subpartition parent. If so, get limits of what child tables can be created based on parent suffix SELECT sub_min::timestamp, sub_max::timestamp INTO v_sub_timestamp_min, v_sub_timestamp_max FROM @extschema@.check_subpartition_limits(p_parent_table, 'time'); SELECT tableowner, schemaname, tablename, tablespace INTO v_parent_owner, v_parent_schema, v_parent_tablename, v_parent_tablespace FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); END IF; 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. 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'); CONTINUE; END; -- 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 CONTINUE; END IF; END IF; -- This suffix generation code is in partition_data_time() as well v_partition_suffix := to_char(v_time, 'YYYY'); IF v_partition_interval < '1 year' AND v_partition_interval <> '1 week' THEN v_partition_suffix := v_partition_suffix ||'_'|| to_char(v_time, 'MM'); IF v_partition_interval < '1 month' AND v_partition_interval <> '1 week' THEN v_partition_suffix := v_partition_suffix ||'_'|| to_char(v_time, 'DD'); IF v_partition_interval < '1 day' THEN v_partition_suffix := v_partition_suffix || '_' || to_char(v_time, 'HH24MI'); IF v_partition_interval < '1 minute' THEN v_partition_suffix := v_partition_suffix || to_char(v_time, 'SS'); END IF; -- end < minute IF END IF; -- end < day IF END IF; -- end < month IF END IF; -- end < year IF IF v_partition_interval = '1 week' THEN v_partition_suffix := to_char(v_time, 'IYYY') || 'w' || to_char(v_time, 'IW'); END IF; -- "Q" is ignored in to_timestamp, so handle special case IF v_partition_interval = '3 months' AND v_type = 'time' THEN v_year := to_char(v_time, 'YYYY'); v_quarter := to_char(v_time, 'Q'); v_partition_suffix := v_year || 'q' || v_quarter; END IF; v_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_partition_suffix, TRUE); SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_tablename IS NOT NULL 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, 'Creating new partition '||v_partition_name||' with interval from '||v_partition_timestamp_start||' to '||(v_partition_timestamp_end-'1sec'::interval)); END IF; SELECT relpersistence INTO v_unlogged FROM pg_catalog.pg_class WHERE oid::regclass = p_parent_table::regclass; v_sql := 'CREATE'; IF v_unlogged = 'u' THEN v_sql := v_sql || ' UNLOGGED'; END IF; v_sql := v_sql || ' TABLE '||v_partition_name||' (LIKE '||p_parent_table||' INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING STORAGE INCLUDING COMMENTS)'; SELECT relhasoids INTO v_hasoids FROM pg_catalog.pg_class WHERE oid::regclass = p_parent_table::regclass; IF v_hasoids IS TRUE THEN v_sql := v_sql || ' WITH (OIDS)'; END IF; EXECUTE v_sql; SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_parent_tablespace IS NOT NULL THEN EXECUTE 'ALTER TABLE '||v_partition_name||' SET TABLESPACE '||v_parent_tablespace; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' ADD CONSTRAINT '||v_tablename||'_partition_check CHECK ('||v_control||'>='||quote_literal(v_partition_timestamp_start)||' AND '||v_control||'<'||quote_literal(v_partition_timestamp_end)||')'; EXECUTE 'ALTER TABLE '||v_partition_name||' INHERIT '||p_parent_table; -- If custom time, set extra config options. IF v_type = 'time-custom' THEN INSERT INTO @extschema@.custom_time_partitions (parent_table, child_table, partition_range) VALUES ( p_parent_table, v_partition_name, tstzrange(v_partition_timestamp_start, v_partition_timestamp_end, '[)') ); END IF; FOR v_parent_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = p_parent_table GROUP BY grantee LOOP EXECUTE 'GRANT '||array_to_string(v_parent_grant.types, ',')||' ON '||v_partition_name||' TO '||v_parent_grant.grantee; SELECT array_agg(r) INTO v_revoke FROM (SELECT unnest(v_all) AS r EXCEPT SELECT unnest(v_parent_grant.types)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE '||array_to_string(v_revoke, ',')||' ON '||v_partition_name||' FROM '||v_parent_grant.grantee||' CASCADE'; END IF; v_grantees := array_append(v_grantees, v_parent_grant.grantee::text); END LOOP; -- Revoke all privileges from roles that have none on the parent IF v_grantees IS NOT NULL THEN SELECT array_agg(r) INTO v_revoke FROM ( SELECT DISTINCT grantee::text AS r FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = v_partition_name EXCEPT SELECT unnest(v_grantees)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE ALL ON '||v_partition_name||' FROM '||array_to_string(v_revoke, ','); END IF; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' OWNER TO '||v_parent_owner; IF v_inherit_fk THEN PERFORM @extschema@.apply_foreign_keys(quote_ident(v_parent_schema)||'.'||quote_ident(v_parent_tablename), v_partition_name); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); 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 FOR v_row IN SELECT sub_parent , sub_control , sub_partition_type , 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_use_run_maintenance , sub_jobmon FROM @extschema@.part_config_sub WHERE sub_parent = p_parent_table LOOP IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Subpartitioning '||v_partition_name); END IF; v_sql := format('SELECT @extschema@.create_parent( p_parent_table := %L , p_control := %L , p_type := %L , p_interval := %L , p_constraint_cols := %L , p_premake := %L , p_use_run_maintenance := %L , p_inherit_fk := %L , p_jobmon := %L )' , v_partition_name , v_row.sub_control , v_row.sub_partition_type , v_row.sub_partition_interval , v_row.sub_constraint_cols , v_row.sub_premake , v_row.sub_use_run_maintenance , v_row.sub_inherit_fk , v_row.sub_jobmon); EXECUTE v_sql; 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 WHERE parent_table = v_partition_name; END LOOP; -- end sub partitioning LOOP 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, 'Analyzing partition set: '||p_parent_table); END IF; EXECUTE 'ANALYZE '||p_parent_table; 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 v_step_id := add_step(v_job_id, 'No partitions created for partition set: '||p_parent_table); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; IF v_step_overflow_id IS NOT NULL THEN PERFORM fail_job(v_job_id); ELSE PERFORM close_job(v_job_id); END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_partition_created; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN CREATE TABLE: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Apply constraints managed by partman extension */ CREATE OR REPLACE FUNCTION apply_constraints(p_parent_table text, p_child_table text DEFAULT NULL, p_analyze boolean DEFAULT FALSE, p_debug boolean DEFAULT FALSE) RETURNS void LANGUAGE plpgsql AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_child_table text; v_child_tablename text; v_col text; v_constraint_cols text[]; v_constraint_col_type text; v_constraint_name text; v_datetime_string text; v_existing_constraint_name text; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_last_partition text; v_last_partition_id int; v_last_partition_timestamp timestamp; v_constraint_values record; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_partition_interval text; v_partition_suffix text; v_premake int; v_sql text; v_step_id bigint; v_suffix_position int; v_type text; BEGIN SELECT partition_type , partition_interval , premake , datetime_string , constraint_cols , jobmon INTO v_type , v_partition_interval , v_premake , v_datetime_string , v_constraint_cols , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_constraint_cols IS NULL THEN IF p_debug THEN RAISE NOTICE 'Given parent table (%) not set up for constraint management (constraint_cols is NULL)', p_parent_table; END IF; -- Returns silently to allow this function to be simply called by maintenance processes without having to check if config options are set. RETURN; END IF; SELECT show_partitions INTO v_last_partition FROM @extschema@.show_partitions(p_parent_table, 'DESC') LIMIT 1; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE CONSTRAINT: '||p_parent_table); END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; -- If p_child_table is null, figure out the partition that is the one right before the premake value backwards. IF p_child_table IS NULL THEN IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Automatically determining most recent child on which to apply constraints'); END IF; v_suffix_position := (length(v_last_partition) - position('p_' in reverse(v_last_partition))) + 2; IF v_type IN ('time', 'time-custom') THEN v_last_partition_timestamp := to_timestamp(substring(v_last_partition from v_suffix_position), v_datetime_string); v_partition_suffix := to_char(v_last_partition_timestamp - (v_partition_interval::interval * ((v_premake * 2)+1) ), v_datetime_string); ELSIF v_type = 'id' THEN v_last_partition_id := substring(v_last_partition from v_suffix_position)::int; v_partition_suffix := (v_last_partition_id - (v_partition_interval::int * ((v_premake * 2)+1) ))::text; END IF; v_child_table := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_partition_suffix, TRUE); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Target child table: '||v_child_table); END IF; ELSE v_child_table := p_child_table; END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Checking if target child table exists'); END IF; SELECT tablename INTO v_child_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_child_table; IF v_child_tablename IS NULL THEN IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'NOTICE', 'Target child table ('||v_child_table||') does not exist. Skipping constraint creation.'); PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; IF p_debug THEN RAISE NOTICE 'Target child table (%) does not exist. Skipping constraint creation.', v_child_table; END IF; RETURN; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; FOREACH v_col IN ARRAY v_constraint_cols LOOP SELECT c.conname INTO v_existing_constraint_name FROM pg_catalog.pg_constraint c JOIN pg_catalog.pg_attribute a ON c.conrelid = a.attrelid WHERE conrelid = v_child_table::regclass AND c.conname LIKE 'partmanconstr_%' AND c.contype = 'c' AND a.attname = v_col AND ARRAY[a.attnum] <@ c.conkey AND a.attisdropped = false; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Applying new constraint on column: '||v_col); END IF; IF v_existing_constraint_name IS NOT NULL THEN IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'NOTICE', 'Partman managed constraint already exists on this table ('||v_child_table||') and column ('||v_col||'). Skipping creation.'); END IF; RAISE WARNING 'Partman managed constraint already exists on this table (%) and column (%). Skipping creation.', v_child_table, v_col ; CONTINUE; END IF; -- 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 'SELECT min('||v_col||')::text AS min, max('||v_col||')::text AS max FROM '||v_child_table INTO v_constraint_values; IF v_constraint_values IS NOT NULL THEN v_sql := concat('ALTER TABLE ', v_child_table, ' ADD CONSTRAINT ', v_constraint_name , ' CHECK (', v_col, ' >= ', quote_literal(v_constraint_values.min), ' AND ' , v_col, ' <= ', quote_literal(v_constraint_values.max), ')' ); IF p_debug THEN RAISE NOTICE 'Constraint creation query: %', v_sql; END IF; EXECUTE v_sql; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'New constraint created: '||v_sql); END IF; ELSE IF p_debug THEN RAISE NOTICE 'Given column (%) contains all NULLs. No constraint created', v_col; END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'NOTICE', 'Given column ('||v_col||') contains all NULLs. No constraint created'); END IF; END IF; END LOOP; IF p_analyze THEN IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Running analyze on partition set: '||p_parent_table); END IF; IF p_debug THEN RAISE NOTICE 'Running analyze on partition set: %', p_parent_table; END IF; EXECUTE 'ANALYZE '||p_parent_table; 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 PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN CREATE CONSTRAINT: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Apply foreign keys that exist on the given parent to the given child table */ CREATE OR REPLACE FUNCTION apply_foreign_keys(p_parent_table text, p_child_table text DEFAULT NULL, p_debug boolean DEFAULT false) RETURNS void LANGUAGE plpgsql AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_job_id bigint; v_jobmon text; v_jobmon_schema text; v_old_search_path text; v_ref_schema text; v_ref_table text; v_row record; v_schemaname text; v_sql text; v_step_id bigint; v_tablename text; BEGIN SELECT jobmon INTO v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN APPLYING FOREIGN KEYS: '||p_parent_table); END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Checking if target child table exists'); END IF; SELECT schemaname, tablename INTO v_schemaname, v_tablename FROM pg_catalog.pg_tables WHERE schemaname||'.'||tablename = p_child_table; IF v_tablename IS NULL THEN IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'CRITICAL', 'Target child table ('||v_child_table||') does not exist.'); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION 'Target child table (%.%) does not exist.', v_schemaname, v_tablename; RETURN; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; FOR v_row IN SELECT n.nspname||'.'||cl.relname AS ref_table , '"'||string_agg(att.attname, '","')||'"' AS ref_column , '"'||string_agg(att2.attname, '","')||'"' AS child_column , keys.condeferred , keys.condeferrable , keys.confupdtype , keys.confdeltype , keys.confmatchtype FROM ( SELECT unnest(con.conkey) as ref , unnest(con.confkey) as child , con.confrelid , con.conrelid , con.condeferred , con.condeferrable , con.confupdtype , con.confdeltype , con.confmatchtype FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid JOIN pg_catalog.pg_constraint con ON c.oid = con.conrelid WHERE n.nspname ||'.'|| c.relname = p_parent_table AND con.contype = 'f' ORDER BY con.conkey ) keys JOIN pg_catalog.pg_class cl ON cl.oid = keys.confrelid JOIN pg_catalog.pg_namespace n ON cl.relnamespace = n.oid JOIN pg_catalog.pg_attribute att ON att.attrelid = keys.confrelid AND att.attnum = keys.child JOIN pg_catalog.pg_attribute att2 ON att2.attrelid = keys.conrelid AND att2.attnum = keys.ref GROUP BY n.nspname, cl.relname, keys.condeferred, keys.condeferrable, keys.confupdtype, keys.confdeltype, keys.confmatchtype LOOP SELECT schemaname, tablename INTO v_ref_schema, v_ref_table FROM pg_tables WHERE schemaname||'.'||tablename = v_row.ref_table; v_sql := format('ALTER TABLE %I.%I ADD FOREIGN KEY (%s) REFERENCES %I.%I (%s)', v_schemaname, v_tablename, v_row.child_column, v_ref_schema, v_ref_table, v_row.ref_column); CASE WHEN v_row.confmatchtype = 'f' THEN v_sql := v_sql || ' MATCH FULL '; WHEN v_row.confmatchtype = 's' THEN v_sql := v_sql || ' MATCH SIMPLE '; WHEN v_row.confmatchtype = 'p' THEN v_sql := v_sql || ' MATCH PARTIAL '; END CASE; CASE WHEN v_row.confupdtype = 'a' THEN v_sql := v_sql || ' ON UPDATE NO ACTION '; WHEN v_row.confupdtype = 'r' THEN v_sql := v_sql || ' ON UPDATE RESTRICT '; WHEN v_row.confupdtype = 'c' THEN v_sql := v_sql || ' ON UPDATE CASCADE '; WHEN v_row.confupdtype = 'n' THEN v_sql := v_sql || ' ON UPDATE SET NULL '; WHEN v_row.confupdtype = 'd' THEN v_sql := v_sql || ' ON UPDATE SET DEFAULT '; END CASE; CASE WHEN v_row.confdeltype = 'a' THEN v_sql := v_sql || ' ON DELETE NO ACTION '; WHEN v_row.confdeltype = 'r' THEN v_sql := v_sql || ' ON DELETE RESTRICT '; WHEN v_row.confdeltype = 'c' THEN v_sql := v_sql || ' ON DELETE CASCADE '; WHEN v_row.confdeltype = 'n' THEN v_sql := v_sql || ' ON DELETE SET NULL '; WHEN v_row.confdeltype = 'd' THEN v_sql := v_sql || ' ON DELETE SET DEFAULT '; END CASE; CASE WHEN v_row.condeferrable = true AND v_row.condeferred = true THEN v_sql := v_sql || ' DEFERRABLE INITIALLY DEFERRED '; WHEN v_row.condeferrable = false AND v_row.condeferred = false THEN v_sql := v_sql || ' NOT DEFERRABLE '; WHEN v_row.condeferrable = true AND v_row.condeferred = false THEN v_sql := v_sql || ' DEFERRABLE INITIALLY IMMEDIATE '; END CASE; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Applying FK: '||v_sql); END IF; EXECUTE v_sql; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'FK applied'); END IF; END LOOP; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN CREATE APPLYING FOREIGN KEYS: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Function to monitor for data getting inserted into parent tables managed by extension */ CREATE OR REPLACE FUNCTION check_parent() RETURNS SETOF @extschema@.check_parent_table LANGUAGE plpgsql STABLE SECURITY DEFINER AS $$ DECLARE v_count bigint = 0; v_sql text; v_tables record; v_trouble @extschema@.check_parent_table%rowtype; BEGIN FOR v_tables IN SELECT DISTINCT parent_table FROM @extschema@.part_config LOOP v_sql := 'SELECT count(1) AS n FROM ONLY '||v_tables.parent_table; EXECUTE v_sql INTO v_count; IF v_count > 0 THEN v_trouble.parent_table := v_tables.parent_table; v_trouble.count := v_count; RETURN NEXT v_trouble; END IF; v_count := 0; END LOOP; RETURN; END $$; /* * Create a partition set that is a subpartition of an already existing partition set. * Given the parent table of any current partition set, it will turn all existing children into parent tables of their own partition sets * using the configuration options given as parameters to this function. * Uses another config table that allows for turning all future child partitions into a new parent automatically. * To avoid logical complications and contention issues, ALL subpartitions must be maintained using run_maintenance(). * This means the automatic, trigger based partition creation for serial partitioning will not work if it is a subpartition. */ CREATE OR REPLACE FUNCTION create_sub_parent( p_top_parent text , p_control text , p_type text , p_interval text , p_constraint_cols text[] DEFAULT NULL , p_premake int DEFAULT 4 , p_start_partition text DEFAULT NULL , p_inherit_fk boolean DEFAULT true , p_jobmon boolean DEFAULT true , p_debug boolean DEFAULT false) RETURNS boolean LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_last_partition text; v_row record; v_row_last_part record; v_run_maint boolean; v_sql text; v_success boolean := false; v_top_type text; BEGIN SELECT use_run_maintenance INTO v_run_maint FROM @extschema@.part_config WHERE parent_table = p_top_parent; IF v_run_maint 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; ELSIF v_run_maint = false THEN RAISE EXCEPTION 'Any parent table that will be part of a sub-partitioned set (on any level) must have use_run_maintenance set to true in part_config table, even for serial partitioning. See documentation for more info.'; END IF; FOR v_row IN -- Loop through all current children to turn them into partitioned tables SELECT show_partitions AS child_table FROM @extschema@.show_partitions(p_top_parent) LOOP -- Just call existing create_parent() function but add the given parameters to the part_config_sub table as well v_sql := format('SELECT @extschema@.create_parent( p_parent_table := %L , p_control := %L , p_type := %L , p_interval := %L , p_constraint_cols := %L , p_premake := %L , p_use_run_maintenance := %L , p_start_partition := %L , p_inherit_fk := %L , p_jobmon := %L , p_debug := %L )' , v_row.child_table , p_control , p_type , p_interval , p_constraint_cols , p_premake , true , p_start_partition , p_inherit_fk , p_jobmon , p_debug); EXECUTE v_sql; END LOOP; INSERT INTO @extschema@.part_config_sub ( sub_parent , sub_control , sub_partition_type , sub_partition_interval , sub_constraint_cols , sub_premake , sub_inherit_fk , sub_use_run_maintenance , sub_jobmon) VALUES ( p_top_parent , p_control , p_type , p_interval , p_constraint_cols , p_premake , p_inherit_fk , true , p_jobmon); v_success := true; RETURN v_success; END $$; /* * Drop constraints managed by pg_partman */ CREATE OR REPLACE FUNCTION drop_constraints(p_parent_table text, p_child_table text, p_debug boolean DEFAULT false) RETURNS void LANGUAGE plpgsql AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_col text; v_constraint_cols text[]; v_existing_constraint_name text; v_exists boolean := FALSE; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_sql text; v_step_id bigint; BEGIN SELECT constraint_cols , jobmon INTO v_constraint_cols , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_constraint_cols IS NULL THEN RAISE EXCEPTION 'Given parent table (%) not set up for constraint management (constraint_cols is NULL)', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN DROP CONSTRAINT: '||p_parent_table); v_step_id := add_step(v_job_id, 'Entering constraint drop loop'); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; FOREACH v_col IN ARRAY v_constraint_cols LOOP SELECT c.conname INTO v_existing_constraint_name FROM pg_catalog.pg_constraint c JOIN pg_catalog.pg_attribute a ON c.conrelid = a.attrelid WHERE conrelid = p_child_table::regclass AND c.conname LIKE 'partmanconstr_%' AND c.contype = 'c' AND a.attname = v_col AND ARRAY[a.attnum] <@ c.conkey AND a.attisdropped = false; IF v_existing_constraint_name IS NOT NULL THEN v_exists := TRUE; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Dropping constraint on column: '||v_col); END IF; v_sql := 'ALTER TABLE '||p_child_table||' DROP CONSTRAINT '||v_existing_constraint_name; IF p_debug THEN RAISE NOTICE 'Constraint drop query: %', v_sql; END IF; EXECUTE v_sql; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Drop constraint query: '||v_sql); END IF; END IF; END LOOP; IF v_jobmon_schema IS NOT NULL AND v_exists IS FALSE THEN v_step_id := add_step(v_job_id, 'No constraints found to drop on child table: '||p_child_table); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN DROP CONSTRAINT: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * 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. */ CREATE OR REPLACE FUNCTION 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 SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_adv_lock boolean; v_child_table text; v_control text; v_drop_count int := 0; v_id_position int; v_index record; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_max bigint; v_old_search_path text; v_partition_interval bigint; v_partition_id bigint; v_retention bigint; v_retention_keep_index boolean; v_retention_keep_table boolean; v_retention_schema text; v_row_max_id record; v_step_id bigint; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman drop_partition_id')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'drop_partition_id already running.'; RETURN 0; END IF; -- Allow override of configuration options IF p_retention IS NULL THEN SELECT partition_interval::bigint , control , retention::bigint , retention_keep_table , retention_keep_index , retention_schema , jobmon INTO v_partition_interval , v_control , v_retention , v_retention_keep_table , v_retention_keep_index , v_retention_schema , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND partition_type = 'id' 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_interval::bigint , control , retention_keep_table , retention_keep_index , retention_schema , jobmon INTO v_partition_interval , v_control , v_retention_keep_table , v_retention_keep_index , v_retention_schema , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND partition_type = 'id'; v_retention := p_retention; IF v_partition_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF p_keep_table IS NOT NULL THEN v_retention_keep_table = p_keep_table; END IF; IF p_keep_index IS NOT NULL THEN v_retention_keep_index = p_keep_index; END IF; IF p_retention_schema IS NOT NULL THEN v_retention_schema = p_retention_schema; 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 show_partitions FROM @extschema@.show_partitions(p_parent_table, 'DESC') LOOP EXECUTE 'SELECT max('||v_control||') FROM '||v_row_max_id.show_partitions INTO v_max; IF v_max IS NOT NULL THEN EXIT; END IF; END LOOP; -- Loop through child tables of the given parent FOR v_child_table IN SELECT n.nspname||'.'||c.relname FROM pg_inherits i join pg_class c ON i.inhrelid = c.oid join pg_namespace n ON c.relnamespace = n.oid WHERE i.inhparent::regclass = p_parent_table::regclass ORDER BY i.inhrelid ASC LOOP v_id_position := (length(v_child_table) - position('p_' in reverse(v_child_table))) + 2; v_partition_id := substring(v_child_table from v_id_position)::bigint; -- Add one interval since partition names contain the start of the constraint period IF v_retention <= (v_max - (v_partition_id + v_partition_interval)) THEN -- Only create a jobmon entry if there's actual retention work done IF v_jobmon_schema IS NOT NULL AND v_job_id IS NULL THEN v_job_id := add_job('PARTMAN DROP ID PARTITION: '|| p_parent_table); END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Uninherit table '||v_child_table||' from '||p_parent_table); END IF; EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; 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, 'Drop table '||v_child_table); END IF; EXECUTE 'DROP TABLE '||v_child_table||' CASCADE'; 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 FOR v_index IN SELECT i.indexrelid::regclass AS name , c.conname FROM pg_catalog.pg_index i LEFT JOIN pg_catalog.pg_constraint c ON i.indexrelid = c.conindid WHERE i.indrelid = v_child_table::regclass LOOP IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Drop index '||v_index.name||' from '||v_child_table); END IF; IF v_index.conname IS NOT NULL THEN EXECUTE 'ALTER TABLE '||v_child_table||' DROP CONSTRAINT '||v_index.conname; ELSE EXECUTE 'DROP INDEX '||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; ELSE -- Move to new schema IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Moving table '||v_child_table||' to schema '||v_retention_schema); END IF; EXECUTE 'ALTER TABLE '||v_child_table||' SET SCHEMA '||v_retention_schema; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; -- End retention schema if -- If child table is a subpartition, remove it from part_config & part_config_sub (should cascade due to FK) DELETE FROM @extschema@.part_config WHERE parent_table = v_child_table; v_drop_count := v_drop_count + 1; END IF; -- End retention check IF END LOOP; -- End child table loop IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Finished partition drop maintenance'); PERFORM update_step(v_step_id, 'OK', v_drop_count||' partitions dropped.'); PERFORM close_job(v_job_id); END IF; EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_drop_count; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN DROP ID PARTITION: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Function to drop child tables from a time-based partition set. * Options to move table to different schema, drop only indexes or actually drop the table from the database. */ CREATE OR REPLACE FUNCTION 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) RETURNS int LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_adv_lock boolean; v_child_table text; v_datetime_string text; v_drop_count int := 0; v_index record; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_partition_interval interval; v_partition_timestamp timestamp; v_quarter text; v_retention interval; v_retention_keep_index boolean; v_retention_keep_table boolean; v_retention_schema text; v_step_id bigint; v_time_position int; v_type text; v_year text; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman drop_partition_time')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'drop_partition_time already running.'; RETURN 0; END IF; -- Allow override of configuration options IF p_retention IS NULL THEN SELECT partition_type , partition_interval::interval , retention::interval , retention_keep_table , retention_keep_index , datetime_string , retention_schema , jobmon INTO v_type , v_partition_interval , v_retention , v_retention_keep_table , v_retention_keep_index , v_datetime_string , v_retention_schema , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (partition_type = 'time' OR partition_type = 'time-custom') 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 , retention_keep_table , retention_keep_index , datetime_string , retention_schema , jobmon INTO v_type , v_partition_interval , v_retention_keep_table , v_retention_keep_index , v_datetime_string , v_retention_schema , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (partition_type = 'time' OR partition_type = 'time-custom'); v_retention := p_retention; IF v_partition_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF p_keep_table IS NOT NULL THEN v_retention_keep_table = p_keep_table; END IF; IF p_keep_index IS NOT NULL THEN v_retention_keep_index = p_keep_index; END IF; IF p_retention_schema IS NOT NULL THEN v_retention_schema = p_retention_schema; END IF; -- Loop through child tables of the given parent FOR v_child_table IN SELECT n.nspname||'.'||c.relname FROM pg_inherits i join pg_class c ON i.inhrelid = c.oid join pg_namespace n ON c.relnamespace = n.oid WHERE i.inhparent::regclass = p_parent_table::regclass ORDER BY i.inhrelid ASC LOOP -- pull out datetime portion of partition's tablename to make the next one v_time_position := (length(v_child_table) - position('p_' in reverse(v_child_table))) + 2; IF v_partition_interval <> '3 months' OR (v_partition_interval = '3 months' AND v_type = 'time-custom') THEN v_partition_timestamp := to_timestamp(substring(v_child_table from v_time_position), v_datetime_string); ELSE -- to_timestamp doesn't recognize 'Q' date string formater. Handle it v_year := split_part(substring(v_child_table from v_time_position), 'q', 1); v_quarter := split_part(substring(v_child_table from v_time_position), 'q', 2); CASE WHEN v_quarter = '1' THEN v_partition_timestamp := to_timestamp(v_year || '-01-01', 'YYYY-MM-DD'); WHEN v_quarter = '2' THEN v_partition_timestamp := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); WHEN v_quarter = '3' THEN v_partition_timestamp := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); WHEN v_quarter = '4' THEN v_partition_timestamp := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); END CASE; END IF; -- Add one interval since partition names contain the start of the constraint period IF v_retention < (CURRENT_TIMESTAMP - (v_partition_timestamp + v_partition_interval)) THEN -- Only create a jobmon entry if there's actual retention work done IF v_jobmon_schema IS NOT NULL AND v_job_id IS NULL THEN v_job_id := add_job('PARTMAN DROP TIME PARTITION: '|| p_parent_table); END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Uninherit table '||v_child_table||' from '||p_parent_table); END IF; EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; IF v_type = 'time-custom' THEN DELETE FROM @extschema@.custom_time_partitions WHERE parent_table = p_parent_table AND child_table = v_child_table; 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, 'Drop table '||v_child_table); END IF; EXECUTE 'DROP TABLE '||v_child_table||' CASCADE'; 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 FOR v_index IN SELECT i.indexrelid::regclass AS name , c.conname FROM pg_catalog.pg_index i LEFT JOIN pg_catalog.pg_constraint c ON i.indexrelid = c.conindid WHERE i.indrelid = v_child_table::regclass LOOP IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Drop index '||v_index.name||' from '||v_child_table); END IF; IF v_index.conname IS NOT NULL THEN EXECUTE 'ALTER TABLE '||v_child_table||' DROP CONSTRAINT '||v_index.conname; ELSE EXECUTE 'DROP INDEX '||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; ELSE -- Move to new schema IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Moving table '||v_child_table||' to schema '||v_retention_schema); END IF; EXECUTE 'ALTER TABLE '||v_child_table||' SET SCHEMA '||v_retention_schema; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; -- End retention schema if -- If child table is a subpartition, remove it from part_config & part_config_sub (should cascade due to FK) DELETE FROM @extschema@.part_config WHERE parent_table = v_child_table; v_drop_count := v_drop_count + 1; END IF; -- End retention check IF END LOOP; -- End child table loop IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Finished partition drop maintenance'); PERFORM update_step(v_step_id, 'OK', v_drop_count||' partitions dropped.'); PERFORM close_job(v_job_id); END IF; EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_drop_count; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN DROP TIME PARTITION: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Populate the child table(s) of an id-based partition set with old data from the original parent */ CREATE OR REPLACE FUNCTION partition_data_id(p_parent_table text, p_batch_count int DEFAULT 1, p_batch_interval int DEFAULT NULL, p_lock_wait numeric DEFAULT 0, p_order text DEFAULT 'ASC') RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_control text; v_current_partition_name text; 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_rowcount bigint; v_sql text; v_start_control bigint; v_total_rows bigint := 0; BEGIN SELECT partition_interval::bigint , control INTO v_partition_interval , v_control FROM @extschema@.part_config WHERE parent_table = p_parent_table AND partition_type = 'id'; IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF p_batch_interval IS NULL OR p_batch_interval > v_partition_interval THEN p_batch_interval := v_partition_interval; END IF; FOR i IN 1..p_batch_count LOOP IF p_order = 'ASC' THEN EXECUTE 'SELECT min('||v_control||') FROM ONLY '||p_parent_table INTO v_start_control; IF v_start_control IS NULL THEN EXIT; END IF; v_min_partition_id = v_start_control - (v_start_control % v_partition_interval); v_partition_id := ARRAY[v_min_partition_id]; -- Check if custom batch interval overflows current partition maximum IF (v_start_control + p_batch_interval) >= (v_min_partition_id + v_partition_interval) THEN v_max_partition_id := v_min_partition_id + v_partition_interval; ELSE v_max_partition_id := v_start_control + p_batch_interval; END IF; ELSIF p_order = 'DESC' THEN EXECUTE 'SELECT max('||v_control||') FROM ONLY '||p_parent_table INTO v_start_control; IF v_start_control IS NULL THEN EXIT; END IF; v_min_partition_id = v_start_control - (v_start_control % v_partition_interval); -- Must be greater than max value still in parent table since query below grabs < max v_max_partition_id := v_min_partition_id + v_partition_interval; v_partition_id := ARRAY[v_min_partition_id]; -- Make sure minimum doesn't underflow current partition minimum IF (v_start_control - p_batch_interval) >= v_min_partition_id THEN v_min_partition_id = v_start_control - p_batch_interval; END IF; ELSE RAISE EXCEPTION 'Invalid value for p_order. Must be ASC or DESC'; END IF; -- do some locking with timeout, if required IF p_lock_wait > 0 THEN v_lock_iter := 0; WHILE v_lock_iter <= 5 LOOP v_lock_iter := v_lock_iter + 1; BEGIN v_sql := 'SELECT * FROM ONLY ' || p_parent_table || ' WHERE '||v_control||' >= '||quote_literal(v_min_partition_id)|| ' AND '||v_control||' < '||quote_literal(v_max_partition_id) ||' FOR UPDATE NOWAIT'; EXECUTE v_sql; 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 RETURN -1; END IF; END IF; PERFORM @extschema@.create_partition_id(p_parent_table, v_partition_id); SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname||'.'||tablename = p_parent_table; v_current_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_min_partition_id::text, TRUE); EXECUTE 'WITH partition_data AS ( DELETE FROM ONLY '||p_parent_table||' WHERE '||v_control||' >= '||v_min_partition_id|| ' AND '||v_control||' < '||v_max_partition_id||' RETURNING *) INSERT INTO '||v_current_partition_name||' SELECT * FROM partition_data'; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total_rows := v_total_rows + v_rowcount; IF v_rowcount = 0 THEN EXIT; END IF; END LOOP; PERFORM @extschema@.create_function_id(p_parent_table); RETURN v_total_rows; END $$; /* * Populate the child table(s) of a time-based partition set with old data from the original parent */ CREATE OR REPLACE FUNCTION 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') RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_control text; v_datetime_string text; v_current_partition_name text; v_max_partition_timestamp timestamp; v_last_partition text; v_lock_iter int := 1; v_lock_obtained boolean := FALSE; v_min_partition_timestamp timestamp; v_parent_schema text; v_parent_tablename text; v_partition_interval interval; v_partition_suffix text; v_partition_timestamp timestamp[]; v_quarter text; v_rowcount bigint; v_sql text; v_start_control timestamp; v_time_position int; v_total_rows bigint := 0; v_type text; v_year text; BEGIN SELECT partition_type , partition_interval::interval , control , datetime_string INTO v_type , v_partition_interval , v_control , v_datetime_string FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (partition_type = 'time' OR partition_type = 'time-custom'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF p_batch_interval IS NULL OR p_batch_interval > v_partition_interval THEN p_batch_interval := v_partition_interval; END IF; SELECT show_partitions INTO v_last_partition FROM @extschema@.show_partitions(p_parent_table, 'DESC') LIMIT 1; FOR i IN 1..p_batch_count LOOP IF p_order = 'ASC' THEN EXECUTE 'SELECT min('||v_control||') FROM ONLY '||p_parent_table INTO v_start_control; ELSIF p_order = 'DESC' THEN EXECUTE 'SELECT max('||v_control||') FROM ONLY '||p_parent_table INTO v_start_control; ELSE RAISE EXCEPTION 'Invalid value for p_order. Must be ASC or DESC'; END IF; IF v_start_control IS NULL THEN EXIT; END IF; IF v_type = 'time' 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_type = 'time-custom' THEN -- Keep going backwards, checking if the time interval encompases the current v_start_control value v_time_position := (length(v_last_partition) - position('p_' in reverse(v_last_partition))) + 2; v_min_partition_timestamp := to_timestamp(substring(v_last_partition from v_time_position), v_datetime_string); 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 v_max_partition_timestamp := v_min_partition_timestamp; BEGIN v_min_partition_timestamp := v_min_partition_timestamp - v_partition_interval; 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_interval; END; END IF; END LOOP; END IF; v_partition_timestamp := ARRAY[v_min_partition_timestamp]; IF p_order = 'ASC' THEN IF (v_start_control + p_batch_interval) >= (v_min_partition_timestamp + v_partition_interval) THEN v_max_partition_timestamp := v_min_partition_timestamp + v_partition_interval; ELSE v_max_partition_timestamp := v_start_control + p_batch_interval; END IF; ELSIF p_order = 'DESC' THEN -- Must be greater than max value still in parent table since query below grabs < max v_max_partition_timestamp := v_min_partition_timestamp + v_partition_interval; -- Make sure minimum doesn't underflow current partition minimum IF (v_start_control - p_batch_interval) >= v_min_partition_timestamp THEN v_min_partition_timestamp = v_start_control - p_batch_interval; END IF; ELSE RAISE EXCEPTION 'Invalid value for p_order. Must be ASC or DESC'; END IF; -- do some locking with timeout, if required IF p_lock_wait > 0 THEN v_lock_iter := 0; WHILE v_lock_iter <= 5 LOOP v_lock_iter := v_lock_iter + 1; BEGIN v_sql := 'SELECT * FROM ONLY ' || p_parent_table || ' WHERE '||v_control||' >= '||quote_literal(v_min_partition_timestamp)|| ' AND '||v_control||' < '||quote_literal(v_max_partition_timestamp) ||' FOR UPDATE NOWAIT'; EXECUTE v_sql; 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 RETURN -1; END IF; END IF; PERFORM @extschema@.create_partition_time(p_parent_table, v_partition_timestamp); -- This suffix generation code is in create_partition_time() as well v_partition_suffix := to_char(v_min_partition_timestamp, 'YYYY'); IF v_partition_interval < '1 year' AND v_partition_interval <> '1 week' THEN v_partition_suffix := v_partition_suffix ||'_'|| to_char(v_min_partition_timestamp, 'MM'); IF v_partition_interval < '1 month' AND v_partition_interval <> '1 week' THEN v_partition_suffix := v_partition_suffix ||'_'|| to_char(v_min_partition_timestamp, 'DD'); IF v_partition_interval < '1 day' THEN v_partition_suffix := v_partition_suffix || '_' || to_char(v_min_partition_timestamp, 'HH24MI'); IF v_partition_interval < '1 minute' THEN v_partition_suffix := v_partition_suffix || to_char(v_min_partition_timestamp, 'SS'); END IF; -- end < minute IF END IF; -- end < day IF END IF; -- end < month IF END IF; -- end < year IF IF v_partition_interval = '1 week' THEN v_partition_suffix := to_char(v_min_partition_timestamp, 'IYYY') || 'w' || to_char(v_min_partition_timestamp, 'IW'); END IF; -- "Q" is ignored in to_timestamp, so handle special case IF v_partition_interval = '3 months' AND (v_type = 'time') THEN v_year := to_char(v_min_partition_timestamp, 'YYYY'); v_quarter := to_char(v_min_partition_timestamp, 'Q'); v_partition_suffix := v_year || 'q' || v_quarter; END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname||'.'||tablename = p_parent_table; v_current_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_partition_suffix, TRUE); EXECUTE 'WITH partition_data AS ( DELETE FROM ONLY '||p_parent_table||' WHERE '||v_control||' >= '||quote_literal(v_min_partition_timestamp)|| ' AND '||v_control||' < '||quote_literal(v_max_partition_timestamp)||' RETURNING *) INSERT INTO '||v_current_partition_name||' SELECT * FROM partition_data'; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total_rows := v_total_rows + v_rowcount; IF v_rowcount = 0 THEN EXIT; END IF; END LOOP; PERFORM @extschema@.create_function_time(p_parent_table); RETURN v_total_rows; END $$; /* * Function to re-apply ownership & privileges on all child tables in a partition set using parent table as reference */ CREATE OR REPLACE FUNCTION reapply_privileges(p_parent_table text) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_all text[] := ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER']; v_child_owner text; v_child_table text; v_child_grant record; v_grant text; v_grantees text[]; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_match boolean; v_old_search_path text; v_parent_owner text; v_owner_sql text; v_revoke text[]; v_parent_grant record; v_sql text; v_step_id bigint; BEGIN SELECT jobmon INTO v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon IS NULL THEN RAISE EXCEPTION 'Given table is not managed by this extention: %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN RE-APPLYING PRIVILEGES TO ALL CHILD TABLES OF: '||p_parent_table); v_step_id := add_step(v_job_id, 'Setting new child table privileges'); END IF; SELECT tableowner INTO v_parent_owner FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; FOR v_child_table IN SELECT n.nspname||'.'||c.relname FROM pg_inherits i join pg_class c ON i.inhrelid = c.oid join pg_namespace n ON c.relnamespace = n.oid WHERE i.inhparent::regclass = p_parent_table::regclass ORDER BY i.inhrelid ASC LOOP IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'PENDING', 'Currently on child partition in ascending order: '||v_child_table); END IF; v_grantees := NULL; FOR v_parent_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = p_parent_table GROUP BY grantee LOOP -- Compare parent & child grants. Don't re-apply if it already exists v_match := false; FOR v_child_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = v_child_table GROUP BY grantee LOOP IF v_parent_grant.types = v_child_grant.types AND v_parent_grant.grantee = v_child_grant.grantee THEN v_match := true; END IF; END LOOP; IF v_match = false THEN EXECUTE 'GRANT '||array_to_string(v_parent_grant.types, ',')||' ON '||v_child_table||' TO '||v_parent_grant.grantee; SELECT array_agg(r) INTO v_revoke FROM (SELECT unnest(v_all) AS r EXCEPT SELECT unnest(v_parent_grant.types)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE '||array_to_string(v_revoke, ',')||' ON '||v_child_table||' FROM '||v_parent_grant.grantee||' CASCADE'; END IF; END IF; v_grantees := array_append(v_grantees, v_parent_grant.grantee::text); END LOOP; -- Revoke all privileges from roles that have none on the parent IF v_grantees IS NOT NULL THEN SELECT array_agg(r) INTO v_revoke FROM ( SELECT DISTINCT grantee::text AS r FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = v_child_table EXCEPT SELECT unnest(v_grantees)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE ALL ON '||v_child_table||' FROM '||array_to_string(v_revoke, ','); END IF; END IF; SELECT tableowner INTO v_child_owner FROM pg_tables WHERE schemaname ||'.'|| tablename = v_child_table; IF v_parent_owner <> v_child_owner THEN EXECUTE 'ALTER TABLE '||v_child_table||' OWNER TO '||v_parent_owner; END IF; END LOOP; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); PERFORM close_job(v_job_id); END IF; IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN RE-APPLYING PRIVILEGES TO ALL CHILD TABLES OF: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Function to manage pre-creation of the next partitions in a set. * 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_run_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. * Be aware that constraint exclusion may not work properly until an analyze on the partition set is run. */ CREATE OR REPLACE FUNCTION run_maintenance(p_parent_table text DEFAULT NULL, p_analyze boolean DEFAULT true, p_jobmon boolean DEFAULT true) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_adv_lock boolean; v_create_count int := 0; v_current_partition text; v_current_partition_id bigint; v_current_partition_timestamp timestamp; v_datetime_string text; v_drop_count int := 0; v_id_position int; 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 timestamp; v_next_partition_id bigint; v_next_partition_timestamp timestamp; v_old_search_path text; v_premade_count int; v_quarter text; v_row record; v_row_max_id record; v_row_sub record; v_step_id bigint; v_step_overflow_id bigint; v_step_serial_id bigint; v_sub_id_max bigint; v_sub_id_min bigint; v_sub_parent text; v_sub_timestamp_max timestamp; v_sub_timestamp_min timestamp; v_tablename text; v_tables_list_sql text; v_time_position int; v_year text; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman run_maintenance')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'Partman maintenance already running.'; RETURN; END IF; IF p_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN RUN MAINTENANCE'); v_step_id := add_step(v_job_id, 'Running maintenance loop'); END IF; v_tables_list_sql := 'SELECT parent_table , partition_type , partition_interval , control , premake , datetime_string , undo_in_progress FROM @extschema@.part_config'; IF p_parent_table IS NULL THEN v_tables_list_sql := v_tables_list_sql || ' WHERE use_run_maintenance = true'; ELSE v_tables_list_sql := v_tables_list_sql || format(' WHERE parent_table = %L', p_parent_table); END IF; FOR v_row IN EXECUTE v_tables_list_sql LOOP CONTINUE WHEN v_row.undo_in_progress; SELECT show_partitions INTO v_last_partition FROM @extschema@.show_partitions(v_row.parent_table, 'DESC') LIMIT 1; IF v_row.partition_type = 'time' OR v_row.partition_type = 'time-custom' THEN IF v_row.partition_type = 'time' THEN CASE WHEN v_row.partition_interval::interval = '15 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0); WHEN v_row.partition_interval::interval = '30 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0); WHEN v_row.partition_interval::interval = '1 hour' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP); WHEN v_row.partition_interval::interval = '1 day' THEN v_current_partition_timestamp := date_trunc('day', CURRENT_TIMESTAMP); WHEN v_row.partition_interval::interval = '1 week' THEN v_current_partition_timestamp := date_trunc('week', CURRENT_TIMESTAMP); WHEN v_row.partition_interval::interval = '1 month' THEN v_current_partition_timestamp := date_trunc('month', CURRENT_TIMESTAMP); WHEN v_row.partition_interval::interval = '3 months' THEN v_current_partition_timestamp := date_trunc('quarter', CURRENT_TIMESTAMP); WHEN v_row.partition_interval::interval = '1 year' THEN v_current_partition_timestamp := date_trunc('year', CURRENT_TIMESTAMP); END CASE; ELSIF v_row.partition_type = 'time-custom' THEN SELECT child_table INTO v_current_partition FROM @extschema@.custom_time_partitions WHERE parent_table = v_row.parent_table AND partition_range @> CURRENT_TIMESTAMP; IF v_current_partition IS NULL THEN RAISE EXCEPTION 'Current time partition missing from custom_time_partitions config table for table % and timestamp %', CURRENT_TIMESTAMP, v_row.parent_table; END IF; v_time_position := (length(v_current_partition) - position('p_' in reverse(v_current_partition))) + 2; v_current_partition_timestamp := to_timestamp(substring(v_current_partition from v_time_position), v_row.datetime_string); END IF; -- Determine if this table is a child of a subpartition parent. If so, get limits of what child tables can be created based on parent suffix SELECT sub_min::timestamp, sub_max::timestamp INTO v_sub_timestamp_min, v_sub_timestamp_max FROM @extschema@.check_subpartition_limits(v_row.parent_table, 'time'); -- No need to run maintenance if it's outside the bounds of the top parent. IF v_sub_timestamp_min IS NOT NULL THEN IF v_current_partition_timestamp < v_sub_timestamp_min OR v_current_partition_timestamp > v_sub_timestamp_max THEN CONTINUE; END IF; END IF; v_time_position := (length(v_last_partition) - position('p_' in reverse(v_last_partition))) + 2; IF v_row.partition_interval::interval <> '3 months' OR (v_row.partition_interval::interval = '3 months' AND v_row.partition_type = 'time-custom') THEN v_last_partition_timestamp := to_timestamp(substring(v_last_partition from v_time_position), v_row.datetime_string); ELSE -- to_timestamp doesn't recognize 'Q' date string formater. Handle it v_year := split_part(substring(v_last_partition from v_time_position), 'q', 1); v_quarter := split_part(substring(v_last_partition from v_time_position), 'q', 2); CASE WHEN v_quarter = '1' THEN v_last_partition_timestamp := to_timestamp(v_year || '-01-01', 'YYYY-MM-DD'); WHEN v_quarter = '2' THEN v_last_partition_timestamp := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); WHEN v_quarter = '3' THEN v_last_partition_timestamp := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); WHEN v_quarter = '4' THEN v_last_partition_timestamp := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); END CASE; END IF; -- Check and see how many premade partitions there are. 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; -- 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 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 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', 'Child partition creation skippd for parent table '||v_partition_time); END IF; RAISE WARNING 'Attempted partition time interval is outside PostgreSQL''s supported time range. Child partition creation skipped for parent table %', v_row.parent_table; CONTINUE; END; v_last_partition_created := @extschema@.create_partition_time(v_row.parent_table, ARRAY[v_next_partition_timestamp], p_analyze); v_create_count := v_create_count + 1; PERFORM @extschema@.create_function_time(v_row.parent_table); -- Manage additonal constraints if set PERFORM @extschema@.apply_constraints(v_row.parent_table); v_premade_count = round(EXTRACT('epoch' FROM age(v_next_partition_timestamp, v_current_partition_timestamp)) / EXTRACT('epoch' FROM v_row.partition_interval::interval)); END LOOP; ELSIF v_row.partition_type = 'id' THEN -- 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 show_partitions FROM @extschema@.show_partitions(v_row.parent_table, 'DESC') LOOP EXECUTE 'SELECT '||v_row.control||' - ('||v_row.control||' % '||v_row.partition_interval::int||') FROM '||v_row_max_id.show_partitions||' WHERE '||v_row.control||' = (SELECT max('||v_row.control||') FROM '||v_row_max_id.show_partitions||')' INTO v_current_partition_id; IF v_current_partition_id IS NOT NULL THEN EXIT; END IF; END LOOP; -- Determine if this table is a child of a subpartition parent. If so, get limits to see if run_maintenance even needs to run for it. SELECT sub_min::bigint, sub_max::bigint INTO v_sub_id_min, v_sub_id_max FROM @extschema@.check_subpartition_limits(v_row.parent_table, 'id'); -- No need to run maintenance if it's outside the bounds of the top parent. IF v_sub_id_min IS NOT NULL THEN IF v_current_partition_id < v_sub_id_min OR v_current_partition_id > v_sub_id_max THEN CONTINUE; END IF; END IF; v_id_position := (length(v_last_partition) - position('p_' in reverse(v_last_partition))) + 2; v_last_partition_id = substring(v_last_partition from v_id_position)::bigint; v_next_partition_id := v_last_partition_id + v_row.partition_interval::bigint; WHILE ((v_next_partition_id - v_current_partition_id) / v_row.partition_interval::bigint) <= v_row.premake LOOP v_last_partition_created := @extschema@.create_partition_id(v_row.parent_table, ARRAY[v_next_partition_id], p_analyze); IF v_last_partition_created THEN PERFORM @extschema@.create_function_id(v_row.parent_table); PERFORM @extschema@.apply_constraints(v_row.parent_table); END IF; v_next_partition_id := v_next_partition_id + v_row.partition_interval::bigint; END LOOP; END IF; -- end main IF check for time or id END LOOP; -- end of creation loop -- Manage dropping old partitions if retention option is set FOR v_row IN SELECT parent_table FROM @extschema@.part_config WHERE retention IS NOT NULL AND undo_in_progress = false AND (partition_type = 'time' OR partition_type = 'time-custom') LOOP IF p_parent_table IS NULL THEN v_drop_count := v_drop_count + @extschema@.drop_partition_time(v_row.parent_table); ELSE -- Only run retention on table given in parameter IF p_parent_table <> v_row.parent_table THEN CONTINUE; ELSE v_drop_count := v_drop_count + @extschema@.drop_partition_time(v_row.parent_table); END IF; END IF; END LOOP; FOR v_row IN SELECT parent_table FROM @extschema@.part_config WHERE retention IS NOT NULL AND undo_in_progress = false AND partition_type = 'id' LOOP IF p_parent_table IS NULL THEN v_drop_count := v_drop_count + @extschema@.drop_partition_id(v_row.parent_table); ELSE -- Only run retention on table given in parameter IF p_parent_table <> v_row.parent_table THEN CONTINUE; ELSE v_drop_count := v_drop_count + @extschema@.drop_partition_id(v_row.parent_table); END IF; END IF; END LOOP; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Partition maintenance finished. '||v_create_count||' partitons made. '||v_drop_count||' partitions dropped.'); IF v_step_overflow_id IS NOT NULL OR v_step_serial_id IS NOT NULL THEN PERFORM fail_job(v_job_id); ELSE PERFORM close_job(v_job_id); END IF; EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN RUN MAINTENANCE'')', v_jobmon_schema) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Function to list all child partitions in a set. */ CREATE OR REPLACE FUNCTION show_partitions (p_parent_table text, p_order text DEFAULT 'ASC') RETURNS SETOF text LANGUAGE plpgsql STABLE SECURITY DEFINER AS $$ DECLARE v_datetime_string text; v_partition_interval text; v_type text; BEGIN IF p_order NOT IN ('ASC', 'DESC') THEN RAISE EXCEPTION 'p_order paramter must be one of the following values: ASC, DESC'; END IF; SELECT partition_type , partition_interval , datetime_string INTO v_type , v_partition_interval , v_datetime_string FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_type IN ('time', 'time-custom') THEN RETURN QUERY EXECUTE ' SELECT n.nspname::text ||''.''|| 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 = '||quote_literal(p_parent_table)||'::regclass ORDER BY to_timestamp(substring(c.relname from ((length(c.relname) - position(''p_'' in reverse(c.relname))) + 2) ), '||quote_literal(v_datetime_string)||') ' || p_order; ELSIF v_type = 'id' THEN RETURN QUERY EXECUTE ' SELECT n.nspname::text ||''.''|| 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 = '||quote_literal(p_parent_table)||'::regclass ORDER BY substring(c.relname from ((length(c.relname) - position(''p_'' in reverse(c.relname))) + 2) )::bigint ' || p_order; END IF; END $$; /* * Function to undo partitioning. * Will actually work on any parent/child table set, not just ones created by pg_partman. */ CREATE OR REPLACE FUNCTION undo_partition(p_parent_table text, p_batch_count int DEFAULT 1, p_keep_table boolean DEFAULT true, p_jobmon boolean DEFAULT true, p_lock_wait numeric DEFAULT 0) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_adv_lock boolean; v_batch_loop_count bigint := 0; v_child_count bigint; v_child_table text; v_copy_sql text; v_function_name text; v_job_id bigint; v_jobmon_schema text; v_lock_iter int := 1; v_lock_obtained boolean := FALSE; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_partition_interval interval; v_rowcount bigint; v_step_id bigint; v_total bigint := 0; v_trig_name text; v_undo_count int := 0; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman undo_partition')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'undo_partition already running.'; RETURN 0; END IF; IF p_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN UNDO PARTITIONING: '||p_parent_table); v_step_id := add_step(v_job_id, 'Undoing partitioning for table '||p_parent_table); END IF; -- Stops new time partitons 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; -- Stop data going into child tables and stop new id partitions from being made. SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; 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, v_parent_schema, '_part_trig_func', FALSE); SELECT tgname INTO v_trig_name FROM pg_catalog.pg_trigger t WHERE tgname = v_trig_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 'LOCK TABLE ONLY '||p_parent_table||' IN ACCESS EXCLUSIVE MODE NOWAIT'; 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'; RETURN -1; END IF; END IF; -- END p_lock_wait IF EXECUTE 'DROP TRIGGER IF EXISTS '||v_trig_name||' ON '||p_parent_table; END IF; -- END trigger IF v_lock_obtained := FALSE; -- reset for reuse later EXECUTE 'DROP FUNCTION IF EXISTS '||v_function_name||'()'; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Stopped partition creation process. Removed trigger & trigger function'); END IF; WHILE v_batch_loop_count < p_batch_count LOOP SELECT n.nspname||'.'||c.relname INTO v_child_table FROM pg_inherits i JOIN pg_class c ON i.inhrelid = c.oid JOIN pg_namespace n ON c.relnamespace = n.oid WHERE i.inhparent::regclass = p_parent_table::regclass ORDER BY i.inhrelid ASC; EXIT WHEN v_child_table IS NULL; EXECUTE 'SELECT count(*) FROM '||v_child_table INTO v_child_count; IF v_child_count = 0 THEN -- No rows left in this child table. Remove from partition set. -- lockwait timeout for table 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 'LOCK TABLE ONLY '||v_child_table||' IN ACCESS EXCLUSIVE MODE NOWAIT'; 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 child table for removal from partition set'; RETURN -1; END IF; END IF; -- END p_lock_wait IF v_lock_obtained := FALSE; -- reset for reuse later EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; IF p_keep_table = false THEN EXECUTE 'DROP TABLE '||v_child_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table DROPPED. Moved '||coalesce(v_rowcount, 0)||' rows to parent'); END IF; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table UNINHERITED, not DROPPED. Copied '||coalesce(v_rowcount, 0)||' rows to parent'); END IF; END IF; v_undo_count := v_undo_count + 1; CONTINUE; END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing child partition: '||v_child_table); END IF; -- do some locking with timeout, if required 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 'SELECT * FROM '|| v_child_table ||' FOR UPDATE NOWAIT'; 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 batch of rows to move'; RETURN -1; END IF; END IF; v_copy_sql := 'INSERT INTO '||p_parent_table||' SELECT * FROM '||v_child_table; EXECUTE v_copy_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total := v_total + v_rowcount; EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; IF p_keep_table = false THEN EXECUTE 'DROP TABLE '||v_child_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table DROPPED. Moved '||v_rowcount||' rows to parent'); END IF; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table UNINHERITED, not DROPPED. Copied '||v_rowcount||' rows to parent'); END IF; END IF; v_batch_loop_count := v_batch_loop_count + 1; v_undo_count := v_undo_count + 1; END LOOP; IF v_undo_count = 0 THEN -- FOR loop never ran, so there's no child tables left. DELETE FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing config from pg_partman (if it existed)'); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; RAISE NOTICE 'Copied % row(s) from % child table(s) to the parent: %', v_total, v_undo_count, p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Final stats'); PERFORM update_step(v_step_id, 'OK', 'Copied '||v_total||' row(s) from '||v_undo_count||' child table(s) to the parent'); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_total; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN UNDO PARTITIONING: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Function to undo id-based partitioning created by this extension */ CREATE OR REPLACE FUNCTION undo_partition_id(p_parent_table text, p_batch_count int DEFAULT 1, p_batch_interval bigint DEFAULT NULL, p_keep_table boolean DEFAULT true, p_lock_wait numeric DEFAULT 0) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_adv_lock boolean; v_batch_loop_count int := 0; v_child_loop_total bigint := 0; v_child_min bigint; v_child_table text; v_control text; v_exists int; v_function_name text; v_inner_loop_count int; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_lock_iter int := 1; v_lock_obtained boolean := FALSE; v_move_sql text; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_partition_interval bigint; v_row record; v_rowcount bigint; v_step_id bigint; v_sub_count int; v_trig_name text; v_total bigint := 0; v_undo_count int := 0; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman undo_partition_id')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'undo_partition_id already running.'; RETURN 0; END IF; SELECT partition_interval::bigint , control , jobmon INTO v_partition_interval , v_control , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND partition_type = 'id'; IF v_partition_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; -- Check if any child tables are themselves partitioned or part of an inheritance tree. Prevent undo at this level if so. -- Need to either lock child tables at all levels or handle the proper removal of triggers on all child tables first -- before multi-level undo can be performed safely. FOR v_row IN SELECT show_partitions AS child_table FROM @extschema@.show_partitions(p_parent_table) LOOP SELECT count(*) INTO v_sub_count FROM pg_catalog.pg_inherits WHERE inhparent::regclass = v_row.child_table::regclass; 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.child_table; END IF; END LOOP; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN UNDO PARTITIONING: '||p_parent_table); v_step_id := add_step(v_job_id, 'Undoing partitioning for table '||p_parent_table); END IF; IF p_batch_interval IS NULL THEN p_batch_interval := v_partition_interval; END IF; -- Stops new time partitons 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; -- Stop data going into child tables and stop new id partitions from being made. SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; 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, v_parent_schema, '_part_trig_func', FALSE); SELECT tgname INTO v_trig_name FROM pg_catalog.pg_trigger t WHERE tgname = v_trig_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 'LOCK TABLE ONLY '||p_parent_table||' IN ACCESS EXCLUSIVE MODE NOWAIT'; 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'; RETURN -1; END IF; END IF; -- END p_lock_wait IF EXECUTE 'DROP TRIGGER IF EXISTS '||v_trig_name||' ON '||p_parent_table; END IF; -- END trigger IF v_lock_obtained := FALSE; -- reset for reuse later EXECUTE 'DROP FUNCTION IF EXISTS '||v_function_name||'()'; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Stopped partition creation process. Removed trigger & trigger function'); END IF; <> WHILE v_batch_loop_count < p_batch_count LOOP SELECT n.nspname||'.'||c.relname INTO v_child_table FROM pg_inherits i JOIN pg_class c ON i.inhrelid = c.oid JOIN pg_namespace n ON c.relnamespace = n.oid WHERE i.inhparent::regclass = p_parent_table::regclass ORDER BY i.inhrelid ASC; EXIT WHEN v_child_table IS NULL; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing child partition: '||v_child_table); END IF; EXECUTE 'SELECT min('||v_control||') FROM '||v_child_table INTO v_child_min; IF v_child_min IS NULL THEN -- No rows left in this child table. Remove from partition set. -- lockwait timeout for table 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 'LOCK TABLE ONLY '||v_child_table||' IN ACCESS EXCLUSIVE MODE NOWAIT'; 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 child table for removal from partition set'; RETURN -1; END IF; END IF; -- END p_lock_wait IF v_lock_obtained := FALSE; -- reset for reuse later EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; IF p_keep_table = false THEN EXECUTE 'DROP TABLE '||v_child_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table DROPPED. Moved '||v_child_loop_total||' rows to parent'); END IF; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table UNINHERITED, not DROPPED. Moved '||v_child_loop_total||' rows to parent'); END IF; END IF; v_undo_count := v_undo_count + 1; CONTINUE outer_child_loop; END IF; v_inner_loop_count := 1; v_child_loop_total := 0; <> LOOP -- lockwait timeout for row batches 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 'SELECT * FROM ' || v_child_table || ' WHERE '||v_control||' <= '||quote_literal(v_child_min + (p_batch_interval * v_inner_loop_count)) ||' FOR UPDATE NOWAIT'; 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 batch of rows to move'; RETURN -1; END IF; END IF; -- Get everything from the current child minimum up to the multiples of the given interval v_move_sql := 'WITH move_data AS (DELETE FROM '||v_child_table|| ' WHERE '||v_control||' <= '||quote_literal(v_child_min + (p_batch_interval * v_inner_loop_count))||' RETURNING *) INSERT INTO '||p_parent_table||' SELECT * FROM move_data'; EXECUTE v_move_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total := v_total + v_rowcount; v_child_loop_total := v_child_loop_total + v_rowcount; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Moved '||v_child_loop_total||' rows to parent.'); END IF; EXIT inner_child_loop WHEN v_rowcount = 0; -- exit before loop incr if table is empty v_inner_loop_count := v_inner_loop_count + 1; v_batch_loop_count := v_batch_loop_count + 1; EXIT outer_child_loop WHEN v_batch_loop_count >= p_batch_count; -- Exit outer FOR loop if p_batch_count is reached END LOOP inner_child_loop; END LOOP outer_child_loop; IF v_batch_loop_count < p_batch_count THEN -- FOR loop never ran, so there's no child tables left. DELETE FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing config from pg_partman'); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; RAISE NOTICE 'Copied % row(s) to the parent. Removed % partitions.', v_total, v_undo_count; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Final stats'); PERFORM update_step(v_step_id, 'OK', 'Copied '||v_total||' row(s) to the parent. Removed '||v_undo_count||' partitions.'); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_total; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN UNDO PARTITIONING: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Function to undo time-based partitioning created by this extension */ CREATE OR REPLACE FUNCTION undo_partition_time(p_parent_table text, p_batch_count int DEFAULT 1, p_batch_interval interval DEFAULT NULL, p_keep_table boolean DEFAULT true, p_lock_wait numeric DEFAULT 0) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_adv_lock boolean; v_batch_loop_count int := 0; v_child_min timestamptz; v_child_loop_total bigint := 0; v_child_table text; v_control text; v_function_name text; v_inner_loop_count int; v_lock_iter int := 1; v_lock_obtained boolean := FALSE; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_move_sql text; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_partition_interval interval; v_row record; v_rowcount bigint; v_step_id bigint; v_sub_count int; v_total bigint := 0; v_trig_name text; v_type text; v_undo_count int := 0; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman undo_partition_time')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'undo_partition_time already running.'; RETURN 0; END IF; SELECT partition_type , partition_interval::interval , control , jobmon INTO v_type , v_partition_interval , v_control , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (partition_type = 'time' OR partition_type = 'time-custom'); IF v_partition_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; -- Check if any child tables are themselves partitioned or part of an inheritance tree. Prevent undo at this level if so. -- Need to either lock child tables at all levels or handle the proper removal of triggers on all child tables first -- before multi-level undo can be performed safely. FOR v_row IN SELECT show_partitions AS child_table FROM @extschema@.show_partitions(p_parent_table) LOOP SELECT count(*) INTO v_sub_count FROM pg_catalog.pg_inherits WHERE inhparent::regclass = v_row.child_table::regclass; 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.child_table; END IF; END LOOP; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN UNDO PARTITIONING: '||p_parent_table); v_step_id := add_step(v_job_id, 'Undoing partitioning for table '||p_parent_table); END IF; IF p_batch_interval IS NULL THEN p_batch_interval := v_partition_interval; END IF; -- Stops new time partitons 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; -- Stop data going into child tables. SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; 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, v_parent_schema, '_part_trig_func', FALSE); SELECT tgname INTO v_trig_name FROM pg_catalog.pg_trigger t WHERE tgname = v_trig_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 'LOCK TABLE ONLY '||p_parent_table||' IN ACCESS EXCLUSIVE MODE NOWAIT'; 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'; RETURN -1; END IF; END IF; -- END p_lock_wait IF EXECUTE 'DROP TRIGGER IF EXISTS '||v_trig_name||' ON '||p_parent_table; END IF; -- END trigger IF v_lock_obtained := FALSE; -- reset for reuse later EXECUTE 'DROP FUNCTION IF EXISTS '||v_function_name||'()'; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Stopped partition creation process. Removed trigger & trigger function'); END IF; <> WHILE v_batch_loop_count < p_batch_count LOOP SELECT n.nspname||'.'||c.relname INTO v_child_table FROM pg_inherits i JOIN pg_class c ON i.inhrelid = c.oid JOIN pg_namespace n ON c.relnamespace = n.oid WHERE i.inhparent::regclass = p_parent_table::regclass ORDER BY i.inhrelid ASC; EXIT WHEN v_child_table IS NULL; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing child partition: '||v_child_table); END IF; EXECUTE 'SELECT min('||v_control||') FROM '||v_child_table INTO v_child_min; IF v_child_min IS NULL THEN -- No rows left in this child table. Remove from partition set. -- lockwait timeout for table 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 'LOCK TABLE ONLY '||v_child_table||' IN ACCESS EXCLUSIVE MODE NOWAIT'; 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 child table for removal from partition set'; RETURN -1; END IF; END IF; -- END p_lock_wait IF v_lock_obtained := FALSE; -- reset for reuse later EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; IF p_keep_table = false THEN EXECUTE 'DROP TABLE '||v_child_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table DROPPED. Moved '||v_child_loop_total||' rows to parent'); END IF; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table UNINHERITED, not DROPPED. Moved '||v_child_loop_total||' rows to parent'); END IF; END IF; IF v_type = 'time-custom' THEN DELETE FROM @extschema@.custom_time_partitions WHERE parent_table = p_parent_table AND child_table = v_child_table; END IF; v_undo_count := v_undo_count + 1; CONTINUE outer_child_loop; END IF; v_inner_loop_count := 1; v_child_loop_total := 0; <> LOOP -- do some locking with timeout, if required 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 'SELECT * FROM ' || v_child_table || ' WHERE '||v_control||' <= '||quote_literal(v_child_min + (p_batch_interval * v_inner_loop_count)) ||' FOR UPDATE NOWAIT'; 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 batch of rows to move'; RETURN -1; END IF; END IF; -- Get everything from the current child minimum up to the multiples of the given interval v_move_sql := 'WITH move_data AS (DELETE FROM '||v_child_table|| ' WHERE '||v_control||' <= '||quote_literal(v_child_min + (p_batch_interval * v_inner_loop_count))||' RETURNING *) INSERT INTO '||p_parent_table||' SELECT * FROM move_data'; EXECUTE v_move_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total := v_total + v_rowcount; v_child_loop_total := v_child_loop_total + v_rowcount; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Moved '||v_child_loop_total||' rows to parent.'); END IF; EXIT inner_child_loop WHEN v_rowcount = 0; -- exit before loop incr if table is empty v_inner_loop_count := v_inner_loop_count + 1; v_batch_loop_count := v_batch_loop_count + 1; EXIT outer_child_loop WHEN v_batch_loop_count >= p_batch_count; -- Exit outer FOR loop if p_batch_count is reached END LOOP inner_child_loop; END LOOP outer_child_loop; IF v_batch_loop_count < p_batch_count THEN -- FOR loop never ran, so there's no child tables left. DELETE FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing config from pg_partman'); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; RAISE NOTICE 'Copied % row(s) to the parent. Removed % partitions.', v_total, v_undo_count; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Final stats'); PERFORM update_step(v_step_id, 'OK', 'Copied '||v_total||' row(s) to the parent. Removed '||v_undo_count||' partitions.'); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_total; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN UNDO PARTITIONING: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Check if parent table is a subpartition of an already existing id based partition set managed by pg_partman * If so, limit what child tables can be created based on parent suffix */ CREATE OR REPLACE FUNCTION check_subpartition_limits(p_parent_table text, p_type text, OUT sub_min text, OUT sub_max text) RETURNS record LANGUAGE plpgsql AS $$ DECLARE v_datetime_string text; v_id_position int; v_partition_interval interval; v_quarter text; v_sub_id_max bigint; v_sub_id_min bigint; v_sub_timestamp_max timestamp; v_sub_timestamp_min timestamp; v_time_position int; v_top_datetime_string text; v_top_interval text; v_top_parent text; v_top_type text; v_year text; BEGIN -- CTE query is done individually for each type (time, id) because it should return NULL if the top parent is not the same type in a subpartition set (id->time or time->id) IF p_type = 'id' THEN WITH top_oid AS ( SELECT i.inhparent AS top_parent_oid FROM pg_catalog.pg_class c JOIN pg_catalog.pg_inherits i ON c.oid = i.inhrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname||'.'||c.relname = p_parent_table ) SELECT n.nspname||'.'||c.relname, p.datetime_string, p.partition_interval, p.partition_type INTO v_top_parent, v_top_datetime_string, v_top_interval, v_top_type FROM pg_catalog.pg_class c JOIN top_oid t ON c.oid = t.top_parent_oid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace JOIN @extschema@.part_config p ON p.parent_table = n.nspname||'.'||c.relname WHERE c.oid = t.top_parent_oid AND p.partition_type = 'id'; IF v_top_parent IS NOT NULL THEN v_id_position := (length(p_parent_table) - position('p_' in reverse(p_parent_table))) + 2; v_sub_id_min := substring(p_parent_table from v_id_position)::bigint; v_sub_id_max := (v_sub_id_min + v_top_interval::bigint) - 1; sub_min := v_sub_id_min::text; sub_max := v_sub_id_max::text; END IF; ELSIF p_type = 'time' THEN WITH top_oid AS ( SELECT i.inhparent AS top_parent_oid FROM pg_catalog.pg_class c JOIN pg_catalog.pg_inherits i ON c.oid = i.inhrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname||'.'||c.relname = p_parent_table ) SELECT n.nspname||'.'||c.relname, p.datetime_string, p.partition_interval, p.partition_type INTO v_top_parent, v_top_datetime_string, v_top_interval, v_top_type FROM pg_catalog.pg_class c JOIN top_oid t ON c.oid = t.top_parent_oid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace JOIN @extschema@.part_config p ON p.parent_table = n.nspname||'.'||c.relname WHERE c.oid = t.top_parent_oid AND p.partition_type = 'time' OR p.partition_type = 'time-custom'; IF v_top_parent IS NOT NULL THEN v_time_position := (length(p_parent_table) - position('p_' in reverse(p_parent_table))) + 2; IF v_top_interval::interval <> '3 months' OR (v_top_interval::interval = '3 months' AND v_top_type = 'time-custom') THEN v_sub_timestamp_min := to_timestamp(substring(p_parent_table from v_time_position), v_top_datetime_string); ELSE -- to_timestamp doesn't recognize 'Q' date string formater. Handle it v_year := split_part(substring(p_parent_table from v_time_position), 'q', 1); v_quarter := split_part(substring(p_parent_table from v_time_position), 'q', 2); CASE WHEN v_quarter = '1' THEN v_sub_timestamp_min := to_timestamp(v_year || '-01-01', 'YYYY-MM-DD'); WHEN v_quarter = '2' THEN v_sub_timestamp_min := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); WHEN v_quarter = '3' THEN v_sub_timestamp_min := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); WHEN v_quarter = '4' THEN v_sub_timestamp_min := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); END CASE; END IF; v_sub_timestamp_max = (v_sub_timestamp_min + v_top_interval::interval) - '1 sec'::interval; sub_min := v_sub_timestamp_min::text; sub_max := v_sub_timestamp_max::text; END IF; ELSE RAISE EXCEPTION 'Invalid type given as parameter to check_subpartition_limits()'; END IF; RETURN; END $$; pg_partman-2.2.2/updates/pg_partman--1.8.8--2.0.0.sql000066400000000000000000005636401262146621700214470ustar00rootroot00000000000000-- ************* CRITICAL MESSAGE ************** -- This file is only a transitional upgrade file to get from 1.8.8 to 2.0.0. See the CHANGELOG file for all relevant changes. -- You MUST upgrade beyond this file/version to the latest version of the 2.x series. DO NOT continue running 2.0.0!!!! -- ************* CRITICAL MESSAGE ************** ALTER TABLE @extschema@.part_config RENAME COLUMN "type" TO partition_type; ALTER TABLE @extschema@.part_config_sub RENAME COLUMN "sub_type" to "sub_partition_type"; ALTER TABLE @extschema@.part_config RENAME COLUMN part_interval TO partition_interval; ALTER TABLE @extschema@.part_config_sub RENAME COLUMN sub_part_interval TO sub_partition_interval; --NOTE This function is in the table sql file /* * Ensure that sub-partitioned tables that are themselves sub-partitions have the same configuration options set when they are part of the same inheritance tree */ CREATE OR REPLACE FUNCTION @extschema@.check_subpart_sameconfig(text) RETURNS boolean LANGUAGE sql STABLE AS $$ WITH child_tables AS ( SELECT n.nspname||'.'||c.relname AS 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::regclass = $1::regclass ) SELECT CASE WHEN count(*) <= 1 THEN true WHEN count(*) > 1 THEN false END FROM ( 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_use_run_maintenance , sub_jobmon FROM @extschema@.part_config_sub a JOIN child_tables b on a.sub_parent = b.tablename) x; $$; /* * Check function for config table partition types */ CREATE OR REPLACE FUNCTION @extschema@.check_partition_type (p_type text) RETURNS boolean LANGUAGE plpgsql IMMUTABLE SECURITY DEFINER AS $$ DECLARE v_result boolean; BEGIN SELECT p_type IN ('time', 'time-custom', 'id') INTO v_result; RETURN v_result; END $$; UPDATE @extschema@.part_config SET partition_type = 'time' WHERE partition_type = 'time-static' OR partition_type = 'time-dynamic'; UPDATE @extschema@.part_config_sub SET sub_partition_type = 'time' WHERE sub_partition_type = 'time-static' OR sub_partition_type = 'time-dynamic'; UPDATE @extschema@.part_config_sub SET sub_partition_type = 'id' WHERE sub_partition_type = 'id-static' OR sub_partition_type = 'id-dynamic'; /* * Function to turn a table into the parent of a partition set */ CREATE OR REPLACE FUNCTION create_parent( p_parent_table text , p_control text , p_type text , p_interval text , p_constraint_cols text[] DEFAULT NULL , p_premake int DEFAULT 4 , p_use_run_maintenance boolean DEFAULT NULL , p_start_partition text DEFAULT NULL , p_inherit_fk boolean DEFAULT true , p_jobmon boolean DEFAULT true , p_debug boolean DEFAULT false) RETURNS boolean LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_base_timestamp timestamp; v_count int := 1; v_datetime_string text; v_higher_parent text := p_parent_table; v_id_interval bigint; v_id_position int; v_job_id bigint; v_jobmon_schema text; v_last_partition_created boolean; v_max bigint; v_notnull boolean; v_old_search_path text; v_parent_partition_id bigint; v_parent_partition_timestamp timestamp; v_partition_time timestamp; v_partition_time_array timestamp[]; v_partition_id_array bigint[]; v_row record; v_run_maint boolean; v_sql text; v_start_time timestamp; v_starting_partition_id bigint; v_step_id bigint; v_step_overflow_id bigint; v_sub_parent text; v_success boolean := false; v_tablename text; v_time_interval interval; v_time_position int; v_top_datetime_string text; v_top_parent text := p_parent_table; BEGIN IF position('.' in p_parent_table) = 0 THEN RAISE EXCEPTION 'Parent table must be schema qualified'; END IF; SELECT tablename INTO v_tablename FROM pg_tables WHERE schemaname || '.' || tablename = p_parent_table; IF v_tablename IS NULL THEN RAISE EXCEPTION 'Please create given parent table first: %', p_parent_table; END IF; SELECT attnotnull INTO v_notnull FROM pg_attribute WHERE attrelid = p_parent_table::regclass AND attname = p_control; IF v_notnull = false OR v_notnull IS NULL THEN RAISE EXCEPTION 'Control column (%) for parent table (%) must be NOT NULL', p_control, p_parent_table; END IF; IF NOT @extschema@.check_partition_type(p_type) THEN RAISE EXCEPTION '% is not a valid partitioning type', p_type; END IF; EXECUTE 'LOCK TABLE '||p_parent_table||' IN ACCESS EXCLUSIVE MODE'; IF p_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF p_use_run_maintenance IS NOT NULL THEN IF p_use_run_maintenance IS FALSE AND (p_type = 'time' OR p_type = 'time-custom') THEN RAISE EXCEPTION 'p_run_maintenance cannot be set to false for time based partitioning'; END IF; v_run_maint := p_use_run_maintenance; ELSIF p_type = 'time' OR p_type = 'time-custom' THEN v_run_maint := TRUE; ELSIF p_type = 'id' THEN v_run_maint := FALSE; ELSE RAISE EXCEPTION 'use_run_maintenance value cannot be set NULL'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN SETUP PARENT: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating initial partitions on new parent table: '||p_parent_table); 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 WITH parent_table AS ( SELECT h.inhparent as parent_oid from pg_inherits h where h.inhrelid::regclass = p_parent_table::regclass ), sibling_children as ( select i.inhrelid::regclass::text as tablename from pg_inherits i join parent_table p on i.inhparent = p.parent_oid ) 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_use_run_maintenance , sub_jobmon FROM @extschema@.part_config_sub a JOIN sibling_children b on a.sub_parent = b.tablename LIMIT 1 LOOP INSERT INTO @extschema@.part_config_sub ( sub_parent , 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_use_run_maintenance , sub_jobmon) VALUES ( p_parent_table , v_row.sub_partition_type , v_row.sub_control , 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_use_run_maintenance , v_row.sub_jobmon); END LOOP; IF p_type = 'time' OR p_type = 'time-custom' 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 <> 'time-custom' THEN RAISE EXCEPTION 'Must use a predefined time interval if not using type "time-custom". See documentation.'; 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; -- First partition is either the min premake or p_start_partition v_start_time := COALESCE(p_start_partition::timestamp, CURRENT_TIMESTAMP - (v_time_interval * p_premake)); 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 v_datetime_string := 'YYYY'; 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 v_partition_time_array := array_append(v_partition_time_array, v_base_timestamp); LOOP -- If current loop value is less than or equal to the value of the max premake, add time to array. IF (v_base_timestamp + (v_time_interval * v_count)) < (CURRENT_TIMESTAMP + (v_time_interval * p_premake)) THEN BEGIN v_partition_time := (v_base_timestamp + (v_time_interval * v_count))::timestamp; 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. 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'); CONTINUE; END; ELSE EXIT; -- all needed partitions added to array. Exit the loop. END IF; v_count := v_count + 1; END LOOP; INSERT INTO @extschema@.part_config ( parent_table , partition_type , partition_interval , control , premake , constraint_cols , datetime_string , use_run_maintenance , inherit_fk , jobmon) VALUES ( p_parent_table , p_type , v_time_interval , p_control , p_premake , p_constraint_cols , v_datetime_string , v_run_maint , p_inherit_fk , p_jobmon); v_last_partition_created := @extschema@.create_partition_time(p_parent_table, v_partition_time_array, false); 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 ( SELECT i.inhparent AS top_parent_oid FROM pg_catalog.pg_inherits i JOIN pg_catalog.pg_class c ON c.oid = i.inhrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname||'.'||c.relname = p_parent_table ) SELECT n.nspname||'.'||c.relname, p.datetime_string INTO v_top_parent, v_top_datetime_string 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 JOIN @extschema@.part_config p ON p.parent_table = n.nspname||'.'||c.relname; IF v_top_parent IS NOT NULL THEN -- If so create the lowest possible partition that is within the boundary of the parent v_time_position := (length(p_parent_table) - position('p_' in reverse(p_parent_table))) + 2; v_parent_partition_timestamp := to_timestamp(substring(p_parent_table from v_time_position), v_top_datetime_string); IF v_base_timestamp >= v_parent_partition_timestamp THEN WHILE v_base_timestamp >= v_parent_partition_timestamp LOOP v_base_timestamp := v_base_timestamp - v_time_interval; END LOOP; v_base_timestamp := v_base_timestamp + v_time_interval; -- add one back since while loop set it one lower than is needed ELSIF v_base_timestamp < v_parent_partition_timestamp THEN WHILE v_base_timestamp < v_parent_partition_timestamp LOOP v_base_timestamp := v_base_timestamp + v_time_interval; END LOOP; -- Don't need to remove one since new starting time will fit in top parent interval 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); ELSE -- Currently unknown edge case if code gets here RAISE EXCEPTION 'No child tables created. Unexpected edge case encountered. Please report this error to author with conditions that led to it.'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Time partitions premade: '||p_premake); END IF; END IF; IF p_type = 'id' THEN v_id_interval := p_interval::bigint; IF v_id_interval < 10 THEN RAISE EXCEPTION 'Interval for serial partitioning must be greater than or equal to 10'; END IF; -- Check if parent table is a subpartition of an already existing id partition set managed by pg_partman. WHILE v_higher_parent IS NOT NULL LOOP -- initially set in DECLARE WITH top_oid AS ( SELECT i.inhparent AS top_parent_oid FROM pg_catalog.pg_inherits i JOIN pg_catalog.pg_class c ON c.oid = i.inhrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname||'.'||c.relname = v_higher_parent ) SELECT n.nspname||'.'||c.relname INTO v_higher_parent 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 JOIN @extschema@.part_config p ON p.parent_table = n.nspname||'.'||c.relname WHERE p.partition_type = 'id'; IF v_higher_parent IS NOT NULL THEN -- v_top_parent initially set in DECLARE v_top_parent := v_higher_parent; END IF; END LOOP; -- If custom start partition is set, use that. -- If custom start is not set and there is already data, start partitioning with the highest current value and ensure it's grabbed from highest top parent table v_sql := 'SELECT COALESCE('||quote_nullable(p_start_partition::bigint)||', max('||p_control||')::bigint, 0) FROM '||v_top_parent||' LIMIT 1'; EXECUTE v_sql INTO v_max; 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 THEN v_partition_id_array = array_append(v_partition_id_array, (v_starting_partition_id - v_id_interval*i)); END IF; v_partition_id_array = array_append(v_partition_id_array, (v_id_interval*i) + v_starting_partition_id); END LOOP; INSERT INTO @extschema@.part_config ( parent_table , partition_type , partition_interval , control , premake , constraint_cols , use_run_maintenance , inherit_fk , jobmon) VALUES ( p_parent_table , p_type , v_id_interval , p_control , p_premake , p_constraint_cols , v_run_maint , p_inherit_fk , p_jobmon); v_last_partition_created := @extschema@.create_partition_id(p_parent_table, v_partition_id_array, false); 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 -- See if it's actually a subpartition of a parent id partition WITH top_oid AS ( SELECT i.inhparent AS top_parent_oid FROM pg_catalog.pg_inherits i JOIN pg_catalog.pg_class c ON c.oid = i.inhrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname||'.'||c.relname = p_parent_table ) SELECT n.nspname||'.'||c.relname INTO v_top_parent 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 JOIN @extschema@.part_config p ON p.parent_table = n.nspname||'.'||c.relname WHERE p.partition_type = 'id'; IF v_top_parent IS NOT NULL THEN -- Create the lowest possible partition that is within the boundary of the parent v_id_position := (length(p_parent_table) - position('p_' in reverse(p_parent_table))) + 2; v_parent_partition_id = substring(p_parent_table from v_id_position)::bigint; IF v_starting_partition_id >= v_parent_partition_id THEN WHILE v_starting_partition_id >= v_parent_partition_id LOOP v_starting_partition_id := v_starting_partition_id - v_id_interval; END LOOP; v_starting_partition_id := v_starting_partition_id + v_id_interval; -- add one back since while loop set it one lower than is needed ELSIF v_starting_partition_id < v_parent_partition_id THEN WHILE v_starting_partition_id < v_parent_partition_id LOOP v_starting_partition_id := v_starting_partition_id + v_id_interval; END LOOP; -- Don't need to remove one since new starting id will fit in top parent interval 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); ELSE -- Currently unknown edge case if code gets here RAISE EXCEPTION 'No child tables created. Unexpected edge case encountered. Please report this error to author with conditions that led to it.'; END IF; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Creating partition function'); END IF; IF p_type = 'time' OR p_type = 'time-custom' THEN PERFORM @extschema@.create_function_time(p_parent_table); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Time function created'); END IF; ELSIF p_type = 'id' THEN PERFORM @extschema@.create_function_id(p_parent_table); 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); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); IF v_step_overflow_id IS NOT NULL THEN PERFORM fail_job(v_job_id); ELSE PERFORM close_job(v_job_id); END IF; EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; v_success := true; RETURN v_success; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN CREATE PARENT: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''Partition creation for table '||p_parent_table||' failed'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Create the trigger function for the parent table of an id-based partition set */ CREATE OR REPLACE FUNCTION create_function_id(p_parent_table text) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_control text; v_count int; v_current_partition_name text; v_current_partition_id bigint; v_datetime_string text; v_final_partition_id bigint; v_function_name text; v_higher_parent text := p_parent_table; v_id_position int; v_job_id bigint; v_jobmon text; v_jobmon_schema text; v_last_partition text; v_max bigint; v_next_partition_id bigint; v_next_partition_name text; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_partition_interval bigint; v_premake int; v_prev_partition_id bigint; v_prev_partition_name text; v_row_max_id record; v_run_maint boolean; v_step_id bigint; v_top_parent text := p_parent_table; v_trig_func text; BEGIN SELECT partition_interval::bigint , control , premake , use_run_maintenance , jobmon INTO v_partition_interval , v_control , v_premake , v_run_maint , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND partition_type = 'id'; IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; SELECT show_partitions INTO v_last_partition FROM @extschema@.show_partitions(p_parent_table, 'DESC') LIMIT 1; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE FUNCTION: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating partition function for table '||p_parent_table); END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; v_function_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, '_part_trig_func', FALSE); -- Get the highest level top parent if multi-level partitioned in order to get proper max() value below WHILE v_higher_parent IS NOT NULL LOOP -- initially set in DECLARE WITH top_oid AS ( SELECT i.inhparent AS top_parent_oid FROM pg_catalog.pg_inherits i JOIN pg_catalog.pg_class c ON c.oid = i.inhrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname||'.'||c.relname = v_higher_parent ) SELECT n.nspname||'.'||c.relname INTO v_higher_parent 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 JOIN @extschema@.part_config p ON p.parent_table = n.nspname||'.'||c.relname WHERE p.partition_type = 'id'; IF v_higher_parent IS NOT NULL THEN -- initially set in DECLARE v_top_parent := v_higher_parent; END IF; END 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 parent. FOR v_row_max_id IN SELECT show_partitions FROM @extschema@.show_partitions(v_top_parent, 'DESC') LOOP EXECUTE 'SELECT max('||v_control||') FROM '||v_row_max_id.show_partitions INTO v_max; IF v_max IS NOT NULL THEN EXIT; END IF; END LOOP; IF v_max IS NULL THEN v_max := 0; END IF; v_current_partition_id = v_max - (v_max % v_partition_interval); v_next_partition_id := v_current_partition_id + v_partition_interval; v_current_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_current_partition_id::text, TRUE); v_trig_func := 'CREATE OR REPLACE FUNCTION '||v_function_name||'() RETURNS trigger LANGUAGE plpgsql AS $t$ DECLARE v_count int; v_current_partition_id bigint; v_current_partition_name text; v_id_position int; v_last_partition text := '||quote_literal(v_last_partition)||'; v_next_partition_id bigint; v_next_partition_name text; v_partition_created boolean; BEGIN IF TG_OP = ''INSERT'' THEN IF NEW.'||v_control||' >= '||v_current_partition_id||' AND NEW.'||v_control||' < '||v_next_partition_id|| ' THEN '; SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname ||'.'||tablename = v_current_partition_name; IF v_count > 0 THEN v_trig_func := v_trig_func || ' INSERT INTO '||v_current_partition_name||' VALUES (NEW.*); '; ELSE v_trig_func := v_trig_func || ' -- Child table for current values does not exist in this partition set, so write to parent RETURN NEW;'; END IF; FOR i IN 1..v_premake LOOP v_prev_partition_id := v_current_partition_id - (v_partition_interval * i); v_next_partition_id := v_current_partition_id + (v_partition_interval * i); v_final_partition_id := v_next_partition_id + v_partition_interval; v_prev_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_prev_partition_id::text, TRUE); v_next_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_next_partition_id::text, TRUE); -- Check that child table exist before making a rule to insert to them. -- Handles edge case of changing premake immediately after running create_parent(). SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname ||'.'||tablename = v_prev_partition_name; IF v_count > 0 THEN -- Only handle previous partitions if they're starting above zero IF v_prev_partition_id >= 0 THEN v_trig_func := v_trig_func ||' ELSIF NEW.'||v_control||' >= '||v_prev_partition_id||' AND NEW.'||v_control||' < '||v_prev_partition_id + v_partition_interval|| ' THEN INSERT INTO '||v_prev_partition_name||' VALUES (NEW.*); '; END IF; END IF; SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname ||'.'||tablename = v_next_partition_name; IF v_count > 0 THEN v_trig_func := v_trig_func ||' ELSIF NEW.'||v_control||' >= '||v_next_partition_id||' AND NEW.'||v_control||' < '||v_final_partition_id|| ' THEN INSERT INTO '||v_next_partition_name||' VALUES (NEW.*);'; END IF; END LOOP; v_trig_func := v_trig_func ||' ELSE v_current_partition_id := NEW.'||v_control||' - (NEW.'||v_control||' % '||v_partition_interval||'); v_current_partition_name := @extschema@.check_name_length('''||v_parent_tablename||''', '''||v_parent_schema||''', v_current_partition_id::text, TRUE); SELECT count(*) INTO v_count FROM pg_tables WHERE schemaname ||''.''|| tablename = v_current_partition_name; IF v_count > 0 THEN EXECUTE ''INSERT INTO ''||v_current_partition_name||'' VALUES($1.*)'' USING NEW; ELSE RETURN NEW; END IF; END IF;'; IF v_run_maint IS FALSE THEN v_trig_func := v_trig_func ||' v_current_partition_id := NEW.'||v_control||' - (NEW.'||v_control||' % '||v_partition_interval||'); IF (NEW.'||v_control||' % '||v_partition_interval||') > ('||v_partition_interval||' / 2) THEN v_id_position := (length(v_last_partition) - position(''p_'' in reverse(v_last_partition))) + 2; v_next_partition_id := (substring(v_last_partition from v_id_position)::bigint) + '||v_partition_interval||'; WHILE ((v_next_partition_id - v_current_partition_id) / '||v_partition_interval||') <= '||v_premake||' LOOP v_partition_created := @extschema@.create_partition_id('||quote_literal(p_parent_table)||', ARRAY[v_next_partition_id]); IF v_partition_created THEN PERFORM @extschema@.create_function_id('||quote_literal(p_parent_table)||'); PERFORM @extschema@.apply_constraints('||quote_literal(p_parent_table)||'); END IF; v_next_partition_id := v_next_partition_id + '||v_partition_interval||'; END LOOP; END IF;'; END IF; v_trig_func := v_trig_func ||' END IF; RETURN NULL; END $t$;'; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Added function for current id interval: '||v_current_partition_id||' to '||v_final_partition_id-1); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN CREATE FUNCTION: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''Partition function maintenance for table %s failed'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Create the trigger function for the parent table of a time-based partition set */ CREATE OR REPLACE FUNCTION create_function_time(p_parent_table text) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_control text; v_count int; v_current_partition_name text; v_current_partition_timestamp timestamptz; v_datetime_string text; v_final_partition_timestamp timestamptz; v_function_name text; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_new_length int; v_next_partition_name text; v_next_partition_timestamp timestamptz; v_parent_schema text; v_parent_tablename text; v_partition_interval interval; v_premake int; v_prev_partition_name text; v_prev_partition_timestamp timestamptz; v_step_id bigint; v_trig_func text; v_type text; BEGIN SELECT partition_type , partition_interval::interval , control , premake , datetime_string , jobmon INTO v_type , v_partition_interval , v_control , v_premake , v_datetime_string , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (partition_type = 'time' OR partition_type = 'time-custom'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE FUNCTION: '||p_parent_table); v_step_id := add_step(v_job_id, 'Creating partition function for table '||p_parent_table); END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; v_function_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, '_part_trig_func', FALSE); IF v_type = 'time' THEN v_trig_func := 'CREATE OR REPLACE FUNCTION '||v_function_name||'() RETURNS trigger LANGUAGE plpgsql AS $t$ DECLARE v_count int; v_partition_name text; v_partition_timestamp timestamptz; BEGIN IF TG_OP = ''INSERT'' THEN '; CASE WHEN v_partition_interval = '15 mins' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''hour'', NEW.'||v_control||') + ''15min''::interval * floor(date_part(''minute'', NEW.'||v_control||') / 15.0);'; v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0); WHEN v_partition_interval = '30 mins' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''hour'', NEW.'||v_control||') + ''30min''::interval * floor(date_part(''minute'', NEW.'||v_control||') / 30.0);'; v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0); WHEN v_partition_interval = '1 hour' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''hour'', NEW.'||v_control||');'; v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP); WHEN v_partition_interval = '1 day' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''day'', NEW.'||v_control||');'; v_current_partition_timestamp := date_trunc('day', CURRENT_TIMESTAMP); WHEN v_partition_interval = '1 week' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''week'', NEW.'||v_control||');'; v_current_partition_timestamp := date_trunc('week', CURRENT_TIMESTAMP); WHEN v_partition_interval = '1 month' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''month'', NEW.'||v_control||');'; v_current_partition_timestamp := date_trunc('month', CURRENT_TIMESTAMP); WHEN v_partition_interval = '3 months' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''quarter'', NEW.'||v_control||');'; v_current_partition_timestamp := date_trunc('quarter', CURRENT_TIMESTAMP); WHEN v_partition_interval = '1 year' THEN v_trig_func := v_trig_func||'v_partition_timestamp := date_trunc(''year'', NEW.'||v_control||');'; v_current_partition_timestamp := date_trunc('year', CURRENT_TIMESTAMP); END CASE; v_current_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, to_char(v_current_partition_timestamp, v_datetime_string), TRUE); v_next_partition_timestamp := v_current_partition_timestamp + v_partition_interval::interval; v_trig_func := v_trig_func ||' IF NEW.'||v_control||' >= '||quote_literal(v_current_partition_timestamp)||' AND NEW.'||v_control||' < '||quote_literal(v_next_partition_timestamp)|| ' THEN '; SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname ||'.'||tablename = v_current_partition_name; IF v_count > 0 THEN v_trig_func := v_trig_func || ' INSERT INTO '||v_current_partition_name||' VALUES (NEW.*); '; ELSE v_trig_func := v_trig_func || ' -- Child table for current values does not exist in this partition set, so write to parent RETURN NEW;'; END IF; FOR i IN 1..v_premake LOOP v_prev_partition_timestamp := v_current_partition_timestamp - (v_partition_interval::interval * i); v_next_partition_timestamp := v_current_partition_timestamp + (v_partition_interval::interval * i); v_final_partition_timestamp := v_next_partition_timestamp + (v_partition_interval::interval); v_prev_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, to_char(v_prev_partition_timestamp, v_datetime_string), TRUE); v_next_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, to_char(v_next_partition_timestamp, v_datetime_string), TRUE); -- Check that child table exist before making a rule to insert to them. -- Handles edge case of changing premake immediately after running create_parent(). SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname ||'.'||tablename = v_prev_partition_name; IF v_count > 0 THEN v_trig_func := v_trig_func ||' ELSIF NEW.'||v_control||' >= '||quote_literal(v_prev_partition_timestamp)||' AND NEW.'||v_control||' < '|| quote_literal(v_prev_partition_timestamp + v_partition_interval::interval)|| ' THEN INSERT INTO '||v_prev_partition_name||' VALUES (NEW.*);'; END IF; SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname ||'.'||tablename = v_next_partition_name; IF v_count > 0 THEN v_trig_func := v_trig_func ||' ELSIF NEW.'||v_control||' >= '||quote_literal(v_next_partition_timestamp)||' AND NEW.'||v_control||' < '|| quote_literal(v_final_partition_timestamp)|| ' THEN INSERT INTO '||v_next_partition_name||' VALUES (NEW.*);'; END IF; END LOOP; v_trig_func := v_trig_func||' ELSE v_partition_name := @extschema@.check_name_length('''||v_parent_tablename||''', '''||v_parent_schema||''', to_char(v_partition_timestamp, '||quote_literal(v_datetime_string)||'), TRUE); SELECT count(*) INTO v_count FROM pg_tables WHERE schemaname ||''.''|| tablename = v_partition_name; IF v_count > 0 THEN EXECUTE ''INSERT INTO ''||v_partition_name||'' VALUES($1.*)'' USING NEW; ELSE RETURN NEW; END IF; END IF;'; v_trig_func := v_trig_func ||' END IF; RETURN NULL; END $t$;'; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Added function for current time interval: '|| v_current_partition_timestamp||' to '||(v_final_partition_timestamp-'1sec'::interval)); END IF; ELSIF v_type = 'time-custom' THEN v_trig_func := 'CREATE OR REPLACE FUNCTION '||v_function_name||'() RETURNS trigger LANGUAGE plpgsql AS $t$ DECLARE v_child_table text; v_count int; BEGIN SELECT child_table INTO v_child_table FROM @extschema@.custom_time_partitions WHERE partition_range @> NEW.'||v_control||' AND parent_table = '||quote_literal(p_parent_table)||'; SELECT count(*) INTO v_count FROM pg_tables WHERE schemaname ||''.''|| tablename = v_child_table; IF v_count > 0 THEN EXECUTE ''INSERT INTO ''||v_child_table||'' VALUES ($1.*)'' USING NEW; ELSE RETURN NEW; END IF; RETURN NULL; END $t$;'; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Added function for custom time table: '||p_parent_table); END IF; ELSE RAISE EXCEPTION 'ERROR: Invalid time partitioning type given: %', v_type; END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN CREATE FUNCTION: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''Partition function maintenance for table %s failed'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Function to create id partitions */ CREATE OR REPLACE FUNCTION create_partition_id(p_parent_table text, p_partition_ids bigint[], p_analyze boolean DEFAULT true) RETURNS boolean LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE 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_grantees text[]; v_hasoids boolean; v_id bigint; v_inherit_fk boolean; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_parent_grant record; v_parent_owner text; 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_revoke text[]; v_row record; v_sql text; v_step_id bigint; v_sub_id_max bigint; v_sub_id_min bigint; v_tablename text; v_unlogged char; BEGIN SELECT control , partition_interval , inherit_fk , jobmon INTO v_control , v_partition_interval , v_inherit_fk , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND partition_type = 'id'; IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; -- Determine if this table is a child of a subpartition parent. If so, get limits of what child tables can be created based on parent suffix SELECT sub_min::bigint, sub_max::bigint INTO v_sub_id_min, v_sub_id_max FROM @extschema@.check_subpartition_limits(p_parent_table, 'id'); SELECT tableowner, schemaname, tablename, tablespace INTO v_parent_owner, v_parent_schema, v_parent_tablename, v_parent_tablespace FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); 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. 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; END IF; END IF; v_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_id::text, TRUE); -- If child table already exists, skip creation SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_tablename IS NOT NULL 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, 'Creating new partition '||v_partition_name||' with interval from '||v_id||' to '||(v_id + v_partition_interval)-1); END IF; SELECT relpersistence INTO v_unlogged FROM pg_catalog.pg_class WHERE oid::regclass = p_parent_table::regclass; v_sql := 'CREATE'; IF v_unlogged = 'u' THEN v_sql := v_sql || ' UNLOGGED'; END IF; v_sql := v_sql || ' TABLE '||v_partition_name||' (LIKE '||p_parent_table||' INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING STORAGE INCLUDING COMMENTS)'; SELECT relhasoids INTO v_hasoids FROM pg_catalog.pg_class WHERE oid::regclass = p_parent_table::regclass; IF v_hasoids IS TRUE THEN v_sql := v_sql || ' WITH (OIDS)'; END IF; EXECUTE v_sql; SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_parent_tablespace IS NOT NULL THEN EXECUTE 'ALTER TABLE '||v_partition_name||' SET TABLESPACE '||v_parent_tablespace; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' ADD CONSTRAINT '||v_tablename||'_partition_check CHECK ('||v_control||'>='||quote_literal(v_id)||' AND '||v_control||'<'||quote_literal(v_id + v_partition_interval)||')'; EXECUTE 'ALTER TABLE '||v_partition_name||' INHERIT '||p_parent_table; FOR v_parent_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = p_parent_table GROUP BY grantee LOOP EXECUTE 'GRANT '||array_to_string(v_parent_grant.types, ',')||' ON '||v_partition_name||' TO '||v_parent_grant.grantee; SELECT array_agg(r) INTO v_revoke FROM (SELECT unnest(v_all) AS r EXCEPT SELECT unnest(v_parent_grant.types)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE '||array_to_string(v_revoke, ',')||' ON '||v_partition_name||' FROM '||v_parent_grant.grantee||' CASCADE'; END IF; v_grantees := array_append(v_grantees, v_parent_grant.grantee::text); END LOOP; -- Revoke all privileges from roles that have none on the parent IF v_grantees IS NOT NULL THEN SELECT array_agg(r) INTO v_revoke FROM ( SELECT DISTINCT grantee::text AS r FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = v_partition_name EXCEPT SELECT unnest(v_grantees)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE ALL ON '||v_partition_name||' FROM '||array_to_string(v_revoke, ','); END IF; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' OWNER TO '||v_parent_owner; IF v_inherit_fk THEN PERFORM @extschema@.apply_foreign_keys(quote_ident(v_parent_schema)||'.'||quote_ident(v_parent_tablename), v_partition_name); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); 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 FOR v_row IN SELECT sub_parent , sub_control , sub_partition_type , 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_use_run_maintenance , sub_jobmon FROM @extschema@.part_config_sub WHERE sub_parent = p_parent_table LOOP IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Subpartitioning '||v_partition_name); END IF; v_sql := format('SELECT @extschema@.create_parent( p_parent_table := %L , p_control := %L , p_type := %L , p_interval := %L , p_constraint_cols := %L , p_premake := %L , p_use_run_maintenance := %L , p_inherit_fk := %L , p_jobmon := %L )' , v_partition_name , v_row.sub_control , v_row.sub_partition_type , v_row.sub_partition_interval , v_row.sub_constraint_cols , v_row.sub_premake , v_row.sub_use_run_maintenance , v_row.sub_inherit_fk , v_row.sub_jobmon); EXECUTE v_sql; 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 WHERE parent_table = v_partition_name; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END LOOP; -- end sub partitioning LOOP 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, 'Analyzing partition set: '||p_parent_table); END IF; EXECUTE 'ANALYZE '||p_parent_table; 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 v_step_id := add_step(v_job_id, 'No partitions created for partition set: '||p_parent_table); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; PERFORM close_job(v_job_id); END IF; IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_partition_created; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN CREATE TABLE: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Function to create a child table in a time-based partition set */ CREATE OR REPLACE FUNCTION create_partition_time (p_parent_table text, p_partition_times timestamp[], p_analyze boolean DEFAULT true) RETURNS boolean LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE 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_grantees text[]; v_hasoids boolean; v_inherit_fk boolean; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_parent_grant record; v_parent_owner text; v_parent_schema text; v_parent_tablename text; v_partition_created boolean := false; v_partition_name text; v_partition_suffix text; v_parent_tablespace text; v_partition_interval interval; v_partition_timestamp_end timestamp; v_partition_timestamp_start timestamp; v_quarter text; v_revoke text[]; v_row record; v_sql text; v_step_id bigint; v_step_overflow_id bigint; v_sub_timestamp_max timestamp; v_sub_timestamp_min timestamp; v_tablename text; v_trunc_value text; v_time timestamp; v_type text; v_unlogged char; v_year text; BEGIN SELECT partition_type , control , partition_interval , inherit_fk , jobmon , datetime_string INTO v_type , v_control , v_partition_interval , v_inherit_fk , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND partition_type = 'time' OR partition_type = 'time-custom'; IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; -- Determine if this table is a child of a subpartition parent. If so, get limits of what child tables can be created based on parent suffix SELECT sub_min::timestamp, sub_max::timestamp INTO v_sub_timestamp_min, v_sub_timestamp_max FROM @extschema@.check_subpartition_limits(p_parent_table, 'time'); SELECT tableowner, schemaname, tablename, tablespace INTO v_parent_owner, v_parent_schema, v_parent_tablename, v_parent_tablespace FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE TABLE: '||p_parent_table); END IF; 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. 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'); CONTINUE; END; -- 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 CONTINUE; END IF; END IF; -- This suffix generation code is in partition_data_time() as well v_partition_suffix := to_char(v_time, 'YYYY'); IF v_partition_interval < '1 year' AND v_partition_interval <> '1 week' THEN v_partition_suffix := v_partition_suffix ||'_'|| to_char(v_time, 'MM'); IF v_partition_interval < '1 month' AND v_partition_interval <> '1 week' THEN v_partition_suffix := v_partition_suffix ||'_'|| to_char(v_time, 'DD'); IF v_partition_interval < '1 day' THEN v_partition_suffix := v_partition_suffix || '_' || to_char(v_time, 'HH24MI'); IF v_partition_interval < '1 minute' THEN v_partition_suffix := v_partition_suffix || to_char(v_time, 'SS'); END IF; -- end < minute IF END IF; -- end < day IF END IF; -- end < month IF END IF; -- end < year IF IF v_partition_interval = '1 week' THEN v_partition_suffix := to_char(v_time, 'IYYY') || 'w' || to_char(v_time, 'IW'); END IF; -- "Q" is ignored in to_timestamp, so handle special case IF v_partition_interval = '3 months' AND v_type = 'time' THEN v_year := to_char(v_time, 'YYYY'); v_quarter := to_char(v_time, 'Q'); v_partition_suffix := v_year || 'q' || v_quarter; END IF; v_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_partition_suffix, TRUE); SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_tablename IS NOT NULL 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, 'Creating new partition '||v_partition_name||' with interval from '||v_partition_timestamp_start||' to '||(v_partition_timestamp_end-'1sec'::interval)); END IF; SELECT relpersistence INTO v_unlogged FROM pg_catalog.pg_class WHERE oid::regclass = p_parent_table::regclass; v_sql := 'CREATE'; IF v_unlogged = 'u' THEN v_sql := v_sql || ' UNLOGGED'; END IF; v_sql := v_sql || ' TABLE '||v_partition_name||' (LIKE '||p_parent_table||' INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING STORAGE INCLUDING COMMENTS)'; SELECT relhasoids INTO v_hasoids FROM pg_catalog.pg_class WHERE oid::regclass = p_parent_table::regclass; IF v_hasoids IS TRUE THEN v_sql := v_sql || ' WITH (OIDS)'; END IF; EXECUTE v_sql; SELECT tablename INTO v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_partition_name; IF v_parent_tablespace IS NOT NULL THEN EXECUTE 'ALTER TABLE '||v_partition_name||' SET TABLESPACE '||v_parent_tablespace; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' ADD CONSTRAINT '||v_tablename||'_partition_check CHECK ('||v_control||'>='||quote_literal(v_partition_timestamp_start)||' AND '||v_control||'<'||quote_literal(v_partition_timestamp_end)||')'; EXECUTE 'ALTER TABLE '||v_partition_name||' INHERIT '||p_parent_table; -- If custom time, set extra config options. IF v_type = 'time-custom' THEN INSERT INTO @extschema@.custom_time_partitions (parent_table, child_table, partition_range) VALUES ( p_parent_table, v_partition_name, tstzrange(v_partition_timestamp_start, v_partition_timestamp_end, '[)') ); END IF; FOR v_parent_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = p_parent_table GROUP BY grantee LOOP EXECUTE 'GRANT '||array_to_string(v_parent_grant.types, ',')||' ON '||v_partition_name||' TO '||v_parent_grant.grantee; SELECT array_agg(r) INTO v_revoke FROM (SELECT unnest(v_all) AS r EXCEPT SELECT unnest(v_parent_grant.types)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE '||array_to_string(v_revoke, ',')||' ON '||v_partition_name||' FROM '||v_parent_grant.grantee||' CASCADE'; END IF; v_grantees := array_append(v_grantees, v_parent_grant.grantee::text); END LOOP; -- Revoke all privileges from roles that have none on the parent IF v_grantees IS NOT NULL THEN SELECT array_agg(r) INTO v_revoke FROM ( SELECT DISTINCT grantee::text AS r FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = v_partition_name EXCEPT SELECT unnest(v_grantees)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE ALL ON '||v_partition_name||' FROM '||array_to_string(v_revoke, ','); END IF; END IF; EXECUTE 'ALTER TABLE '||v_partition_name||' OWNER TO '||v_parent_owner; IF v_inherit_fk THEN PERFORM @extschema@.apply_foreign_keys(quote_ident(v_parent_schema)||'.'||quote_ident(v_parent_tablename), v_partition_name); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); 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 FOR v_row IN SELECT sub_parent , sub_control , sub_partition_type , 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_use_run_maintenance , sub_jobmon FROM @extschema@.part_config_sub WHERE sub_parent = p_parent_table LOOP IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Subpartitioning '||v_partition_name); END IF; v_sql := format('SELECT @extschema@.create_parent( p_parent_table := %L , p_control := %L , p_type := %L , p_interval := %L , p_constraint_cols := %L , p_premake := %L , p_use_run_maintenance := %L , p_inherit_fk := %L , p_jobmon := %L )' , v_partition_name , v_row.sub_control , v_row.sub_partition_type , v_row.sub_partition_interval , v_row.sub_constraint_cols , v_row.sub_premake , v_row.sub_use_run_maintenance , v_row.sub_inherit_fk , v_row.sub_jobmon); EXECUTE v_sql; 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 WHERE parent_table = v_partition_name; END LOOP; -- end sub partitioning LOOP 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, 'Analyzing partition set: '||p_parent_table); END IF; EXECUTE 'ANALYZE '||p_parent_table; 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 v_step_id := add_step(v_job_id, 'No partitions created for partition set: '||p_parent_table); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; IF v_step_overflow_id IS NOT NULL THEN PERFORM fail_job(v_job_id); ELSE PERFORM close_job(v_job_id); END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_partition_created; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN CREATE TABLE: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Apply constraints managed by partman extension */ CREATE OR REPLACE FUNCTION apply_constraints(p_parent_table text, p_child_table text DEFAULT NULL, p_analyze boolean DEFAULT FALSE, p_debug boolean DEFAULT FALSE) RETURNS void LANGUAGE plpgsql AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_child_table text; v_child_tablename text; v_col text; v_constraint_cols text[]; v_constraint_col_type text; v_constraint_name text; v_datetime_string text; v_existing_constraint_name text; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_last_partition text; v_last_partition_id int; v_last_partition_timestamp timestamp; v_constraint_values record; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_partition_interval text; v_partition_suffix text; v_premake int; v_sql text; v_step_id bigint; v_suffix_position int; v_type text; BEGIN SELECT partition_type , partition_interval , premake , datetime_string , constraint_cols , jobmon INTO v_type , v_partition_interval , v_premake , v_datetime_string , v_constraint_cols , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_constraint_cols IS NULL THEN IF p_debug THEN RAISE NOTICE 'Given parent table (%) not set up for constraint management (constraint_cols is NULL)', p_parent_table; END IF; -- Returns silently to allow this function to be simply called by maintenance processes without having to check if config options are set. RETURN; END IF; SELECT show_partitions INTO v_last_partition FROM @extschema@.show_partitions(p_parent_table, 'DESC') LIMIT 1; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN CREATE CONSTRAINT: '||p_parent_table); END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; -- If p_child_table is null, figure out the partition that is the one right before the premake value backwards. IF p_child_table IS NULL THEN IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Automatically determining most recent child on which to apply constraints'); END IF; v_suffix_position := (length(v_last_partition) - position('p_' in reverse(v_last_partition))) + 2; IF v_type IN ('time', 'time-custom') THEN v_last_partition_timestamp := to_timestamp(substring(v_last_partition from v_suffix_position), v_datetime_string); v_partition_suffix := to_char(v_last_partition_timestamp - (v_partition_interval::interval * ((v_premake * 2)+1) ), v_datetime_string); ELSIF v_type = 'id' THEN v_last_partition_id := substring(v_last_partition from v_suffix_position)::int; v_partition_suffix := (v_last_partition_id - (v_partition_interval::int * ((v_premake * 2)+1) ))::text; END IF; v_child_table := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_partition_suffix, TRUE); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Target child table: '||v_child_table); END IF; ELSE v_child_table := p_child_table; END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Checking if target child table exists'); END IF; SELECT tablename INTO v_child_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_child_table; IF v_child_tablename IS NULL THEN IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'NOTICE', 'Target child table ('||v_child_table||') does not exist. Skipping constraint creation.'); PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; IF p_debug THEN RAISE NOTICE 'Target child table (%) does not exist. Skipping constraint creation.', v_child_table; END IF; RETURN; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; FOREACH v_col IN ARRAY v_constraint_cols LOOP SELECT c.conname INTO v_existing_constraint_name FROM pg_catalog.pg_constraint c JOIN pg_catalog.pg_attribute a ON c.conrelid = a.attrelid WHERE conrelid = v_child_table::regclass AND c.conname LIKE 'partmanconstr_%' AND c.contype = 'c' AND a.attname = v_col AND ARRAY[a.attnum] <@ c.conkey AND a.attisdropped = false; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Applying new constraint on column: '||v_col); END IF; IF v_existing_constraint_name IS NOT NULL THEN IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'NOTICE', 'Partman managed constraint already exists on this table ('||v_child_table||') and column ('||v_col||'). Skipping creation.'); END IF; RAISE WARNING 'Partman managed constraint already exists on this table (%) and column (%). Skipping creation.', v_child_table, v_col ; CONTINUE; END IF; -- 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 'SELECT min('||v_col||')::text AS min, max('||v_col||')::text AS max FROM '||v_child_table INTO v_constraint_values; IF v_constraint_values IS NOT NULL THEN v_sql := concat('ALTER TABLE ', v_child_table, ' ADD CONSTRAINT ', v_constraint_name , ' CHECK (', v_col, ' >= ', quote_literal(v_constraint_values.min), ' AND ' , v_col, ' <= ', quote_literal(v_constraint_values.max), ')' ); IF p_debug THEN RAISE NOTICE 'Constraint creation query: %', v_sql; END IF; EXECUTE v_sql; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'New constraint created: '||v_sql); END IF; ELSE IF p_debug THEN RAISE NOTICE 'Given column (%) contains all NULLs. No constraint created', v_col; END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'NOTICE', 'Given column ('||v_col||') contains all NULLs. No constraint created'); END IF; END IF; END LOOP; IF p_analyze THEN IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Running analyze on partition set: '||p_parent_table); END IF; IF p_debug THEN RAISE NOTICE 'Running analyze on partition set: %', p_parent_table; END IF; EXECUTE 'ANALYZE '||p_parent_table; 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 PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN CREATE CONSTRAINT: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Apply foreign keys that exist on the given parent to the given child table */ CREATE OR REPLACE FUNCTION apply_foreign_keys(p_parent_table text, p_child_table text, p_debug boolean DEFAULT false) RETURNS void LANGUAGE plpgsql AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_job_id bigint; v_jobmon text; v_jobmon_schema text; v_old_search_path text; v_ref_schema text; v_ref_table text; v_row record; v_schemaname text; v_sql text; v_step_id bigint; v_tablename text; BEGIN SELECT jobmon INTO v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN APPLYING FOREIGN KEYS: '||p_parent_table); END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Checking if target child table exists'); END IF; SELECT schemaname, tablename INTO v_schemaname, v_tablename FROM pg_catalog.pg_tables WHERE schemaname||'.'||tablename = p_child_table; IF v_tablename IS NULL THEN IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'CRITICAL', 'Target child table ('||v_child_table||') does not exist.'); PERFORM fail_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RAISE EXCEPTION 'Target child table (%.%) does not exist.', v_schemaname, v_tablename; RETURN; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; FOR v_row IN SELECT n.nspname||'.'||cl.relname AS ref_table , '"'||string_agg(att.attname, '","')||'"' AS ref_column , '"'||string_agg(att2.attname, '","')||'"' AS child_column , keys.condeferred , keys.condeferrable , keys.confupdtype , keys.confdeltype , keys.confmatchtype FROM ( SELECT unnest(con.conkey) as ref , unnest(con.confkey) as child , con.confrelid , con.conrelid , con.condeferred , con.condeferrable , con.confupdtype , con.confdeltype , con.confmatchtype FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid JOIN pg_catalog.pg_constraint con ON c.oid = con.conrelid WHERE n.nspname ||'.'|| c.relname = p_parent_table AND con.contype = 'f' ORDER BY con.conkey ) keys JOIN pg_catalog.pg_class cl ON cl.oid = keys.confrelid JOIN pg_catalog.pg_namespace n ON cl.relnamespace = n.oid JOIN pg_catalog.pg_attribute att ON att.attrelid = keys.confrelid AND att.attnum = keys.child JOIN pg_catalog.pg_attribute att2 ON att2.attrelid = keys.conrelid AND att2.attnum = keys.ref GROUP BY n.nspname, cl.relname, keys.condeferred, keys.condeferrable, keys.confupdtype, keys.confdeltype, keys.confmatchtype LOOP SELECT schemaname, tablename INTO v_ref_schema, v_ref_table FROM pg_tables WHERE schemaname||'.'||tablename = v_row.ref_table; v_sql := format('ALTER TABLE %I.%I ADD FOREIGN KEY (%s) REFERENCES %I.%I (%s)', v_schemaname, v_tablename, v_row.child_column, v_ref_schema, v_ref_table, v_row.ref_column); CASE WHEN v_row.confmatchtype = 'f' THEN v_sql := v_sql || ' MATCH FULL '; WHEN v_row.confmatchtype = 's' THEN v_sql := v_sql || ' MATCH SIMPLE '; WHEN v_row.confmatchtype = 'p' THEN v_sql := v_sql || ' MATCH PARTIAL '; END CASE; CASE WHEN v_row.confupdtype = 'a' THEN v_sql := v_sql || ' ON UPDATE NO ACTION '; WHEN v_row.confupdtype = 'r' THEN v_sql := v_sql || ' ON UPDATE RESTRICT '; WHEN v_row.confupdtype = 'c' THEN v_sql := v_sql || ' ON UPDATE CASCADE '; WHEN v_row.confupdtype = 'n' THEN v_sql := v_sql || ' ON UPDATE SET NULL '; WHEN v_row.confupdtype = 'd' THEN v_sql := v_sql || ' ON UPDATE SET DEFAULT '; END CASE; CASE WHEN v_row.confdeltype = 'a' THEN v_sql := v_sql || ' ON DELETE NO ACTION '; WHEN v_row.confdeltype = 'r' THEN v_sql := v_sql || ' ON DELETE RESTRICT '; WHEN v_row.confdeltype = 'c' THEN v_sql := v_sql || ' ON DELETE CASCADE '; WHEN v_row.confdeltype = 'n' THEN v_sql := v_sql || ' ON DELETE SET NULL '; WHEN v_row.confdeltype = 'd' THEN v_sql := v_sql || ' ON DELETE SET DEFAULT '; END CASE; CASE WHEN v_row.condeferrable = true AND v_row.condeferred = true THEN v_sql := v_sql || ' DEFERRABLE INITIALLY DEFERRED '; WHEN v_row.condeferrable = false AND v_row.condeferred = false THEN v_sql := v_sql || ' NOT DEFERRABLE '; WHEN v_row.condeferrable = true AND v_row.condeferred = false THEN v_sql := v_sql || ' DEFERRABLE INITIALLY IMMEDIATE '; END CASE; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Applying FK: '||v_sql); END IF; EXECUTE v_sql; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'FK applied'); END IF; END LOOP; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN CREATE APPLYING FOREIGN KEYS: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Function to monitor for data getting inserted into parent tables managed by extension */ CREATE OR REPLACE FUNCTION check_parent() RETURNS SETOF @extschema@.check_parent_table LANGUAGE plpgsql STABLE SECURITY DEFINER AS $$ DECLARE v_count bigint = 0; v_sql text; v_tables record; v_trouble @extschema@.check_parent_table%rowtype; BEGIN FOR v_tables IN SELECT DISTINCT parent_table FROM @extschema@.part_config LOOP v_sql := 'SELECT count(1) AS n FROM ONLY '||v_tables.parent_table; EXECUTE v_sql INTO v_count; IF v_count > 0 THEN v_trouble.parent_table := v_tables.parent_table; v_trouble.count := v_count; RETURN NEXT v_trouble; END IF; v_count := 0; END LOOP; RETURN; END $$; /* * Create a partition set that is a subpartition of an already existing partition set. * Given the parent table of any current partition set, it will turn all existing children into parent tables of their own partition sets * using the configuration options given as parameters to this function. * Uses another config table that allows for turning all future child partitions into a new parent automatically. * To avoid logical complications and contention issues, ALL subpartitions must be maintained using run_maintenance(). * This means the automatic, trigger based partition creation for serial partitioning will not work if it is a subpartition. */ CREATE OR REPLACE FUNCTION create_sub_parent( p_top_parent text , p_control text , p_type text , p_interval text , p_constraint_cols text[] DEFAULT NULL , p_premake int DEFAULT 4 , p_start_partition text DEFAULT NULL , p_inherit_fk boolean DEFAULT true , p_jobmon boolean DEFAULT true , p_debug boolean DEFAULT false) RETURNS boolean LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_last_partition text; v_row record; v_row_last_part record; v_run_maint boolean; v_sql text; v_success boolean := false; v_top_type text; BEGIN SELECT use_run_maintenance INTO v_run_maint FROM @extschema@.part_config WHERE parent_table = p_top_parent; IF v_run_maint 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; ELSIF v_run_maint = false THEN RAISE EXCEPTION 'Any parent table that will be part of a sub-partitioned set (on any level) must have use_run_maintenance set to true in part_config table, even for serial partitioning. See documentation for more info.'; END IF; FOR v_row IN -- Loop through all current children to turn them into partitioned tables SELECT show_partitions AS child_table FROM @extschema@.show_partitions(p_top_parent) LOOP -- Just call existing create_parent() function but add the given parameters to the part_config_sub table as well v_sql := format('SELECT @extschema@.create_parent( p_parent_table := %L , p_control := %L , p_type := %L , p_interval := %L , p_constraint_cols := %L , p_premake := %L , p_use_run_maintenance := %L , p_start_partition := %L , p_inherit_fk := %L , p_jobmon := %L , p_debug := %L )' , v_row.child_table , p_control , p_type , p_interval , p_constraint_cols , p_premake , true , p_start_partition , p_inherit_fk , p_jobmon , p_debug); EXECUTE v_sql; END LOOP; INSERT INTO @extschema@.part_config_sub ( sub_parent , sub_control , sub_partition_type , sub_partition_interval , sub_constraint_cols , sub_premake , sub_inherit_fk , sub_use_run_maintenance , sub_jobmon) VALUES ( p_top_parent , p_control , p_type , p_interval , p_constraint_cols , p_premake , p_inherit_fk , true , p_jobmon); v_success := true; RETURN v_success; END $$; /* * Drop constraints managed by pg_partman */ CREATE OR REPLACE FUNCTION drop_constraints(p_parent_table text, p_child_table text, p_debug boolean DEFAULT false) RETURNS void LANGUAGE plpgsql AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_col text; v_constraint_cols text[]; v_existing_constraint_name text; v_exists boolean := FALSE; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_sql text; v_step_id bigint; BEGIN SELECT constraint_cols , jobmon INTO v_constraint_cols , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_constraint_cols IS NULL THEN RAISE EXCEPTION 'Given parent table (%) not set up for constraint management (constraint_cols is NULL)', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN DROP CONSTRAINT: '||p_parent_table); v_step_id := add_step(v_job_id, 'Entering constraint drop loop'); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; FOREACH v_col IN ARRAY v_constraint_cols LOOP SELECT c.conname INTO v_existing_constraint_name FROM pg_catalog.pg_constraint c JOIN pg_catalog.pg_attribute a ON c.conrelid = a.attrelid WHERE conrelid = p_child_table::regclass AND c.conname LIKE 'partmanconstr_%' AND c.contype = 'c' AND a.attname = v_col AND ARRAY[a.attnum] <@ c.conkey AND a.attisdropped = false; IF v_existing_constraint_name IS NOT NULL THEN v_exists := TRUE; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Dropping constraint on column: '||v_col); END IF; v_sql := 'ALTER TABLE '||p_child_table||' DROP CONSTRAINT '||v_existing_constraint_name; IF p_debug THEN RAISE NOTICE 'Constraint drop query: %', v_sql; END IF; EXECUTE v_sql; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Drop constraint query: '||v_sql); END IF; END IF; END LOOP; IF v_jobmon_schema IS NOT NULL AND v_exists IS FALSE THEN v_step_id := add_step(v_job_id, 'No constraints found to drop on child table: '||p_child_table); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN DROP CONSTRAINT: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * 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. */ CREATE OR REPLACE FUNCTION 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 SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_adv_lock boolean; v_child_table text; v_control text; v_drop_count int := 0; v_id_position int; v_index record; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_max bigint; v_old_search_path text; v_partition_interval bigint; v_partition_id bigint; v_retention bigint; v_retention_keep_index boolean; v_retention_keep_table boolean; v_retention_schema text; v_row_max_id record; v_step_id bigint; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman drop_partition_id')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'drop_partition_id already running.'; RETURN 0; END IF; -- Allow override of configuration options IF p_retention IS NULL THEN SELECT partition_interval::bigint , control , retention::bigint , retention_keep_table , retention_keep_index , retention_schema , jobmon INTO v_partition_interval , v_control , v_retention , v_retention_keep_table , v_retention_keep_index , v_retention_schema , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND partition_type = 'id' 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_interval::bigint , control , retention_keep_table , retention_keep_index , retention_schema , jobmon INTO v_partition_interval , v_control , v_retention_keep_table , v_retention_keep_index , v_retention_schema , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND partition_type = 'id'; v_retention := p_retention; IF v_partition_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF p_keep_table IS NOT NULL THEN v_retention_keep_table = p_keep_table; END IF; IF p_keep_index IS NOT NULL THEN v_retention_keep_index = p_keep_index; END IF; IF p_retention_schema IS NOT NULL THEN v_retention_schema = p_retention_schema; 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 show_partitions FROM @extschema@.show_partitions(p_parent_table, 'DESC') LOOP EXECUTE 'SELECT max('||v_control||') FROM '||v_row_max_id.show_partitions INTO v_max; IF v_max IS NOT NULL THEN EXIT; END IF; END LOOP; -- Loop through child tables of the given parent FOR v_child_table IN SELECT n.nspname||'.'||c.relname FROM pg_inherits i join pg_class c ON i.inhrelid = c.oid join pg_namespace n ON c.relnamespace = n.oid WHERE i.inhparent::regclass = p_parent_table::regclass ORDER BY i.inhrelid ASC LOOP v_id_position := (length(v_child_table) - position('p_' in reverse(v_child_table))) + 2; v_partition_id := substring(v_child_table from v_id_position)::bigint; -- Add one interval since partition names contain the start of the constraint period IF v_retention <= (v_max - (v_partition_id + v_partition_interval)) THEN -- Only create a jobmon entry if there's actual retention work done IF v_jobmon_schema IS NOT NULL AND v_job_id IS NULL THEN v_job_id := add_job('PARTMAN DROP ID PARTITION: '|| p_parent_table); END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Uninherit table '||v_child_table||' from '||p_parent_table); END IF; EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; 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, 'Drop table '||v_child_table); END IF; EXECUTE 'DROP TABLE '||v_child_table||' CASCADE'; 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 FOR v_index IN SELECT i.indexrelid::regclass AS name , c.conname FROM pg_catalog.pg_index i LEFT JOIN pg_catalog.pg_constraint c ON i.indexrelid = c.conindid WHERE i.indrelid = v_child_table::regclass LOOP IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Drop index '||v_index.name||' from '||v_child_table); END IF; IF v_index.conname IS NOT NULL THEN EXECUTE 'ALTER TABLE '||v_child_table||' DROP CONSTRAINT '||v_index.conname; ELSE EXECUTE 'DROP INDEX '||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; ELSE -- Move to new schema IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Moving table '||v_child_table||' to schema '||v_retention_schema); END IF; EXECUTE 'ALTER TABLE '||v_child_table||' SET SCHEMA '||v_retention_schema; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; -- End retention schema if -- If child table is a subpartition, remove it from part_config & part_config_sub (should cascade due to FK) DELETE FROM @extschema@.part_config WHERE parent_table = v_child_table; v_drop_count := v_drop_count + 1; END IF; -- End retention check IF END LOOP; -- End child table loop IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Finished partition drop maintenance'); PERFORM update_step(v_step_id, 'OK', v_drop_count||' partitions dropped.'); PERFORM close_job(v_job_id); END IF; EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_drop_count; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN DROP ID PARTITION: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Function to drop child tables from a time-based partition set. * Options to move table to different schema, drop only indexes or actually drop the table from the database. */ CREATE OR REPLACE FUNCTION 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) RETURNS int LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_adv_lock boolean; v_child_table text; v_datetime_string text; v_drop_count int := 0; v_index record; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_partition_interval interval; v_partition_timestamp timestamp; v_quarter text; v_retention interval; v_retention_keep_index boolean; v_retention_keep_table boolean; v_retention_schema text; v_step_id bigint; v_time_position int; v_type text; v_year text; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman drop_partition_time')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'drop_partition_time already running.'; RETURN 0; END IF; -- Allow override of configuration options IF p_retention IS NULL THEN SELECT partition_type , partition_interval::interval , retention::interval , retention_keep_table , retention_keep_index , datetime_string , retention_schema , jobmon INTO v_type , v_partition_interval , v_retention , v_retention_keep_table , v_retention_keep_index , v_datetime_string , v_retention_schema , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (partition_type = 'time' OR partition_type = 'time-custom') 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 , retention_keep_table , retention_keep_index , datetime_string , retention_schema , jobmon INTO v_type , v_partition_interval , v_retention_keep_table , v_retention_keep_index , v_datetime_string , v_retention_schema , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (partition_type = 'time' OR partition_type = 'time-custom'); v_retention := p_retention; IF v_partition_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF p_keep_table IS NOT NULL THEN v_retention_keep_table = p_keep_table; END IF; IF p_keep_index IS NOT NULL THEN v_retention_keep_index = p_keep_index; END IF; IF p_retention_schema IS NOT NULL THEN v_retention_schema = p_retention_schema; END IF; -- Loop through child tables of the given parent FOR v_child_table IN SELECT n.nspname||'.'||c.relname FROM pg_inherits i join pg_class c ON i.inhrelid = c.oid join pg_namespace n ON c.relnamespace = n.oid WHERE i.inhparent::regclass = p_parent_table::regclass ORDER BY i.inhrelid ASC LOOP -- pull out datetime portion of partition's tablename to make the next one v_time_position := (length(v_child_table) - position('p_' in reverse(v_child_table))) + 2; IF v_partition_interval <> '3 months' OR (v_partition_interval = '3 months' AND v_type = 'time-custom') THEN v_partition_timestamp := to_timestamp(substring(v_child_table from v_time_position), v_datetime_string); ELSE -- to_timestamp doesn't recognize 'Q' date string formater. Handle it v_year := split_part(substring(v_child_table from v_time_position), 'q', 1); v_quarter := split_part(substring(v_child_table from v_time_position), 'q', 2); CASE WHEN v_quarter = '1' THEN v_partition_timestamp := to_timestamp(v_year || '-01-01', 'YYYY-MM-DD'); WHEN v_quarter = '2' THEN v_partition_timestamp := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); WHEN v_quarter = '3' THEN v_partition_timestamp := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); WHEN v_quarter = '4' THEN v_partition_timestamp := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); END CASE; END IF; -- Add one interval since partition names contain the start of the constraint period IF v_retention < (CURRENT_TIMESTAMP - (v_partition_timestamp + v_partition_interval)) THEN -- Only create a jobmon entry if there's actual retention work done IF v_jobmon_schema IS NOT NULL AND v_job_id IS NULL THEN v_job_id := add_job('PARTMAN DROP TIME PARTITION: '|| p_parent_table); END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Uninherit table '||v_child_table||' from '||p_parent_table); END IF; EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; IF v_type = 'time-custom' THEN DELETE FROM @extschema@.custom_time_partitions WHERE parent_table = p_parent_table AND child_table = v_child_table; 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, 'Drop table '||v_child_table); END IF; EXECUTE 'DROP TABLE '||v_child_table||' CASCADE'; 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 FOR v_index IN SELECT i.indexrelid::regclass AS name , c.conname FROM pg_catalog.pg_index i LEFT JOIN pg_catalog.pg_constraint c ON i.indexrelid = c.conindid WHERE i.indrelid = v_child_table::regclass LOOP IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Drop index '||v_index.name||' from '||v_child_table); END IF; IF v_index.conname IS NOT NULL THEN EXECUTE 'ALTER TABLE '||v_child_table||' DROP CONSTRAINT '||v_index.conname; ELSE EXECUTE 'DROP INDEX '||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; ELSE -- Move to new schema IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Moving table '||v_child_table||' to schema '||v_retention_schema); END IF; EXECUTE 'ALTER TABLE '||v_child_table||' SET SCHEMA '||v_retention_schema; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; -- End retention schema if -- If child table is a subpartition, remove it from part_config & part_config_sub (should cascade due to FK) DELETE FROM @extschema@.part_config WHERE parent_table = v_child_table; v_drop_count := v_drop_count + 1; END IF; -- End retention check IF END LOOP; -- End child table loop IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Finished partition drop maintenance'); PERFORM update_step(v_step_id, 'OK', v_drop_count||' partitions dropped.'); PERFORM close_job(v_job_id); END IF; EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_drop_count; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN DROP TIME PARTITION: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Populate the child table(s) of an id-based partition set with old data from the original parent */ CREATE OR REPLACE FUNCTION partition_data_id(p_parent_table text, p_batch_count int DEFAULT 1, p_batch_interval int DEFAULT NULL, p_lock_wait numeric DEFAULT 0, p_order text DEFAULT 'ASC') RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_control text; v_current_partition_name text; 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_rowcount bigint; v_sql text; v_start_control bigint; v_total_rows bigint := 0; BEGIN SELECT partition_interval::bigint , control INTO v_partition_interval , v_control FROM @extschema@.part_config WHERE parent_table = p_parent_table AND partition_type = 'id'; IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF p_batch_interval IS NULL OR p_batch_interval > v_partition_interval THEN p_batch_interval := v_partition_interval; END IF; FOR i IN 1..p_batch_count LOOP IF p_order = 'ASC' THEN EXECUTE 'SELECT min('||v_control||') FROM ONLY '||p_parent_table INTO v_start_control; IF v_start_control IS NULL THEN EXIT; END IF; v_min_partition_id = v_start_control - (v_start_control % v_partition_interval); v_partition_id := ARRAY[v_min_partition_id]; -- Check if custom batch interval overflows current partition maximum IF (v_start_control + p_batch_interval) >= (v_min_partition_id + v_partition_interval) THEN v_max_partition_id := v_min_partition_id + v_partition_interval; ELSE v_max_partition_id := v_start_control + p_batch_interval; END IF; ELSIF p_order = 'DESC' THEN EXECUTE 'SELECT max('||v_control||') FROM ONLY '||p_parent_table INTO v_start_control; IF v_start_control IS NULL THEN EXIT; END IF; v_min_partition_id = v_start_control - (v_start_control % v_partition_interval); -- Must be greater than max value still in parent table since query below grabs < max v_max_partition_id := v_min_partition_id + v_partition_interval; v_partition_id := ARRAY[v_min_partition_id]; -- Make sure minimum doesn't underflow current partition minimum IF (v_start_control - p_batch_interval) >= v_min_partition_id THEN v_min_partition_id = v_start_control - p_batch_interval; END IF; ELSE RAISE EXCEPTION 'Invalid value for p_order. Must be ASC or DESC'; END IF; -- do some locking with timeout, if required IF p_lock_wait > 0 THEN v_lock_iter := 0; WHILE v_lock_iter <= 5 LOOP v_lock_iter := v_lock_iter + 1; BEGIN v_sql := 'SELECT * FROM ONLY ' || p_parent_table || ' WHERE '||v_control||' >= '||quote_literal(v_min_partition_id)|| ' AND '||v_control||' < '||quote_literal(v_max_partition_id) ||' FOR UPDATE NOWAIT'; EXECUTE v_sql; 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 RETURN -1; END IF; END IF; PERFORM @extschema@.create_partition_id(p_parent_table, v_partition_id); SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname||'.'||tablename = p_parent_table; v_current_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_min_partition_id::text, TRUE); EXECUTE 'WITH partition_data AS ( DELETE FROM ONLY '||p_parent_table||' WHERE '||v_control||' >= '||v_min_partition_id|| ' AND '||v_control||' < '||v_max_partition_id||' RETURNING *) INSERT INTO '||v_current_partition_name||' SELECT * FROM partition_data'; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total_rows := v_total_rows + v_rowcount; IF v_rowcount = 0 THEN EXIT; END IF; END LOOP; PERFORM @extschema@.create_function_id(p_parent_table); RETURN v_total_rows; END $$; /* * Populate the child table(s) of a time-based partition set with old data from the original parent */ CREATE OR REPLACE FUNCTION 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') RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_control text; v_datetime_string text; v_current_partition_name text; v_max_partition_timestamp timestamp; v_last_partition text; v_lock_iter int := 1; v_lock_obtained boolean := FALSE; v_min_partition_timestamp timestamp; v_parent_schema text; v_parent_tablename text; v_partition_interval interval; v_partition_suffix text; v_partition_timestamp timestamp[]; v_quarter text; v_rowcount bigint; v_sql text; v_start_control timestamp; v_time_position int; v_total_rows bigint := 0; v_type text; v_year text; BEGIN SELECT partition_type , partition_interval::interval , control , datetime_string INTO v_type , v_partition_interval , v_control , v_datetime_string FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (partition_type = 'time' OR partition_type = 'time-custom'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF p_batch_interval IS NULL OR p_batch_interval > v_partition_interval THEN p_batch_interval := v_partition_interval; END IF; SELECT show_partitions INTO v_last_partition FROM @extschema@.show_partitions(p_parent_table, 'DESC') LIMIT 1; FOR i IN 1..p_batch_count LOOP IF p_order = 'ASC' THEN EXECUTE 'SELECT min('||v_control||') FROM ONLY '||p_parent_table INTO v_start_control; ELSIF p_order = 'DESC' THEN EXECUTE 'SELECT max('||v_control||') FROM ONLY '||p_parent_table INTO v_start_control; ELSE RAISE EXCEPTION 'Invalid value for p_order. Must be ASC or DESC'; END IF; IF v_start_control IS NULL THEN EXIT; END IF; IF v_type = 'time' 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_type = 'time-custom' THEN -- Keep going backwards, checking if the time interval encompases the current v_start_control value v_time_position := (length(v_last_partition) - position('p_' in reverse(v_last_partition))) + 2; v_min_partition_timestamp := to_timestamp(substring(v_last_partition from v_time_position), v_datetime_string); 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 v_max_partition_timestamp := v_min_partition_timestamp; BEGIN v_min_partition_timestamp := v_min_partition_timestamp - v_partition_interval; 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_interval; END; END IF; END LOOP; END IF; v_partition_timestamp := ARRAY[v_min_partition_timestamp]; IF p_order = 'ASC' THEN IF (v_start_control + p_batch_interval) >= (v_min_partition_timestamp + v_partition_interval) THEN v_max_partition_timestamp := v_min_partition_timestamp + v_partition_interval; ELSE v_max_partition_timestamp := v_start_control + p_batch_interval; END IF; ELSIF p_order = 'DESC' THEN -- Must be greater than max value still in parent table since query below grabs < max v_max_partition_timestamp := v_min_partition_timestamp + v_partition_interval; -- Make sure minimum doesn't underflow current partition minimum IF (v_start_control - p_batch_interval) >= v_min_partition_timestamp THEN v_min_partition_timestamp = v_start_control - p_batch_interval; END IF; ELSE RAISE EXCEPTION 'Invalid value for p_order. Must be ASC or DESC'; END IF; -- do some locking with timeout, if required IF p_lock_wait > 0 THEN v_lock_iter := 0; WHILE v_lock_iter <= 5 LOOP v_lock_iter := v_lock_iter + 1; BEGIN v_sql := 'SELECT * FROM ONLY ' || p_parent_table || ' WHERE '||v_control||' >= '||quote_literal(v_min_partition_timestamp)|| ' AND '||v_control||' < '||quote_literal(v_max_partition_timestamp) ||' FOR UPDATE NOWAIT'; EXECUTE v_sql; 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 RETURN -1; END IF; END IF; PERFORM @extschema@.create_partition_time(p_parent_table, v_partition_timestamp); -- This suffix generation code is in create_partition_time() as well v_partition_suffix := to_char(v_min_partition_timestamp, 'YYYY'); IF v_partition_interval < '1 year' AND v_partition_interval <> '1 week' THEN v_partition_suffix := v_partition_suffix ||'_'|| to_char(v_min_partition_timestamp, 'MM'); IF v_partition_interval < '1 month' AND v_partition_interval <> '1 week' THEN v_partition_suffix := v_partition_suffix ||'_'|| to_char(v_min_partition_timestamp, 'DD'); IF v_partition_interval < '1 day' THEN v_partition_suffix := v_partition_suffix || '_' || to_char(v_min_partition_timestamp, 'HH24MI'); IF v_partition_interval < '1 minute' THEN v_partition_suffix := v_partition_suffix || to_char(v_min_partition_timestamp, 'SS'); END IF; -- end < minute IF END IF; -- end < day IF END IF; -- end < month IF END IF; -- end < year IF IF v_partition_interval = '1 week' THEN v_partition_suffix := to_char(v_min_partition_timestamp, 'IYYY') || 'w' || to_char(v_min_partition_timestamp, 'IW'); END IF; -- "Q" is ignored in to_timestamp, so handle special case IF v_partition_interval = '3 months' AND (v_type = 'time') THEN v_year := to_char(v_min_partition_timestamp, 'YYYY'); v_quarter := to_char(v_min_partition_timestamp, 'Q'); v_partition_suffix := v_year || 'q' || v_quarter; END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname||'.'||tablename = p_parent_table; v_current_partition_name := @extschema@.check_name_length(v_parent_tablename, v_parent_schema, v_partition_suffix, TRUE); EXECUTE 'WITH partition_data AS ( DELETE FROM ONLY '||p_parent_table||' WHERE '||v_control||' >= '||quote_literal(v_min_partition_timestamp)|| ' AND '||v_control||' < '||quote_literal(v_max_partition_timestamp)||' RETURNING *) INSERT INTO '||v_current_partition_name||' SELECT * FROM partition_data'; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total_rows := v_total_rows + v_rowcount; IF v_rowcount = 0 THEN EXIT; END IF; END LOOP; PERFORM @extschema@.create_function_time(p_parent_table); RETURN v_total_rows; END $$; /* * Function to re-apply ownership & privileges on all child tables in a partition set using parent table as reference */ CREATE OR REPLACE FUNCTION reapply_privileges(p_parent_table text) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_all text[] := ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER']; v_child_owner text; v_child_table text; v_child_grant record; v_grant text; v_grantees text[]; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_match boolean; v_old_search_path text; v_parent_owner text; v_owner_sql text; v_revoke text[]; v_parent_grant record; v_sql text; v_step_id bigint; BEGIN SELECT jobmon INTO v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon IS NULL THEN RAISE EXCEPTION 'Given table is not managed by this extention: %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN RE-APPLYING PRIVILEGES TO ALL CHILD TABLES OF: '||p_parent_table); v_step_id := add_step(v_job_id, 'Setting new child table privileges'); END IF; SELECT tableowner INTO v_parent_owner FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; FOR v_child_table IN SELECT n.nspname||'.'||c.relname FROM pg_inherits i join pg_class c ON i.inhrelid = c.oid join pg_namespace n ON c.relnamespace = n.oid WHERE i.inhparent::regclass = p_parent_table::regclass ORDER BY i.inhrelid ASC LOOP IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'PENDING', 'Currently on child partition in ascending order: '||v_child_table); END IF; v_grantees := NULL; FOR v_parent_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = p_parent_table GROUP BY grantee LOOP -- Compare parent & child grants. Don't re-apply if it already exists v_match := false; FOR v_child_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = v_child_table GROUP BY grantee LOOP IF v_parent_grant.types = v_child_grant.types AND v_parent_grant.grantee = v_child_grant.grantee THEN v_match := true; END IF; END LOOP; IF v_match = false THEN EXECUTE 'GRANT '||array_to_string(v_parent_grant.types, ',')||' ON '||v_child_table||' TO '||v_parent_grant.grantee; SELECT array_agg(r) INTO v_revoke FROM (SELECT unnest(v_all) AS r EXCEPT SELECT unnest(v_parent_grant.types)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE '||array_to_string(v_revoke, ',')||' ON '||v_child_table||' FROM '||v_parent_grant.grantee||' CASCADE'; END IF; END IF; v_grantees := array_append(v_grantees, v_parent_grant.grantee::text); END LOOP; -- Revoke all privileges from roles that have none on the parent IF v_grantees IS NOT NULL THEN SELECT array_agg(r) INTO v_revoke FROM ( SELECT DISTINCT grantee::text AS r FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = v_child_table EXCEPT SELECT unnest(v_grantees)) x; IF v_revoke IS NOT NULL THEN EXECUTE 'REVOKE ALL ON '||v_child_table||' FROM '||array_to_string(v_revoke, ','); END IF; END IF; SELECT tableowner INTO v_child_owner FROM pg_tables WHERE schemaname ||'.'|| tablename = v_child_table; IF v_parent_owner <> v_child_owner THEN EXECUTE 'ALTER TABLE '||v_child_table||' OWNER TO '||v_parent_owner; END IF; END LOOP; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); PERFORM close_job(v_job_id); END IF; IF v_jobmon_schema IS NOT NULL THEN EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN RE-APPLYING PRIVILEGES TO ALL CHILD TABLES OF: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Function to manage pre-creation of the next partitions in a set. * 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_run_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. * Be aware that constraint exclusion may not work properly until an analyze on the partition set is run. */ CREATE OR REPLACE FUNCTION run_maintenance(p_parent_table text DEFAULT NULL, p_analyze boolean DEFAULT true, p_jobmon boolean DEFAULT true) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_adv_lock boolean; v_create_count int := 0; v_current_partition text; v_current_partition_id bigint; v_current_partition_timestamp timestamp; v_datetime_string text; v_drop_count int := 0; v_id_position int; 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 timestamp; v_next_partition_id bigint; v_next_partition_timestamp timestamp; v_old_search_path text; v_premade_count int; v_quarter text; v_row record; v_row_max_id record; v_row_sub record; v_step_id bigint; v_step_overflow_id bigint; v_step_serial_id bigint; v_sub_id_max bigint; v_sub_id_min bigint; v_sub_parent text; v_sub_timestamp_max timestamp; v_sub_timestamp_min timestamp; v_tablename text; v_tables_list_sql text; v_time_position int; v_year text; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman run_maintenance')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'Partman maintenance already running.'; RETURN; END IF; IF p_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN RUN MAINTENANCE'); v_step_id := add_step(v_job_id, 'Running maintenance loop'); END IF; v_tables_list_sql := 'SELECT parent_table , partition_type , partition_interval , control , premake , datetime_string , undo_in_progress FROM @extschema@.part_config'; IF p_parent_table IS NULL THEN v_tables_list_sql := v_tables_list_sql || ' WHERE use_run_maintenance = true'; ELSE v_tables_list_sql := v_tables_list_sql || format(' WHERE parent_table = %L', p_parent_table); END IF; FOR v_row IN EXECUTE v_tables_list_sql LOOP CONTINUE WHEN v_row.undo_in_progress; SELECT show_partitions INTO v_last_partition FROM @extschema@.show_partitions(v_row.parent_table, 'DESC') LIMIT 1; IF v_row.partition_type = 'time' OR v_row.partition_type = 'time-custom' THEN IF v_row.partition_type = 'time' THEN CASE WHEN v_row.partition_interval::interval = '15 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0); WHEN v_row.partition_interval::interval = '30 mins' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0); WHEN v_row.partition_interval::interval = '1 hour' THEN v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP); WHEN v_row.partition_interval::interval = '1 day' THEN v_current_partition_timestamp := date_trunc('day', CURRENT_TIMESTAMP); WHEN v_row.partition_interval::interval = '1 week' THEN v_current_partition_timestamp := date_trunc('week', CURRENT_TIMESTAMP); WHEN v_row.partition_interval::interval = '1 month' THEN v_current_partition_timestamp := date_trunc('month', CURRENT_TIMESTAMP); WHEN v_row.partition_interval::interval = '3 months' THEN v_current_partition_timestamp := date_trunc('quarter', CURRENT_TIMESTAMP); WHEN v_row.partition_interval::interval = '1 year' THEN v_current_partition_timestamp := date_trunc('year', CURRENT_TIMESTAMP); END CASE; ELSIF v_row.partition_type = 'time-custom' THEN SELECT child_table INTO v_current_partition FROM @extschema@.custom_time_partitions WHERE parent_table = v_row.parent_table AND partition_range @> CURRENT_TIMESTAMP; IF v_current_partition IS NULL THEN RAISE EXCEPTION 'Current time partition missing from custom_time_partitions config table for table % and timestamp %', CURRENT_TIMESTAMP, v_row.parent_table; END IF; v_time_position := (length(v_current_partition) - position('p_' in reverse(v_current_partition))) + 2; v_current_partition_timestamp := to_timestamp(substring(v_current_partition from v_time_position), v_row.datetime_string); END IF; -- Determine if this table is a child of a subpartition parent. If so, get limits of what child tables can be created based on parent suffix SELECT sub_min::timestamp, sub_max::timestamp INTO v_sub_timestamp_min, v_sub_timestamp_max FROM @extschema@.check_subpartition_limits(v_row.parent_table, 'time'); -- No need to run maintenance if it's outside the bounds of the top parent. IF v_sub_timestamp_min IS NOT NULL THEN IF v_current_partition_timestamp < v_sub_timestamp_min OR v_current_partition_timestamp > v_sub_timestamp_max THEN CONTINUE; END IF; END IF; v_time_position := (length(v_last_partition) - position('p_' in reverse(v_last_partition))) + 2; IF v_row.partition_interval::interval <> '3 months' OR (v_row.partition_interval::interval = '3 months' AND v_row.partition_type = 'time-custom') THEN v_last_partition_timestamp := to_timestamp(substring(v_last_partition from v_time_position), v_row.datetime_string); ELSE -- to_timestamp doesn't recognize 'Q' date string formater. Handle it v_year := split_part(substring(v_last_partition from v_time_position), 'q', 1); v_quarter := split_part(substring(v_last_partition from v_time_position), 'q', 2); CASE WHEN v_quarter = '1' THEN v_last_partition_timestamp := to_timestamp(v_year || '-01-01', 'YYYY-MM-DD'); WHEN v_quarter = '2' THEN v_last_partition_timestamp := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); WHEN v_quarter = '3' THEN v_last_partition_timestamp := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); WHEN v_quarter = '4' THEN v_last_partition_timestamp := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); END CASE; END IF; -- Check and see how many premade partitions there are. 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; -- 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 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 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', 'Child partition creation skippd for parent table '||v_partition_time); END IF; RAISE WARNING 'Attempted partition time interval is outside PostgreSQL''s supported time range. Child partition creation skipped for parent table %', v_row.parent_table; CONTINUE; END; v_last_partition_created := @extschema@.create_partition_time(v_row.parent_table, ARRAY[v_next_partition_timestamp], p_analyze); v_create_count := v_create_count + 1; PERFORM @extschema@.create_function_time(v_row.parent_table); -- Manage additonal constraints if set PERFORM @extschema@.apply_constraints(v_row.parent_table); v_premade_count = round(EXTRACT('epoch' FROM age(v_next_partition_timestamp, v_current_partition_timestamp)) / EXTRACT('epoch' FROM v_row.partition_interval::interval)); END LOOP; ELSIF v_row.partition_type = 'id' THEN -- 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 show_partitions FROM @extschema@.show_partitions(v_row.parent_table, 'DESC') LOOP EXECUTE 'SELECT '||v_row.control||' - ('||v_row.control||' % '||v_row.partition_interval::int||') FROM '||v_row_max_id.show_partitions||' WHERE '||v_row.control||' = (SELECT max('||v_row.control||') FROM '||v_row_max_id.show_partitions||')' INTO v_current_partition_id; IF v_current_partition_id IS NOT NULL THEN EXIT; END IF; END LOOP; -- Determine if this table is a child of a subpartition parent. If so, get limits to see if run_maintenance even needs to run for it. SELECT sub_min::bigint, sub_max::bigint INTO v_sub_id_min, v_sub_id_max FROM @extschema@.check_subpartition_limits(v_row.parent_table, 'id'); -- No need to run maintenance if it's outside the bounds of the top parent. IF v_sub_id_min IS NOT NULL THEN IF v_current_partition_id < v_sub_id_min OR v_current_partition_id > v_sub_id_max THEN CONTINUE; END IF; END IF; v_id_position := (length(v_last_partition) - position('p_' in reverse(v_last_partition))) + 2; v_last_partition_id = substring(v_last_partition from v_id_position)::bigint; v_next_partition_id := v_last_partition_id + v_row.partition_interval::bigint; WHILE ((v_next_partition_id - v_current_partition_id) / v_row.partition_interval::bigint) <= v_row.premake LOOP v_last_partition_created := @extschema@.create_partition_id(v_row.parent_table, ARRAY[v_next_partition_id], p_analyze); IF v_last_partition_created THEN PERFORM @extschema@.create_function_id(v_row.parent_table); PERFORM @extschema@.apply_constraints(v_row.parent_table); END IF; v_next_partition_id := v_next_partition_id + v_row.partition_interval::bigint; END LOOP; END IF; -- end main IF check for time or id END LOOP; -- end of creation loop -- Manage dropping old partitions if retention option is set FOR v_row IN SELECT parent_table FROM @extschema@.part_config WHERE retention IS NOT NULL AND undo_in_progress = false AND (partition_type = 'time' OR partition_type = 'time-custom') LOOP IF p_parent_table IS NULL THEN v_drop_count := v_drop_count + @extschema@.drop_partition_time(v_row.parent_table); ELSE -- Only run retention on table given in parameter IF p_parent_table <> v_row.parent_table THEN CONTINUE; ELSE v_drop_count := v_drop_count + @extschema@.drop_partition_time(v_row.parent_table); END IF; END IF; END LOOP; FOR v_row IN SELECT parent_table FROM @extschema@.part_config WHERE retention IS NOT NULL AND undo_in_progress = false AND partition_type = 'id' LOOP IF p_parent_table IS NULL THEN v_drop_count := v_drop_count + @extschema@.drop_partition_id(v_row.parent_table); ELSE -- Only run retention on table given in parameter IF p_parent_table <> v_row.parent_table THEN CONTINUE; ELSE v_drop_count := v_drop_count + @extschema@.drop_partition_id(v_row.parent_table); END IF; END IF; END LOOP; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Partition maintenance finished. '||v_create_count||' partitons made. '||v_drop_count||' partitions dropped.'); IF v_step_overflow_id IS NOT NULL OR v_step_serial_id IS NOT NULL THEN PERFORM fail_job(v_job_id); ELSE PERFORM close_job(v_job_id); END IF; EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN RUN MAINTENANCE'')', v_jobmon_schema) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Function to list all child partitions in a set. */ CREATE OR REPLACE FUNCTION show_partitions (p_parent_table text, p_order text DEFAULT 'ASC') RETURNS SETOF text LANGUAGE plpgsql STABLE SECURITY DEFINER AS $$ DECLARE v_datetime_string text; v_partition_interval text; v_type text; BEGIN IF p_order NOT IN ('ASC', 'DESC') THEN RAISE EXCEPTION 'p_order paramter must be one of the following values: ASC, DESC'; END IF; SELECT partition_type , partition_interval , datetime_string INTO v_type , v_partition_interval , v_datetime_string FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_type IN ('time', 'time-custom') THEN RETURN QUERY EXECUTE ' SELECT n.nspname::text ||''.''|| 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 = '||quote_literal(p_parent_table)||'::regclass ORDER BY to_timestamp(substring(c.relname from ((length(c.relname) - position(''p_'' in reverse(c.relname))) + 2) ), '||quote_literal(v_datetime_string)||') ' || p_order; ELSIF v_type = 'id' THEN RETURN QUERY EXECUTE ' SELECT n.nspname::text ||''.''|| 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 = '||quote_literal(p_parent_table)||'::regclass ORDER BY substring(c.relname from ((length(c.relname) - position(''p_'' in reverse(c.relname))) + 2) )::bigint ' || p_order; END IF; END $$; /* * Function to undo partitioning. * Will actually work on any parent/child table set, not just ones created by pg_partman. */ CREATE OR REPLACE FUNCTION undo_partition(p_parent_table text, p_batch_count int DEFAULT 1, p_keep_table boolean DEFAULT true, p_jobmon boolean DEFAULT true, p_lock_wait numeric DEFAULT 0) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_adv_lock boolean; v_batch_loop_count bigint := 0; v_child_count bigint; v_child_table text; v_copy_sql text; v_function_name text; v_job_id bigint; v_jobmon_schema text; v_lock_iter int := 1; v_lock_obtained boolean := FALSE; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_partition_interval interval; v_rowcount bigint; v_step_id bigint; v_total bigint := 0; v_trig_name text; v_undo_count int := 0; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman undo_partition')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'undo_partition already running.'; RETURN 0; END IF; IF p_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN UNDO PARTITIONING: '||p_parent_table); v_step_id := add_step(v_job_id, 'Undoing partitioning for table '||p_parent_table); END IF; -- Stops new time partitons 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; -- Stop data going into child tables and stop new id partitions from being made. SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; 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, v_parent_schema, '_part_trig_func', FALSE); SELECT tgname INTO v_trig_name FROM pg_catalog.pg_trigger t WHERE tgname = v_trig_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 'LOCK TABLE ONLY '||p_parent_table||' IN ACCESS EXCLUSIVE MODE NOWAIT'; 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'; RETURN -1; END IF; END IF; -- END p_lock_wait IF EXECUTE 'DROP TRIGGER IF EXISTS '||v_trig_name||' ON '||p_parent_table; END IF; -- END trigger IF v_lock_obtained := FALSE; -- reset for reuse later EXECUTE 'DROP FUNCTION IF EXISTS '||v_function_name||'()'; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Stopped partition creation process. Removed trigger & trigger function'); END IF; WHILE v_batch_loop_count < p_batch_count LOOP SELECT n.nspname||'.'||c.relname INTO v_child_table FROM pg_inherits i JOIN pg_class c ON i.inhrelid = c.oid JOIN pg_namespace n ON c.relnamespace = n.oid WHERE i.inhparent::regclass = p_parent_table::regclass ORDER BY i.inhrelid ASC; EXIT WHEN v_child_table IS NULL; EXECUTE 'SELECT count(*) FROM '||v_child_table INTO v_child_count; IF v_child_count = 0 THEN -- No rows left in this child table. Remove from partition set. -- lockwait timeout for table 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 'LOCK TABLE ONLY '||v_child_table||' IN ACCESS EXCLUSIVE MODE NOWAIT'; 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 child table for removal from partition set'; RETURN -1; END IF; END IF; -- END p_lock_wait IF v_lock_obtained := FALSE; -- reset for reuse later EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; IF p_keep_table = false THEN EXECUTE 'DROP TABLE '||v_child_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table DROPPED. Moved '||coalesce(v_rowcount, 0)||' rows to parent'); END IF; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table UNINHERITED, not DROPPED. Copied '||coalesce(v_rowcount, 0)||' rows to parent'); END IF; END IF; v_undo_count := v_undo_count + 1; CONTINUE; END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing child partition: '||v_child_table); END IF; -- do some locking with timeout, if required 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 'SELECT * FROM '|| v_child_table ||' FOR UPDATE NOWAIT'; 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 batch of rows to move'; RETURN -1; END IF; END IF; v_copy_sql := 'INSERT INTO '||p_parent_table||' SELECT * FROM '||v_child_table; EXECUTE v_copy_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total := v_total + v_rowcount; EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; IF p_keep_table = false THEN EXECUTE 'DROP TABLE '||v_child_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table DROPPED. Moved '||v_rowcount||' rows to parent'); END IF; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table UNINHERITED, not DROPPED. Copied '||v_rowcount||' rows to parent'); END IF; END IF; v_batch_loop_count := v_batch_loop_count + 1; v_undo_count := v_undo_count + 1; END LOOP; IF v_undo_count = 0 THEN -- FOR loop never ran, so there's no child tables left. DELETE FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing config from pg_partman (if it existed)'); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; RAISE NOTICE 'Copied % row(s) from % child table(s) to the parent: %', v_total, v_undo_count, p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Final stats'); PERFORM update_step(v_step_id, 'OK', 'Copied '||v_total||' row(s) from '||v_undo_count||' child table(s) to the parent'); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_total; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN UNDO PARTITIONING: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Function to undo id-based partitioning created by this extension */ CREATE OR REPLACE FUNCTION undo_partition_id(p_parent_table text, p_batch_count int DEFAULT 1, p_batch_interval bigint DEFAULT NULL, p_keep_table boolean DEFAULT true, p_lock_wait numeric DEFAULT 0) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_adv_lock boolean; v_batch_loop_count int := 0; v_child_loop_total bigint := 0; v_child_min bigint; v_child_table text; v_control text; v_exists int; v_function_name text; v_inner_loop_count int; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_lock_iter int := 1; v_lock_obtained boolean := FALSE; v_move_sql text; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_partition_interval bigint; v_row record; v_rowcount bigint; v_step_id bigint; v_sub_count int; v_trig_name text; v_total bigint := 0; v_undo_count int := 0; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman undo_partition_id')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'undo_partition_id already running.'; RETURN 0; END IF; SELECT partition_interval::bigint , control , jobmon INTO v_partition_interval , v_control , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND partition_type = 'id'; IF v_partition_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; -- Check if any child tables are themselves partitioned or part of an inheritance tree. Prevent undo at this level if so. -- Need to either lock child tables at all levels or handle the proper removal of triggers on all child tables first -- before multi-level undo can be performed safely. FOR v_row IN SELECT show_partitions AS child_table FROM @extschema@.show_partitions(p_parent_table) LOOP SELECT count(*) INTO v_sub_count FROM pg_catalog.pg_inherits WHERE inhparent::regclass = v_row.child_table::regclass; 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.child_table; END IF; END LOOP; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN UNDO PARTITIONING: '||p_parent_table); v_step_id := add_step(v_job_id, 'Undoing partitioning for table '||p_parent_table); END IF; IF p_batch_interval IS NULL THEN p_batch_interval := v_partition_interval; END IF; -- Stops new time partitons 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; -- Stop data going into child tables and stop new id partitions from being made. SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; 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, v_parent_schema, '_part_trig_func', FALSE); SELECT tgname INTO v_trig_name FROM pg_catalog.pg_trigger t WHERE tgname = v_trig_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 'LOCK TABLE ONLY '||p_parent_table||' IN ACCESS EXCLUSIVE MODE NOWAIT'; 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'; RETURN -1; END IF; END IF; -- END p_lock_wait IF EXECUTE 'DROP TRIGGER IF EXISTS '||v_trig_name||' ON '||p_parent_table; END IF; -- END trigger IF v_lock_obtained := FALSE; -- reset for reuse later EXECUTE 'DROP FUNCTION IF EXISTS '||v_function_name||'()'; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Stopped partition creation process. Removed trigger & trigger function'); END IF; <> WHILE v_batch_loop_count < p_batch_count LOOP SELECT n.nspname||'.'||c.relname INTO v_child_table FROM pg_inherits i JOIN pg_class c ON i.inhrelid = c.oid JOIN pg_namespace n ON c.relnamespace = n.oid WHERE i.inhparent::regclass = p_parent_table::regclass ORDER BY i.inhrelid ASC; EXIT WHEN v_child_table IS NULL; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing child partition: '||v_child_table); END IF; EXECUTE 'SELECT min('||v_control||') FROM '||v_child_table INTO v_child_min; IF v_child_min IS NULL THEN -- No rows left in this child table. Remove from partition set. -- lockwait timeout for table 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 'LOCK TABLE ONLY '||v_child_table||' IN ACCESS EXCLUSIVE MODE NOWAIT'; 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 child table for removal from partition set'; RETURN -1; END IF; END IF; -- END p_lock_wait IF v_lock_obtained := FALSE; -- reset for reuse later EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; IF p_keep_table = false THEN EXECUTE 'DROP TABLE '||v_child_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table DROPPED. Moved '||v_child_loop_total||' rows to parent'); END IF; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table UNINHERITED, not DROPPED. Moved '||v_child_loop_total||' rows to parent'); END IF; END IF; v_undo_count := v_undo_count + 1; CONTINUE outer_child_loop; END IF; v_inner_loop_count := 1; v_child_loop_total := 0; <> LOOP -- lockwait timeout for row batches 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 'SELECT * FROM ' || v_child_table || ' WHERE '||v_control||' <= '||quote_literal(v_child_min + (p_batch_interval * v_inner_loop_count)) ||' FOR UPDATE NOWAIT'; 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 batch of rows to move'; RETURN -1; END IF; END IF; -- Get everything from the current child minimum up to the multiples of the given interval v_move_sql := 'WITH move_data AS (DELETE FROM '||v_child_table|| ' WHERE '||v_control||' <= '||quote_literal(v_child_min + (p_batch_interval * v_inner_loop_count))||' RETURNING *) INSERT INTO '||p_parent_table||' SELECT * FROM move_data'; EXECUTE v_move_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total := v_total + v_rowcount; v_child_loop_total := v_child_loop_total + v_rowcount; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Moved '||v_child_loop_total||' rows to parent.'); END IF; EXIT inner_child_loop WHEN v_rowcount = 0; -- exit before loop incr if table is empty v_inner_loop_count := v_inner_loop_count + 1; v_batch_loop_count := v_batch_loop_count + 1; EXIT outer_child_loop WHEN v_batch_loop_count >= p_batch_count; -- Exit outer FOR loop if p_batch_count is reached END LOOP inner_child_loop; END LOOP outer_child_loop; IF v_batch_loop_count < p_batch_count THEN -- FOR loop never ran, so there's no child tables left. DELETE FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing config from pg_partman'); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; RAISE NOTICE 'Copied % row(s) to the parent. Removed % partitions.', v_total, v_undo_count; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Final stats'); PERFORM update_step(v_step_id, 'OK', 'Copied '||v_total||' row(s) to the parent. Removed '||v_undo_count||' partitions.'); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_total; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN UNDO PARTITIONING: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Function to undo time-based partitioning created by this extension */ CREATE OR REPLACE FUNCTION undo_partition_time(p_parent_table text, p_batch_count int DEFAULT 1, p_batch_interval interval DEFAULT NULL, p_keep_table boolean DEFAULT true, p_lock_wait numeric DEFAULT 0) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_adv_lock boolean; v_batch_loop_count int := 0; v_child_min timestamptz; v_child_loop_total bigint := 0; v_child_table text; v_control text; v_function_name text; v_inner_loop_count int; v_lock_iter int := 1; v_lock_obtained boolean := FALSE; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_move_sql text; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_partition_interval interval; v_row record; v_rowcount bigint; v_step_id bigint; v_sub_count int; v_total bigint := 0; v_trig_name text; v_type text; v_undo_count int := 0; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman undo_partition_time')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'undo_partition_time already running.'; RETURN 0; END IF; SELECT partition_type , partition_interval::interval , control , jobmon INTO v_type , v_partition_interval , v_control , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (partition_type = 'time' OR partition_type = 'time-custom'); IF v_partition_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; -- Check if any child tables are themselves partitioned or part of an inheritance tree. Prevent undo at this level if so. -- Need to either lock child tables at all levels or handle the proper removal of triggers on all child tables first -- before multi-level undo can be performed safely. FOR v_row IN SELECT show_partitions AS child_table FROM @extschema@.show_partitions(p_parent_table) LOOP SELECT count(*) INTO v_sub_count FROM pg_catalog.pg_inherits WHERE inhparent::regclass = v_row.child_table::regclass; 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.child_table; END IF; END LOOP; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE 'SELECT set_config(''search_path'',''@extschema@,'||v_jobmon_schema||''',''false'')'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN UNDO PARTITIONING: '||p_parent_table); v_step_id := add_step(v_job_id, 'Undoing partitioning for table '||p_parent_table); END IF; IF p_batch_interval IS NULL THEN p_batch_interval := v_partition_interval; END IF; -- Stops new time partitons 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; -- Stop data going into child tables. SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; 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, v_parent_schema, '_part_trig_func', FALSE); SELECT tgname INTO v_trig_name FROM pg_catalog.pg_trigger t WHERE tgname = v_trig_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 'LOCK TABLE ONLY '||p_parent_table||' IN ACCESS EXCLUSIVE MODE NOWAIT'; 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'; RETURN -1; END IF; END IF; -- END p_lock_wait IF EXECUTE 'DROP TRIGGER IF EXISTS '||v_trig_name||' ON '||p_parent_table; END IF; -- END trigger IF v_lock_obtained := FALSE; -- reset for reuse later EXECUTE 'DROP FUNCTION IF EXISTS '||v_function_name||'()'; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Stopped partition creation process. Removed trigger & trigger function'); END IF; <> WHILE v_batch_loop_count < p_batch_count LOOP SELECT n.nspname||'.'||c.relname INTO v_child_table FROM pg_inherits i JOIN pg_class c ON i.inhrelid = c.oid JOIN pg_namespace n ON c.relnamespace = n.oid WHERE i.inhparent::regclass = p_parent_table::regclass ORDER BY i.inhrelid ASC; EXIT WHEN v_child_table IS NULL; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing child partition: '||v_child_table); END IF; EXECUTE 'SELECT min('||v_control||') FROM '||v_child_table INTO v_child_min; IF v_child_min IS NULL THEN -- No rows left in this child table. Remove from partition set. -- lockwait timeout for table 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 'LOCK TABLE ONLY '||v_child_table||' IN ACCESS EXCLUSIVE MODE NOWAIT'; 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 child table for removal from partition set'; RETURN -1; END IF; END IF; -- END p_lock_wait IF v_lock_obtained := FALSE; -- reset for reuse later EXECUTE 'ALTER TABLE '||v_child_table||' NO INHERIT ' || p_parent_table; IF p_keep_table = false THEN EXECUTE 'DROP TABLE '||v_child_table; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table DROPPED. Moved '||v_child_loop_total||' rows to parent'); END IF; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Child table UNINHERITED, not DROPPED. Moved '||v_child_loop_total||' rows to parent'); END IF; END IF; IF v_type = 'time-custom' THEN DELETE FROM @extschema@.custom_time_partitions WHERE parent_table = p_parent_table AND child_table = v_child_table; END IF; v_undo_count := v_undo_count + 1; CONTINUE outer_child_loop; END IF; v_inner_loop_count := 1; v_child_loop_total := 0; <> LOOP -- do some locking with timeout, if required 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 'SELECT * FROM ' || v_child_table || ' WHERE '||v_control||' <= '||quote_literal(v_child_min + (p_batch_interval * v_inner_loop_count)) ||' FOR UPDATE NOWAIT'; 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 batch of rows to move'; RETURN -1; END IF; END IF; -- Get everything from the current child minimum up to the multiples of the given interval v_move_sql := 'WITH move_data AS (DELETE FROM '||v_child_table|| ' WHERE '||v_control||' <= '||quote_literal(v_child_min + (p_batch_interval * v_inner_loop_count))||' RETURNING *) INSERT INTO '||p_parent_table||' SELECT * FROM move_data'; EXECUTE v_move_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total := v_total + v_rowcount; v_child_loop_total := v_child_loop_total + v_rowcount; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Moved '||v_child_loop_total||' rows to parent.'); END IF; EXIT inner_child_loop WHEN v_rowcount = 0; -- exit before loop incr if table is empty v_inner_loop_count := v_inner_loop_count + 1; v_batch_loop_count := v_batch_loop_count + 1; EXIT outer_child_loop WHEN v_batch_loop_count >= p_batch_count; -- Exit outer FOR loop if p_batch_count is reached END LOOP inner_child_loop; END LOOP outer_child_loop; IF v_batch_loop_count < p_batch_count THEN -- FOR loop never ran, so there's no child tables left. DELETE FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing config from pg_partman'); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; RAISE NOTICE 'Copied % row(s) to the parent. Removed % partitions.', v_total, v_undo_count; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Final stats'); PERFORM update_step(v_step_id, 'OK', 'Copied '||v_total||' row(s) to the parent. Removed '||v_undo_count||' partitions.'); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE 'SELECT set_config(''search_path'','''||v_old_search_path||''',''false'')'; END IF; RETURN v_total; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN UNDO PARTITIONING: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Check if parent table is a subpartition of an already existing id based partition set managed by pg_partman * If so, limit what child tables can be created based on parent suffix */ CREATE OR REPLACE FUNCTION check_subpartition_limits(p_parent_table text, p_type text, OUT sub_min text, OUT sub_max text) RETURNS record LANGUAGE plpgsql AS $$ DECLARE v_datetime_string text; v_id_position int; v_partition_interval interval; v_quarter text; v_sub_id_max bigint; v_sub_id_min bigint; v_sub_timestamp_max timestamp; v_sub_timestamp_min timestamp; v_time_position int; v_top_datetime_string text; v_top_interval text; v_top_parent text; v_top_type text; v_year text; BEGIN -- CTE query is done individually for each type (time, id) because it should return NULL if the top parent is not the same type in a subpartition set (id->time or time->id) IF p_type = 'id' THEN WITH top_oid AS ( SELECT i.inhparent AS top_parent_oid FROM pg_catalog.pg_class c JOIN pg_catalog.pg_inherits i ON c.oid = i.inhrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname||'.'||c.relname = p_parent_table ) SELECT n.nspname||'.'||c.relname, p.datetime_string, p.partition_interval, p.partition_type INTO v_top_parent, v_top_datetime_string, v_top_interval, v_top_type FROM pg_catalog.pg_class c JOIN top_oid t ON c.oid = t.top_parent_oid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace JOIN @extschema@.part_config p ON p.parent_table = n.nspname||'.'||c.relname WHERE c.oid = t.top_parent_oid AND p.partition_type = 'id'; IF v_top_parent IS NOT NULL THEN v_id_position := (length(p_parent_table) - position('p_' in reverse(p_parent_table))) + 2; v_sub_id_min := substring(p_parent_table from v_id_position)::bigint; v_sub_id_max := (v_sub_id_min + v_top_interval::bigint) - 1; sub_min := v_sub_id_min::text; sub_max := v_sub_id_max::text; END IF; ELSIF p_type = 'time' THEN WITH top_oid AS ( SELECT i.inhparent AS top_parent_oid FROM pg_catalog.pg_class c JOIN pg_catalog.pg_inherits i ON c.oid = i.inhrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname||'.'||c.relname = p_parent_table ) SELECT n.nspname||'.'||c.relname, p.datetime_string, p.partition_interval, p.partition_type INTO v_top_parent, v_top_datetime_string, v_top_interval, v_top_type FROM pg_catalog.pg_class c JOIN top_oid t ON c.oid = t.top_parent_oid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace JOIN @extschema@.part_config p ON p.parent_table = n.nspname||'.'||c.relname WHERE c.oid = t.top_parent_oid AND p.partition_type = 'time' OR p.partition_type = 'time-custom'; IF v_top_parent IS NOT NULL THEN v_time_position := (length(p_parent_table) - position('p_' in reverse(p_parent_table))) + 2; IF v_top_interval::interval <> '3 months' OR (v_top_interval::interval = '3 months' AND v_top_type = 'time-custom') THEN v_sub_timestamp_min := to_timestamp(substring(p_parent_table from v_time_position), v_top_datetime_string); ELSE -- to_timestamp doesn't recognize 'Q' date string formater. Handle it v_year := split_part(substring(p_parent_table from v_time_position), 'q', 1); v_quarter := split_part(substring(p_parent_table from v_time_position), 'q', 2); CASE WHEN v_quarter = '1' THEN v_sub_timestamp_min := to_timestamp(v_year || '-01-01', 'YYYY-MM-DD'); WHEN v_quarter = '2' THEN v_sub_timestamp_min := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); WHEN v_quarter = '3' THEN v_sub_timestamp_min := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); WHEN v_quarter = '4' THEN v_sub_timestamp_min := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); END CASE; END IF; v_sub_timestamp_max = (v_sub_timestamp_min + v_top_interval::interval) - '1 sec'::interval; sub_min := v_sub_timestamp_min::text; sub_max := v_sub_timestamp_max::text; END IF; ELSE RAISE EXCEPTION 'Invalid type given as parameter to check_subpartition_limits()'; END IF; RETURN; END $$; pg_partman-2.2.2/updates/pg_partman--2.0.0--2.1.0.sql000066400000000000000000006314071262146621700214260ustar00rootroot00000000000000-- 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". ALTER TABLE @extschema@.part_config ADD COLUMN sub_partition_set_full boolean NOT NULL DEFAULT false; CREATE TEMP TABLE partman_preserve_privs_temp (statement text); INSERT INTO partman_preserve_privs_temp SELECT 'GRANT EXECUTE ON FUNCTION @extschema@.apply_foreign_keys(text, text, bigint, boolean) TO '||array_to_string(array_agg(grantee::text), ',')||';' FROM information_schema.routine_privileges WHERE routine_schema = '@extschema@' AND routine_name = 'apply_foreign_keys'; INSERT INTO partman_preserve_privs_temp SELECT 'GRANT EXECUTE ON FUNCTION @extschema@.show_partitions(text, text) TO '||array_to_string(array_agg(grantee::text), ',')||';' FROM information_schema.routine_privileges WHERE routine_schema = '@extschema@' AND routine_name = 'show_partitions'; INSERT INTO partman_preserve_privs_temp SELECT 'GRANT EXECUTE ON FUNCTION @extschema@.check_name_length(text, text, boolean) TO '||array_to_string(array_agg(grantee::text), ',')||';' FROM information_schema.routine_privileges WHERE routine_schema = '@extschema@' AND routine_name = 'check_name_length'; INSERT INTO partman_preserve_privs_temp SELECT 'GRANT EXECUTE ON FUNCTION @extschema@.create_function_id(text, bigint) TO '||array_to_string(array_agg(grantee::text), ',')||';' FROM information_schema.routine_privileges WHERE routine_schema = '@extschema@' AND routine_name = 'create_function_id'; INSERT INTO partman_preserve_privs_temp SELECT 'GRANT EXECUTE ON FUNCTION @extschema@.create_function_time(text, bigint) TO '||array_to_string(array_agg(grantee::text), ',')||';' FROM information_schema.routine_privileges WHERE routine_schema = '@extschema@' AND routine_name = 'create_function_time'; INSERT INTO partman_preserve_privs_temp SELECT 'GRANT EXECUTE ON FUNCTION @extschema@.apply_constraints(text, text, boolean, bigint, boolean) TO '||array_to_string(array_agg(grantee::text), ',')||';' FROM information_schema.routine_privileges WHERE routine_schema = '@extschema@' AND routine_name = 'apply_constraints'; DROP FUNCTION @extschema@.apply_foreign_keys(text, text, boolean); DROP FUNCTION @extschema@.show_partitions (text, text); DROP FUNCTION @extschema@.check_name_length (text, text, text, boolean); DROP FUNCTION @extschema@.create_function_id(text); DROP FUNCTION @extschema@.create_function_time(text); DROP FUNCTION @extschema@.apply_constraints(text, text, boolean, boolean); -- This function goes in table.sql file /* * Ensure that sub-partitioned tables that are themselves sub-partitions have the same configuration options set when they are part of the same inheritance tree */ CREATE OR REPLACE FUNCTION @extschema@.check_subpart_sameconfig(text) RETURNS boolean LANGUAGE sql STABLE AS $$ WITH parent_info AS ( SELECT c1.oid FROM pg_catalog.pg_class c1 JOIN pg_catalog.pg_namespace n1 ON c1.relnamespace = n1.oid WHERE n1.nspname||'.'||c1.relname = $1 ) , child_tables AS ( SELECT n.nspname||'.'||c.relname AS 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 JOIN parent_info pi ON h.inhparent = pi.oid ) SELECT CASE WHEN count(*) <= 1 THEN true WHEN count(*) > 1 THEN false END FROM ( 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_use_run_maintenance , sub_jobmon FROM @extschema@.part_config_sub a JOIN child_tables b on a.sub_parent = b.tablename) x; $$; /* * Apply constraints managed by partman extension */ CREATE OR REPLACE FUNCTION apply_constraints(p_parent_table text, p_child_table text DEFAULT NULL, p_analyze boolean DEFAULT FALSE, p_job_id bigint DEFAULT NULL, p_debug boolean DEFAULT FALSE) RETURNS void LANGUAGE plpgsql AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_child_table text; v_child_tablename text; v_col text; v_constraint_cols text[]; v_constraint_col_type text; v_constraint_name text; v_constraint_values record; v_datetime_string text; v_existing_constraint_name text; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_last_partition text; v_last_partition_id int; v_last_partition_timestamp timestamp; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_partition_interval text; v_partition_suffix text; v_premake int; v_sql text; v_step_id bigint; v_suffix_position int; v_type text; BEGIN SELECT partition_type , partition_interval , premake , datetime_string , constraint_cols , jobmon INTO v_type , v_partition_interval , v_premake , v_datetime_string , v_constraint_cols , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_constraint_cols IS NULL THEN IF p_debug THEN RAISE NOTICE 'Given parent table (%) not set up for constraint management (constraint_cols is NULL)', p_parent_table; END IF; -- Returns silently to allow this function to be simply called by maintenance processes without having to check if config options are set. RETURN; END IF; SELECT partition_tablename INTO v_last_partition FROM @extschema@.show_partitions(p_parent_table, 'DESC') LIMIT 1; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', '@extschema@,'||v_jobmon_schema, 'false'); END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN IF p_job_id IS NULL THEN v_job_id := add_job(format('PARTMAN CREATE CONSTRAINT: %s', p_parent_table)); ELSE v_job_id = p_job_id; END IF; END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; -- If p_child_table is null, figure out the partition that is the one right before the premake value backwards. IF p_child_table IS NULL THEN IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Applying additional constraints: Automatically determining most recent child on which to apply constraints'); END IF; v_suffix_position := (length(v_last_partition) - position('p_' in reverse(v_last_partition))) + 2; IF v_type IN ('time', 'time-custom') THEN v_last_partition_timestamp := to_timestamp(substring(v_last_partition from v_suffix_position), v_datetime_string); v_partition_suffix := to_char(v_last_partition_timestamp - (v_partition_interval::interval * ((v_premake * 2)+1) ), v_datetime_string); ELSIF v_type = 'id' THEN v_last_partition_id := substring(v_last_partition from v_suffix_position)::int; v_partition_suffix := (v_last_partition_id - (v_partition_interval::int * ((v_premake * 2)+1) ))::text; END IF; v_child_table := v_parent_schema ||'.'|| @extschema@.check_name_length(v_parent_tablename, v_partition_suffix, TRUE); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', format('Target child table: %s.%s', v_parent_schema, v_child_table)); END IF; ELSE v_child_table := p_child_table; 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; SELECT tablename INTO v_child_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_child_table; IF v_child_tablename IS NULL THEN IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'NOTICE', format('Target child table (%s) does not exist. Skipping constraint creation.', v_child_table)); PERFORM close_job(v_job_id); EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', v_old_search_path, 'false'); END IF; IF p_debug THEN RAISE NOTICE 'Target child table (%) does not exist. Skipping constraint creation.', v_child_table; END IF; RETURN; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; FOREACH v_col IN ARRAY v_constraint_cols LOOP SELECT con.conname INTO v_existing_constraint_name 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 WHERE c.relname = v_child_tablename AND n.nspname = v_parent_schema AND con.conname LIKE 'partmanconstr_%' AND con.contype = 'c' AND a.attname = v_col AND ARRAY[a.attnum] <@ con.conkey AND a.attisdropped = false; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, format('Applying additional constraints: Applying new constraint on column: %s', v_col)); END IF; IF v_existing_constraint_name IS NOT NULL THEN IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'NOTICE', format('Partman managed constraint already exists on this table (%s) and column (%s). Skipping creation.', v_child_table, v_col)); END IF; RAISE WARNING 'Partman managed constraint already exists on this table (%) and column (%). Skipping creation.', v_child_table, v_col ; CONTINUE; END IF; -- 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; IF v_constraint_values IS NOT NULL THEN v_sql := format('ALTER TABLE %I.%I ADD CONSTRAINT %I CHECK (%I >= %L AND %I <= %L)' , v_parent_schema , v_child_tablename , v_constraint_name , v_col , v_constraint_values.min , v_col , v_constraint_values.max); IF p_debug THEN RAISE NOTICE 'Constraint creation query: %', v_sql; END IF; EXECUTE v_sql; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', format('New constraint created: %s', v_sql)); END IF; ELSE IF p_debug THEN RAISE NOTICE 'Given column (%) contains all NULLs. No constraint created', v_col; END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'NOTICE', format('Given column (%s) contains all NULLs. No constraint created', v_col)); END IF; END IF; END LOOP; IF p_analyze THEN IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, format('Applying additional constraints: Running analyze on partition set: %s', p_parent_table)); END IF; IF p_debug THEN RAISE NOTICE 'Running analyze on partition set: %', 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 PERFORM close_job(v_job_id); EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', v_old_search_path, 'false'); END IF; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN CREATE CONSTRAINT: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Apply foreign keys that exist on the given parent to the given child table */ CREATE OR REPLACE FUNCTION apply_foreign_keys(p_parent_table text, p_child_table text, p_job_id bigint DEFAULT NULL, p_debug boolean DEFAULT false) RETURNS void LANGUAGE plpgsql AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_count int := 0; v_job_id bigint; v_jobmon text; v_jobmon_schema text; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_ref_schema text; v_ref_table text; v_row record; v_schemaname text; v_sql text; v_step_id bigint; v_tablename text; BEGIN SELECT jobmon INTO v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', '@extschema@,'||v_jobmon_schema, 'false'); END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN IF p_job_id IS NULL THEN v_job_id := add_job(format('PARTMAN APPLYING FOREIGN KEYS: %s', p_parent_table)); ELSE -- Don't create a new job, add steps into given job v_job_id := p_job_id; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, format('Applying foreign keys to %s if they exist on parent', p_child_table)); END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname||'.'||tablename = p_parent_table; SELECT schemaname, tablename INTO v_schemaname, v_tablename FROM pg_catalog.pg_tables WHERE schemaname||'.'||tablename = p_child_table; IF v_tablename IS NULL THEN IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'CRITICAL', format('Target child table (%s) does not exist.', p_child_table)); PERFORM fail_job(v_job_id); EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', v_old_search_path, 'false'); END IF; RAISE EXCEPTION 'Target child table (%) does not exist.', p_child_table; RETURN; END IF; FOR v_row 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 JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid WHERE c.relname = v_parent_tablename AND n.nspname = v_parent_schema AND contype = 'f' LOOP v_sql := format('ALTER TABLE %I.%I ADD %s' , v_schemaname , v_tablename , v_row.constraint_def); IF p_debug THEN RAISE NOTICE 'Constraint creation query: %', v_sql; END IF; EXECUTE v_sql; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'FK applied'); END IF; v_count := v_count + 1; END LOOP; IF v_count = 0 AND v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'No FKs found on parent'); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', v_old_search_path, 'false'); END IF; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN CREATE APPLYING FOREIGN KEYS: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Truncate the name of the given object if it is greater than the postgres default max (63 characters). * Also appends given suffix and schema if given and truncates the name so that the entire suffix will fit. * Returns original name with schema given if it doesn't require truncation */ CREATE FUNCTION check_name_length (p_object_name text, p_suffix text DEFAULT NULL, p_table_partition boolean DEFAULT FALSE) RETURNS text LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_new_length int; v_new_name text; BEGIN IF p_table_partition IS TRUE AND (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; END IF; RETURN v_new_name; END $$; /* * Function to monitor for data getting inserted into parent tables managed by extension */ CREATE OR REPLACE FUNCTION check_parent() RETURNS SETOF @extschema@.check_parent_table LANGUAGE plpgsql STABLE SECURITY DEFINER AS $$ DECLARE v_count bigint = 0; v_row record; v_schemaname text; v_tablename text; v_sql text; v_trouble @extschema@.check_parent_table%rowtype; BEGIN FOR v_row IN SELECT DISTINCT parent_table FROM @extschema@.part_config LOOP SELECT schemaname, tablename INTO v_schemaname, v_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'||tablename = v_row.parent_table; v_sql := format('SELECT count(1) AS n FROM ONLY %I.%I', v_schemaname, v_tablename); EXECUTE v_sql INTO v_count; IF v_count > 0 THEN v_trouble.parent_table := v_schemaname ||'.'|| v_tablename; v_trouble.count := v_count; RETURN NEXT v_trouble; END IF; v_count := 0; END LOOP; RETURN; END $$; /* * Create the trigger function for the parent table of an id-based partition set */ CREATE OR REPLACE FUNCTION create_function_id(p_parent_table text, p_job_id bigint DEFAULT NULL) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_control text; v_count int; v_current_partition_name text; v_current_partition_id bigint; v_datetime_string text; v_final_partition_id bigint; v_function_name text; v_higher_parent text := p_parent_table; v_id_position int; v_job_id bigint; v_jobmon text; v_jobmon_schema text; v_last_partition text; v_max bigint; v_next_partition_id bigint; v_next_partition_name text; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_partition_interval bigint; v_premake int; v_prev_partition_id bigint; v_prev_partition_name text; v_row_max_id record; v_run_maint boolean; v_step_id bigint; v_top_parent text := p_parent_table; v_trig_func text; BEGIN SELECT partition_interval::bigint , control , premake , use_run_maintenance , jobmon INTO v_partition_interval , v_control , v_premake , v_run_maint , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND partition_type = 'id'; IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; SELECT partition_tablename INTO v_last_partition FROM @extschema@.show_partitions(p_parent_table, 'DESC') LIMIT 1; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', '@extschema@,'||v_jobmon_schema, 'false'); END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN IF p_job_id IS NULL THEN v_job_id := add_job(format('PARTMAN CREATE FUNCTION: %s', p_parent_table)); ELSE v_job_id = p_job_id; END IF; v_step_id := add_step(v_job_id, format('Creating partition function for table %s', p_parent_table)); END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; v_function_name := @extschema@.check_name_length(v_parent_tablename, '_part_trig_func', FALSE); -- Get the highest level top parent if multi-level partitioned in order to get proper max() value below WHILE v_higher_parent IS NOT NULL LOOP -- initially set in DECLARE WITH top_oid AS ( SELECT i.inhparent AS top_parent_oid FROM pg_catalog.pg_inherits i JOIN pg_catalog.pg_class c ON c.oid = i.inhrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname||'.'||c.relname = v_higher_parent ) SELECT n.nspname||'.'||c.relname INTO v_higher_parent 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 JOIN @extschema@.part_config p ON p.parent_table = n.nspname||'.'||c.relname WHERE p.partition_type = 'id'; IF v_higher_parent IS NOT NULL THEN -- initially set in DECLARE v_top_parent := v_higher_parent; END IF; END 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 parent. FOR v_row_max_id IN SELECT partition_schemaname, partition_tablename FROM @extschema@.show_partitions(v_top_parent, 'DESC') LOOP EXECUTE format('SELECT max(%I) FROM %I.%I', v_control, v_row_max_id.partition_schemaname, v_row_max_id.partition_tablename) INTO v_max; IF v_max IS NOT NULL THEN EXIT; END IF; END LOOP; IF v_max IS NULL THEN v_max := 0; END IF; v_current_partition_id = v_max - (v_max % v_partition_interval); v_next_partition_id := v_current_partition_id + v_partition_interval; v_current_partition_name := @extschema@.check_name_length(v_parent_tablename, v_current_partition_id::text, TRUE); v_trig_func := format('CREATE OR REPLACE FUNCTION %I.%I() RETURNS trigger LANGUAGE plpgsql AS $t$ DECLARE v_count int; v_current_partition_id bigint; v_current_partition_name text; v_id_position int; v_last_partition text := %L; v_next_partition_id bigint; v_next_partition_name text; v_partition_created boolean; BEGIN IF TG_OP = ''INSERT'' THEN IF NEW.%I >= %s AND NEW.%I < %s THEN ' , v_parent_schema , v_function_name , v_last_partition , v_control , v_current_partition_id , v_control , v_next_partition_id ); SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname = v_parent_schema AND tablename = v_current_partition_name; IF v_count > 0 THEN v_trig_func := v_trig_func || format(' INSERT INTO %I.%I VALUES (NEW.*); ', v_parent_schema, v_current_partition_name); ELSE v_trig_func := v_trig_func || ' -- Child table for current values does not exist in this partition set, so write to parent RETURN NEW;'; END IF; FOR i IN 1..v_premake LOOP v_prev_partition_id := v_current_partition_id - (v_partition_interval * i); v_next_partition_id := v_current_partition_id + (v_partition_interval * i); v_final_partition_id := v_next_partition_id + v_partition_interval; v_prev_partition_name := @extschema@.check_name_length(v_parent_tablename, v_prev_partition_id::text, TRUE); v_next_partition_name := @extschema@.check_name_length(v_parent_tablename, v_next_partition_id::text, TRUE); -- Check that child table exist before making a rule to insert to them. -- Handles edge case of changing premake immediately after running create_parent(). SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname = v_parent_schema AND tablename = v_prev_partition_name; IF v_count > 0 THEN -- Only handle previous partitions if they're starting above zero IF v_prev_partition_id >= 0 THEN v_trig_func := v_trig_func ||format(' ELSIF NEW.%I >= %s AND NEW.%I < %s THEN INSERT INTO %I.%I VALUES (NEW.*); ' , v_control , v_prev_partition_id , v_control , v_prev_partition_id + v_partition_interval , v_parent_schema , v_prev_partition_name ); END IF; END IF; SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname ||'.'||tablename = v_next_partition_name; IF v_count > 0 THEN v_trig_func := v_trig_func ||format(' ELSIF NEW.%I >= %s AND NEW.%I < %s THEN INSERT INTO %I.%I VALUES (NEW.*);' , v_control , v_next_partition_id , v_control , v_final_partition_id , v_parent_schema , v_next_partition_name ); END IF; END LOOP; v_trig_func := v_trig_func ||format(' ELSE v_current_partition_id := NEW.%I - (NEW.%I %% %s); v_current_partition_name := @extschema@.check_name_length(%L, v_current_partition_id::text, TRUE); SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname = %L AND tablename = v_current_partition_name; IF v_count > 0 THEN EXECUTE format(''INSERT INTO %%I.%%I VALUES($1.*)'', %L, v_current_partition_name) USING NEW; ELSE RETURN NEW; END IF; END IF;' , v_control , v_control , v_partition_interval , v_parent_tablename , v_parent_schema , v_parent_schema ); IF v_run_maint IS FALSE THEN v_trig_func := v_trig_func ||format(' v_current_partition_id := NEW.%I - (NEW.%I %% %s); IF (NEW.%I %% %s) > (%s / 2) THEN v_id_position := (length(v_last_partition) - position(''p_'' in reverse(v_last_partition))) + 2; v_next_partition_id := (substring(v_last_partition from v_id_position)::bigint) + %s; WHILE ((v_next_partition_id - v_current_partition_id) / %s) <= %s LOOP v_partition_created := @extschema@.create_partition_id(%L, ARRAY[v_next_partition_id]); IF v_partition_created THEN PERFORM @extschema@.create_function_id(%L); PERFORM @extschema@.apply_constraints(%L); END IF; v_next_partition_id := v_next_partition_id + %s; END LOOP; END IF;' , v_control , v_control , v_partition_interval , v_control , v_partition_interval , v_partition_interval , v_partition_interval , v_partition_interval , v_premake , p_parent_table , p_parent_table , p_parent_table , v_partition_interval ); END IF; v_trig_func := v_trig_func ||' END IF; RETURN NULL; END $t$;'; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', format('Added function for current id interval: %s to %s', v_current_partition_id, v_final_partition_id-1)); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', v_old_search_path, 'false'); END IF; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN CREATE FUNCTION: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''Partition function maintenance for table %s failed'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Create the trigger function for the parent table of a time-based partition set */ CREATE OR REPLACE FUNCTION create_function_time(p_parent_table text, p_job_id bigint DEFAULT NULL) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_control text; v_count int; v_current_partition_name text; v_current_partition_timestamp timestamptz; v_datetime_string text; v_final_partition_timestamp timestamptz; v_function_name text; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_new_length int; v_next_partition_name text; v_next_partition_timestamp timestamptz; v_parent_schema text; v_parent_tablename text; v_partition_interval interval; v_premake int; v_prev_partition_name text; v_prev_partition_timestamp timestamptz; v_step_id bigint; v_trig_func text; v_type text; BEGIN SELECT partition_type , partition_interval::interval , control , premake , datetime_string , jobmon INTO v_type , v_partition_interval , v_control , v_premake , v_datetime_string , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (partition_type = 'time' OR partition_type = 'time-custom'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', '@extschema@,'||v_jobmon_schema, 'false'); END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN IF p_job_id IS NULL THEN v_job_id := add_job(format('PARTMAN CREATE FUNCTION: %s', p_parent_table)); ELSE v_job_id = p_job_id; END IF; v_step_id := add_step(v_job_id, format('Creating partition function for table %s', p_parent_table)); END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; v_function_name := @extschema@.check_name_length(v_parent_tablename, '_part_trig_func', FALSE); IF v_type = 'time' THEN v_trig_func := format('CREATE OR REPLACE FUNCTION %I.%I() RETURNS trigger LANGUAGE plpgsql AS $t$ DECLARE v_count int; v_partition_name text; v_partition_timestamp timestamptz; BEGIN IF TG_OP = ''INSERT'' THEN ' , v_parent_schema , v_function_name); CASE WHEN v_partition_interval = '15 mins' THEN v_trig_func := v_trig_func||format('v_partition_timestamp := date_trunc(''hour'', NEW.%I) + ''15min''::interval * floor(date_part(''minute'', NEW.%I) / 15.0);' , v_control , v_control); v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0); WHEN v_partition_interval = '30 mins' THEN v_trig_func := v_trig_func||format('v_partition_timestamp := date_trunc(''hour'', NEW.%I) + ''30min''::interval * floor(date_part(''minute'', NEW.%I) / 30.0);' , v_control , v_control); v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0); WHEN v_partition_interval = '1 hour' THEN v_trig_func := v_trig_func||format('v_partition_timestamp := date_trunc(''hour'', NEW.%I);', v_control); v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP); WHEN v_partition_interval = '1 day' THEN v_trig_func := v_trig_func||format('v_partition_timestamp := date_trunc(''day'', NEW.%I);', v_control); v_current_partition_timestamp := date_trunc('day', CURRENT_TIMESTAMP); WHEN v_partition_interval = '1 week' THEN v_trig_func := v_trig_func||format('v_partition_timestamp := date_trunc(''week'', NEW.%I);', v_control); v_current_partition_timestamp := date_trunc('week', CURRENT_TIMESTAMP); WHEN v_partition_interval = '1 month' THEN v_trig_func := v_trig_func||format('v_partition_timestamp := date_trunc(''month'', NEW.%I);', v_control); v_current_partition_timestamp := date_trunc('month', CURRENT_TIMESTAMP); WHEN v_partition_interval = '3 months' THEN v_trig_func := v_trig_func||format('v_partition_timestamp := date_trunc(''quarter'', NEW.%I);', v_control); v_current_partition_timestamp := date_trunc('quarter', CURRENT_TIMESTAMP); WHEN v_partition_interval = '1 year' THEN v_trig_func := v_trig_func||format('v_partition_timestamp := date_trunc(''year'', NEW.%I);', v_control); v_current_partition_timestamp := date_trunc('year', CURRENT_TIMESTAMP); END CASE; v_current_partition_name := @extschema@.check_name_length(v_parent_tablename, to_char(v_current_partition_timestamp, v_datetime_string), TRUE); v_next_partition_timestamp := v_current_partition_timestamp + v_partition_interval::interval; v_trig_func := v_trig_func ||format(' IF NEW.%I >= %L AND NEW.%I < %L THEN ' , v_control , v_current_partition_timestamp , v_control , v_next_partition_timestamp); SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname = v_parent_schema AND tablename = v_current_partition_name; IF v_count > 0 THEN v_trig_func := v_trig_func || format(' INSERT INTO %I.%I VALUES (NEW.*); ', v_parent_schema, v_current_partition_name); ELSE v_trig_func := v_trig_func || ' -- Child table for current values does not exist in this partition set, so write to parent RETURN NEW;'; END IF; FOR i IN 1..v_premake LOOP v_prev_partition_timestamp := v_current_partition_timestamp - (v_partition_interval::interval * i); v_next_partition_timestamp := v_current_partition_timestamp + (v_partition_interval::interval * i); v_final_partition_timestamp := v_next_partition_timestamp + (v_partition_interval::interval); v_prev_partition_name := @extschema@.check_name_length(v_parent_tablename, to_char(v_prev_partition_timestamp, v_datetime_string), TRUE); v_next_partition_name := @extschema@.check_name_length(v_parent_tablename, to_char(v_next_partition_timestamp, v_datetime_string), TRUE); -- Check that child table exist before making a rule to insert to them. -- Handles edge case of changing premake immediately after running create_parent(). SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname = v_parent_schema AND tablename = v_prev_partition_name; IF v_count > 0 THEN v_trig_func := v_trig_func ||format(' ELSIF NEW.%I >= %L AND NEW.%I < %L THEN INSERT INTO %I.%I VALUES (NEW.*);' , v_control , v_prev_partition_timestamp , v_control , v_prev_partition_timestamp + v_partition_interval::interval , v_parent_schema , v_prev_partition_name); END IF; SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname = v_parent_schema AND tablename = v_next_partition_name; IF v_count > 0 THEN v_trig_func := v_trig_func ||format(' ELSIF NEW.%I >= %L AND NEW.%I < %L THEN INSERT INTO %I.%I VALUES (NEW.*);' , v_control , v_next_partition_timestamp , v_control , v_final_partition_timestamp , v_parent_schema , v_next_partition_name); END IF; END LOOP; v_trig_func := v_trig_func||format(' ELSE v_partition_name := @extschema@.check_name_length(%L, to_char(v_partition_timestamp, %L), TRUE); SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname = %L AND tablename = v_partition_name; IF v_count > 0 THEN EXECUTE format(''INSERT INTO %%I.%%I VALUES($1.*)'', %L, v_partition_name) USING NEW; ELSE RETURN NEW; END IF; END IF;' , v_parent_tablename , v_datetime_string , v_parent_schema , v_parent_schema); v_trig_func := v_trig_func ||' END IF; RETURN NULL; END $t$;'; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', format('Added function for current time interval: %s to %s' , v_current_partition_timestamp , v_final_partition_timestamp-'1sec'::interval)); END IF; ELSIF v_type = 'time-custom' THEN v_trig_func := format('CREATE OR REPLACE FUNCTION %I.%I() RETURNS trigger LANGUAGE plpgsql AS $t$ DECLARE v_child_schemaname text; v_child_table text; v_child_tablename text; BEGIN SELECT child_table INTO v_child_table FROM @extschema@.custom_time_partitions WHERE partition_range @> NEW.%I AND parent_table = %L; SELECT schemaname, tablename INTO v_child_schemaname, v_child_tablename FROM pg_catalog.pg_tables WHERE schemaname ||''.''|| tablename = v_child_table; IF v_child_schemaname IS NOT NULL AND v_child_tablename IS NOT NULL THEN EXECUTE format(''INSERT INTO %%I.%%I VALUES ($1.*)'', v_child_schemaname, v_child_tablename) USING NEW; ELSE RETURN NEW; END IF; RETURN NULL; END $t$;' , v_parent_schema , v_function_name , v_control , v_parent_schema||'.'||v_parent_tablename); EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', format('Added function for custom time table: %s', p_parent_table)); END IF; ELSE RAISE EXCEPTION 'ERROR: Invalid time partitioning type given: %', v_type; END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', v_old_search_path, 'false'); END IF; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN CREATE FUNCTION: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''Partition function maintenance for table %s failed'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Function to turn a table into the parent of a partition set */ CREATE OR REPLACE FUNCTION create_parent( p_parent_table text , p_control text , p_type text , p_interval text , p_constraint_cols text[] DEFAULT NULL , p_premake int DEFAULT 4 , p_use_run_maintenance boolean DEFAULT NULL , p_start_partition text DEFAULT NULL , p_inherit_fk boolean DEFAULT true , p_jobmon boolean DEFAULT true , p_debug boolean DEFAULT false) RETURNS boolean LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_base_timestamp timestamp; v_count int := 1; v_datetime_string text; v_higher_parent text := p_parent_table; v_id_interval bigint; v_id_position int; v_job_id bigint; v_jobmon_schema text; v_last_partition_created boolean; v_max bigint; v_notnull boolean; v_old_search_path text; v_parent_partition_id bigint; v_parent_partition_timestamp timestamp; v_parent_schema text; v_parent_tablename text; v_partition_time timestamp; v_partition_time_array timestamp[]; v_partition_id_array bigint[]; v_row record; v_run_maint boolean; v_sql text; v_start_time timestamp; v_starting_partition_id bigint; v_step_id bigint; v_step_overflow_id bigint; v_sub_parent text; v_success boolean := false; v_time_interval interval; v_time_position int; v_top_datetime_string text; v_top_parent text := p_parent_table; v_top_schemaname text; v_top_tablename text; BEGIN IF position('.' in p_parent_table) = 0 THEN RAISE EXCEPTION 'Parent table must be schema qualified'; END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname || '.' || tablename = p_parent_table; IF v_parent_tablename IS NULL THEN RAISE EXCEPTION 'Please create given parent table first: %', p_parent_table; END IF; 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 AND n.nspname = v_parent_schema AND a.attname = p_control; 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; IF NOT @extschema@.check_partition_type(p_type) THEN RAISE EXCEPTION '% is not a valid partitioning type', p_type; END IF; EXECUTE format('LOCK TABLE %I.%I IN ACCESS EXCLUSIVE MODE', v_parent_schema, v_parent_tablename); IF p_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', '@extschema@,'||v_jobmon_schema, 'false'); END IF; END IF; IF p_use_run_maintenance IS NOT NULL THEN IF p_use_run_maintenance IS FALSE AND (p_type = 'time' OR p_type = 'time-custom') THEN RAISE EXCEPTION 'p_run_maintenance cannot be set to false for time based partitioning'; END IF; v_run_maint := p_use_run_maintenance; ELSIF p_type = 'time' OR p_type = 'time-custom' THEN v_run_maint := TRUE; ELSIF p_type = 'id' THEN v_run_maint := FALSE; ELSE RAISE EXCEPTION 'use_run_maintenance value cannot be set NULL'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job(format('PARTMAN SETUP PARENT: %s', p_parent_table)); v_step_id := add_step(v_job_id, format('Creating initial partitions on new parent table: %s', p_parent_table)); 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 WITH parent_table AS ( SELECT h.inhparent AS parent_oid 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 c.relname = v_parent_tablename AND n.nspname = v_parent_schema ), sibling_children AS ( SELECT i.inhrelid::regclass::text AS tablename FROM pg_inherits i JOIN parent_table p ON i.inhparent = p.parent_oid ) 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_use_run_maintenance , sub_jobmon FROM @extschema@.part_config_sub a JOIN sibling_children b on a.sub_parent = b.tablename LIMIT 1 LOOP INSERT INTO @extschema@.part_config_sub ( sub_parent , 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_use_run_maintenance , sub_jobmon) VALUES ( p_parent_table , v_row.sub_partition_type , v_row.sub_control , 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_use_run_maintenance , v_row.sub_jobmon); END LOOP; IF p_type = 'time' OR p_type = 'time-custom' 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 <> 'time-custom' THEN RAISE EXCEPTION 'Must use a predefined time interval if not using type "time-custom". See documentation.'; 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; -- First partition is either the min premake or p_start_partition v_start_time := COALESCE(p_start_partition::timestamp, CURRENT_TIMESTAMP - (v_time_interval * p_premake)); 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 v_datetime_string := 'YYYY'; 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 v_partition_time_array := array_append(v_partition_time_array, v_base_timestamp); LOOP -- If current loop value is less than or equal to the value of the max premake, add time to array. IF (v_base_timestamp + (v_time_interval * v_count)) < (CURRENT_TIMESTAMP + (v_time_interval * p_premake)) THEN BEGIN v_partition_time := (v_base_timestamp + (v_time_interval * v_count))::timestamp; 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. 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'); CONTINUE; END; ELSE EXIT; -- all needed partitions added to array. Exit the loop. END IF; v_count := v_count + 1; END LOOP; INSERT INTO @extschema@.part_config ( parent_table , partition_type , partition_interval , control , premake , constraint_cols , datetime_string , use_run_maintenance , inherit_fk , jobmon) VALUES ( p_parent_table , p_type , v_time_interval , p_control , p_premake , p_constraint_cols , v_datetime_string , v_run_maint , p_inherit_fk , p_jobmon); v_last_partition_created := @extschema@.create_partition_time(p_parent_table, v_partition_time_array, false); 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 ( SELECT i.inhparent AS top_parent_oid FROM pg_catalog.pg_inherits i JOIN pg_catalog.pg_class c ON c.oid = i.inhrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE c.relname = v_parent_tablename AND n.nspname = v_parent_schema ) SELECT n.nspname||'.'||c.relname, p.datetime_string INTO v_top_parent, v_top_datetime_string 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 JOIN @extschema@.part_config p ON p.parent_table = n.nspname||'.'||c.relname; IF v_top_parent IS NOT NULL THEN -- If so create the lowest possible partition that is within the boundary of the parent v_time_position := (length(p_parent_table) - position('p_' in reverse(p_parent_table))) + 2; v_parent_partition_timestamp := to_timestamp(substring(p_parent_table from v_time_position), v_top_datetime_string); IF v_base_timestamp >= v_parent_partition_timestamp THEN WHILE v_base_timestamp >= v_parent_partition_timestamp LOOP v_base_timestamp := v_base_timestamp - v_time_interval; END LOOP; v_base_timestamp := v_base_timestamp + v_time_interval; -- add one back since while loop set it one lower than is needed ELSIF v_base_timestamp < v_parent_partition_timestamp THEN WHILE v_base_timestamp < v_parent_partition_timestamp LOOP v_base_timestamp := v_base_timestamp + v_time_interval; END LOOP; -- Don't need to remove one since new starting time will fit in top parent interval 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); ELSE -- Currently unknown edge case if code gets here RAISE EXCEPTION 'No child tables created. Unexpected edge case encountered. Please report this error to author with conditions that led to it.'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', format('Time partitions premade: %s', p_premake)); END IF; END IF; IF p_type = 'id' THEN v_id_interval := p_interval::bigint; IF v_id_interval < 10 THEN RAISE EXCEPTION 'Interval for serial partitioning must be greater than or equal to 10'; END IF; -- Check if parent table is a subpartition of an already existing id partition set managed by pg_partman. WHILE v_higher_parent IS NOT NULL LOOP -- initially set in DECLARE WITH top_oid AS ( SELECT i.inhparent AS top_parent_oid FROM pg_catalog.pg_inherits i JOIN pg_catalog.pg_class c ON c.oid = i.inhrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname||'.'||c.relname = v_higher_parent ) SELECT n.nspname||'.'||c.relname INTO v_higher_parent 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 JOIN @extschema@.part_config p ON p.parent_table = n.nspname||'.'||c.relname WHERE p.partition_type = 'id'; IF v_higher_parent IS NOT NULL THEN -- v_top_parent initially set in DECLARE v_top_parent := v_higher_parent; END IF; END LOOP; -- If custom start partition is set, use that. -- If custom start is not set and there is already data, start partitioning with the highest current value and ensure it's grabbed from highest top parent table SELECT schemaname, tablename INTO v_top_schemaname, v_top_tablename FROM pg_catalog.pg_tables WHERE schemaname||'.'||tablename = v_top_parent; v_sql := format('SELECT COALESCE(%L, max(%I)::bigint, 0) FROM %I.%I LIMIT 1' , p_start_partition::bigint , p_control , v_top_schemaname , v_top_tablename); EXECUTE v_sql INTO v_max; 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 THEN v_partition_id_array = array_append(v_partition_id_array, (v_starting_partition_id - v_id_interval*i)); END IF; v_partition_id_array = array_append(v_partition_id_array, (v_id_interval*i) + v_starting_partition_id); END LOOP; INSERT INTO @extschema@.part_config ( parent_table , partition_type , partition_interval , control , premake , constraint_cols , use_run_maintenance , inherit_fk , jobmon) VALUES ( p_parent_table , p_type , v_id_interval , p_control , p_premake , p_constraint_cols , v_run_maint , p_inherit_fk , p_jobmon); v_last_partition_created := @extschema@.create_partition_id(p_parent_table, v_partition_id_array, false); 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 -- See if it's actually a subpartition of a parent id partition WITH top_oid AS ( SELECT i.inhparent AS top_parent_oid FROM pg_catalog.pg_inherits i JOIN pg_catalog.pg_class c ON c.oid = i.inhrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE c.relname = v_parent_tablename AND n.nspname = v_parent_schema ) SELECT n.nspname||'.'||c.relname INTO v_top_parent 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 JOIN @extschema@.part_config p ON p.parent_table = n.nspname||'.'||c.relname WHERE p.partition_type = 'id'; IF v_top_parent IS NOT NULL THEN -- Create the lowest possible partition that is within the boundary of the parent v_id_position := (length(p_parent_table) - position('p_' in reverse(p_parent_table))) + 2; v_parent_partition_id = substring(p_parent_table from v_id_position)::bigint; IF v_starting_partition_id >= v_parent_partition_id THEN WHILE v_starting_partition_id >= v_parent_partition_id LOOP v_starting_partition_id := v_starting_partition_id - v_id_interval; END LOOP; v_starting_partition_id := v_starting_partition_id + v_id_interval; -- add one back since while loop set it one lower than is needed ELSIF v_starting_partition_id < v_parent_partition_id THEN WHILE v_starting_partition_id < v_parent_partition_id LOOP v_starting_partition_id := v_starting_partition_id + v_id_interval; END LOOP; -- Don't need to remove one since new starting id will fit in top parent interval 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); ELSE -- Currently unknown edge case if code gets here RAISE EXCEPTION 'No child tables created. Unexpected edge case encountered. Please report this error to author with conditions that led to it.'; END IF; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Creating partition function'); END IF; IF p_type = 'time' OR p_type = 'time-custom' 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 p_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); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); IF v_step_overflow_id IS NOT NULL THEN PERFORM fail_job(v_job_id); ELSE PERFORM close_job(v_job_id); END IF; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', v_old_search_path, 'false'); END IF; v_success := true; RETURN v_success; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN CREATE PARENT: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''Partition creation for table '||p_parent_table||' failed'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Function to create id partitions */ CREATE OR REPLACE FUNCTION create_partition_id(p_parent_table text, p_partition_ids bigint[], p_analyze boolean DEFAULT true) RETURNS boolean LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE 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_exists text; v_grantees text[]; v_hasoids boolean; v_id bigint; v_inherit_fk boolean; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_parent_grant record; v_parent_owner text; 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_revoke text; v_row record; v_sql text; v_step_id bigint; v_sub_id_max bigint; v_sub_id_min bigint; v_unlogged char; BEGIN SELECT control , partition_interval , inherit_fk , jobmon INTO v_control , v_partition_interval , v_inherit_fk , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND partition_type = 'id'; IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', '@extschema@,'||v_jobmon_schema, 'false'); END IF; END IF; -- Determine if this table is a child of a subpartition parent. If so, get limits of what child tables can be created based on parent suffix SELECT sub_min::bigint, sub_max::bigint INTO v_sub_id_min, v_sub_id_max FROM @extschema@.check_subpartition_limits(p_parent_table, 'id'); SELECT tableowner, schemaname, tablename, tablespace INTO v_parent_owner, v_parent_schema, v_parent_tablename, v_parent_tablespace FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job(format('PARTMAN CREATE TABLE: %s', p_parent_table)); 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. 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; END IF; END IF; v_partition_name := @extschema@.check_name_length(v_parent_tablename, v_id::text, TRUE); -- If child table already exists, skip creation SELECT tablename INTO v_exists FROM pg_catalog.pg_tables WHERE schemaname = v_parent_schema AND tablename = v_partition_name; IF v_exists IS NOT NULL 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, 'Creating new partition '||v_partition_name||' with interval from '||v_id||' to '||(v_id + v_partition_interval)-1); END IF; 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 AND n.nspname = v_parent_schema; v_sql := 'CREATE'; IF v_unlogged = 'u' THEN v_sql := v_sql || ' UNLOGGED'; END IF; v_sql := v_sql || format(' TABLE %I.%I (LIKE %I.%I INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING STORAGE INCLUDING COMMENTS)' , v_parent_schema , v_partition_name , v_parent_schema , v_parent_tablename); 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 AND n.nspname = v_parent_schema; IF v_hasoids IS TRUE THEN v_sql := v_sql || ' WITH (OIDS)'; END IF; EXECUTE v_sql; 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); EXECUTE format('ALTER TABLE %I.%I INHERIT %I.%I', v_parent_schema, v_partition_name, v_parent_schema, v_parent_tablename); FOR v_parent_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = p_parent_table GROUP BY grantee LOOP EXECUTE format('GRANT %s ON %I.%I TO %I' , array_to_string(v_parent_grant.types, ',') , v_parent_schema , v_partition_name , v_parent_grant.grantee); SELECT string_agg(r, ',') INTO v_revoke FROM (SELECT unnest(v_all) AS r EXCEPT SELECT unnest(v_parent_grant.types)) x; IF v_revoke IS NOT NULL THEN EXECUTE format('REVOKE %s ON %I.%I FROM %I CASCADE' , v_revoke , v_parent_schema , v_partition_name , v_parent_grant.grantee); END IF; v_grantees := array_append(v_grantees, v_parent_grant.grantee::text); END LOOP; -- Revoke all privileges from roles that have none on the parent IF v_grantees IS NOT NULL THEN FOR v_row IN SELECT role FROM ( SELECT DISTINCT grantee::text AS role FROM information_schema.table_privileges WHERE table_schema = v_parent_schema AND table_name = v_partition_name EXCEPT SELECT unnest(v_grantees)) x LOOP IF v_row.role IS NOT NULL THEN EXECUTE format('REVOKE ALL ON %I.%I FROM %I' , v_parent_schema , v_partition_name , v_row.role); END IF; END LOOP; END IF; EXECUTE format('ALTER TABLE %I.%I OWNER TO %I', v_parent_schema, v_partition_name, v_parent_owner); IF v_inherit_fk THEN PERFORM @extschema@.apply_foreign_keys(p_parent_table, v_parent_schema||'.'||v_partition_name, v_job_id); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); 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 FOR v_row IN SELECT sub_parent , sub_control , sub_partition_type , 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_use_run_maintenance , sub_jobmon FROM @extschema@.part_config_sub WHERE sub_parent = p_parent_table LOOP IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Subpartitioning '||v_partition_name); END IF; v_sql := format('SELECT @extschema@.create_parent( p_parent_table := %L , p_control := %L , p_type := %L , p_interval := %L , p_constraint_cols := %L , p_premake := %L , p_use_run_maintenance := %L , p_inherit_fk := %L , p_jobmon := %L )' , v_parent_schema||'.'||v_partition_name , v_row.sub_control , v_row.sub_partition_type , v_row.sub_partition_interval , v_row.sub_constraint_cols , v_row.sub_premake , v_row.sub_use_run_maintenance , v_row.sub_inherit_fk , v_row.sub_jobmon); EXECUTE v_sql; 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 WHERE parent_table = v_partition_name; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END LOOP; -- end sub partitioning LOOP 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 v_step_id := add_step(v_job_id, format('No partitions created for partition set: %s', p_parent_table)); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; PERFORM close_job(v_job_id); END IF; IF v_jobmon_schema IS NOT NULL THEN EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', v_old_search_path, 'false'); END IF; RETURN v_partition_created; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN CREATE TABLE: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Function to create a child table in a time-based partition set */ CREATE OR REPLACE FUNCTION create_partition_time (p_parent_table text, p_partition_times timestamp[], p_analyze boolean DEFAULT true) RETURNS boolean LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE 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_datetime_string text; v_exists text; v_grantees text[]; v_hasoids boolean; v_inherit_fk boolean; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_parent_grant record; v_parent_owner text; v_parent_schema text; v_parent_tablename text; v_partition_created boolean := false; v_partition_name text; v_partition_suffix text; v_parent_tablespace text; v_partition_interval interval; v_partition_timestamp_end timestamp; v_partition_timestamp_start timestamp; v_quarter text; v_revoke text; v_row record; v_sql text; v_step_id bigint; v_step_overflow_id bigint; v_sub_timestamp_max timestamp; v_sub_timestamp_min timestamp; v_trunc_value text; v_time timestamp; v_type text; v_unlogged char; v_year text; BEGIN SELECT partition_type , control , partition_interval , inherit_fk , jobmon , datetime_string INTO v_type , v_control , v_partition_interval , v_inherit_fk , v_jobmon , v_datetime_string FROM @extschema@.part_config WHERE parent_table = p_parent_table AND partition_type = 'time' OR partition_type = 'time-custom'; IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', '@extschema@,'||v_jobmon_schema, 'false'); END IF; END IF; -- Determine if this table is a child of a subpartition parent. If so, get limits of what child tables can be created based on parent suffix SELECT sub_min::timestamp, sub_max::timestamp INTO v_sub_timestamp_min, v_sub_timestamp_max FROM @extschema@.check_subpartition_limits(p_parent_table, 'time'); SELECT tableowner, schemaname, tablename, tablespace INTO v_parent_owner, v_parent_schema, v_parent_tablename, v_parent_tablespace FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job(format('PARTMAN CREATE TABLE: %s', p_parent_table)); END IF; 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. 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'); CONTINUE; END; -- 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 CONTINUE; END IF; END IF; -- 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); SELECT tablename INTO v_exists FROM pg_catalog.pg_tables WHERE schemaname = v_parent_schema AND tablename = v_partition_name; IF v_exists IS NOT NULL 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 , v_partition_name , v_partition_timestamp_start , v_partition_timestamp_end-'1sec'::interval)); END IF; 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 AND n.nspname = v_parent_schema; v_sql := 'CREATE'; IF v_unlogged = 'u' THEN v_sql := v_sql || ' UNLOGGED'; END IF; v_sql := v_sql || format(' TABLE %I.%I (LIKE %I.%I INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING STORAGE INCLUDING COMMENTS)' , v_parent_schema , v_partition_name , v_parent_schema , v_parent_tablename); 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 AND n.nspname = v_parent_schema; IF v_hasoids IS TRUE THEN v_sql := v_sql || ' WITH (OIDS)'; END IF; EXECUTE v_sql; 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 >= %L AND %I < %L)' , v_parent_schema , v_partition_name , v_partition_name||'_partition_check' , v_control , v_partition_timestamp_start , v_control , v_partition_timestamp_end); 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_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; FOR v_parent_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = p_parent_table GROUP BY grantee LOOP EXECUTE format('GRANT %s ON %I.%I TO %I' , array_to_string(v_parent_grant.types, ',') , v_parent_schema , v_partition_name , v_parent_grant.grantee); SELECT string_agg(r, ',') INTO v_revoke FROM (SELECT unnest(v_all) AS r EXCEPT SELECT unnest(v_parent_grant.types)) x; IF v_revoke IS NOT NULL THEN EXECUTE format('REVOKE %s ON %I.%I FROM %I CASCADE' , v_revoke , v_parent_schema , v_partition_name , v_parent_grant.grantee); END IF; v_grantees := array_append(v_grantees, v_parent_grant.grantee::text); END LOOP; -- Revoke all privileges from roles that have none on the parent IF v_grantees IS NOT NULL THEN FOR v_row IN SELECT role FROM ( SELECT DISTINCT grantee::text AS role FROM information_schema.table_privileges WHERE table_schema = v_parent_schema AND table_name = v_partition_name EXCEPT SELECT unnest(v_grantees)) x LOOP IF v_row.role IS NOT NULL THEN EXECUTE format('REVOKE ALL ON %I.%I FROM %I' , v_parent_schema , v_partition_name , v_row.role); END IF; END LOOP; END IF; EXECUTE format('ALTER TABLE %I.%I OWNER TO %I', v_parent_schema, v_partition_name, v_parent_owner); IF v_inherit_fk THEN PERFORM @extschema@.apply_foreign_keys(p_parent_table, v_parent_schema||'.'||v_partition_name, v_job_id); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); 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 FOR v_row IN SELECT sub_parent , sub_control , sub_partition_type , 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_use_run_maintenance , sub_jobmon FROM @extschema@.part_config_sub WHERE sub_parent = p_parent_table LOOP IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, format('Subpartitioning %s.%s', v_parent_schema, v_partition_name)); END IF; v_sql := format('SELECT @extschema@.create_parent( p_parent_table := %L , p_control := %L , p_type := %L , p_interval := %L , p_constraint_cols := %L , p_premake := %L , p_use_run_maintenance := %L , p_inherit_fk := %L , p_jobmon := %L )' , v_parent_schema||'.'||v_partition_name , v_row.sub_control , v_row.sub_partition_type , v_row.sub_partition_interval , v_row.sub_constraint_cols , v_row.sub_premake , v_row.sub_use_run_maintenance , v_row.sub_inherit_fk , v_row.sub_jobmon); EXECUTE v_sql; 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 WHERE parent_table = v_partition_name; END LOOP; -- end sub partitioning LOOP 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 v_step_id := add_step(v_job_id, format('No partitions created for partition set: %s. Attempted intervals: %s', p_parent_table, p_partition_times)); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; IF v_step_overflow_id IS NOT NULL THEN PERFORM fail_job(v_job_id); ELSE PERFORM close_job(v_job_id); END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', v_old_search_path, 'false'); END IF; RETURN v_partition_created; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN CREATE TABLE: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Create a partition set that is a subpartition of an already existing partition set. * Given the parent table of any current partition set, it will turn all existing children into parent tables of their own partition sets * using the configuration options given as parameters to this function. * Uses another config table that allows for turning all future child partitions into a new parent automatically. * To avoid logical complications and contention issues, ALL subpartitions must be maintained using run_maintenance(). * This means the automatic, trigger based partition creation for serial partitioning will not work if it is a subpartition. */ CREATE OR REPLACE FUNCTION create_sub_parent( p_top_parent text , p_control text , p_type text , p_interval text , p_constraint_cols text[] DEFAULT NULL , p_premake int DEFAULT 4 , p_start_partition text DEFAULT NULL , p_inherit_fk boolean DEFAULT true , p_jobmon boolean DEFAULT true , p_debug boolean DEFAULT false) RETURNS boolean LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_last_partition text; v_row record; v_row_last_part record; v_run_maint boolean; v_sql text; v_success boolean := false; v_top_type text; BEGIN SELECT use_run_maintenance INTO v_run_maint FROM @extschema@.part_config WHERE parent_table = p_top_parent; IF v_run_maint 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; ELSIF v_run_maint = false THEN RAISE EXCEPTION 'Any parent table that will be part of a sub-partitioned set (on any level) must have use_run_maintenance set to true in part_config table, even for serial partitioning. See documentation for more info.'; END IF; FOR v_row IN -- Loop through all current children to turn them into partitioned tables SELECT partition_schemaname||'.'||partition_tablename AS child_table FROM @extschema@.show_partitions(p_top_parent) LOOP -- Just call existing create_parent() function but add the given parameters to the part_config_sub table as well v_sql := format('SELECT @extschema@.create_parent( p_parent_table := %L , p_control := %L , p_type := %L , p_interval := %L , p_constraint_cols := %L , p_premake := %L , p_use_run_maintenance := %L , p_start_partition := %L , p_inherit_fk := %L , p_jobmon := %L , p_debug := %L )' , v_row.child_table , p_control , p_type , p_interval , p_constraint_cols , p_premake , true , p_start_partition , p_inherit_fk , p_jobmon , p_debug); EXECUTE v_sql; END LOOP; INSERT INTO @extschema@.part_config_sub ( sub_parent , sub_control , sub_partition_type , sub_partition_interval , sub_constraint_cols , sub_premake , sub_inherit_fk , sub_use_run_maintenance , sub_jobmon) VALUES ( p_top_parent , p_control , p_type , p_interval , p_constraint_cols , p_premake , p_inherit_fk , true , p_jobmon); v_success := true; RETURN v_success; END $$; CREATE OR REPLACE FUNCTION create_trigger(p_parent_table text) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_function_name text; v_new_length int; v_parent_schema text; v_parent_tablename text; v_trig_name text; v_trig_sql text; BEGIN SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; v_trig_name := @extschema@.check_name_length(p_object_name := v_parent_tablename, p_suffix := '_part_trig'); -- Ensure function name matches the naming pattern v_function_name := @extschema@.check_name_length(v_parent_tablename, '_part_trig_func', FALSE); v_trig_sql := format('CREATE TRIGGER %I BEFORE INSERT ON %I.%I FOR EACH ROW EXECUTE PROCEDURE %I.%I()' , v_trig_name , v_parent_schema , v_parent_tablename , v_parent_schema , v_function_name); EXECUTE v_trig_sql; END $$; /* * Drop constraints managed by pg_partman */ CREATE OR REPLACE FUNCTION drop_constraints(p_parent_table text, p_child_table text, p_debug boolean DEFAULT false) RETURNS void LANGUAGE plpgsql AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_child_schemaname text; v_child_tablename text; v_col text; v_constraint_cols text[]; v_existing_constraint_name text; v_exists boolean := FALSE; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_sql text; v_step_id bigint; BEGIN SELECT constraint_cols , jobmon INTO v_constraint_cols , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_constraint_cols IS NULL THEN RAISE EXCEPTION 'Given parent table (%) not set up for constraint management (constraint_cols is NULL)', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', '@extschema@,'||v_jobmon_schema, 'false'); END IF; END IF; SELECT schemaname, tablename INTO v_child_schemaname, v_child_tablename FROM pg_catalog.pg_tables WHERE schemaname||'.'||tablename = p_child_table; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job(format('PARTMAN DROP CONSTRAINT: %s', p_parent_table)); v_step_id := add_step(v_job_id, 'Entering constraint drop loop'); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; FOREACH v_col IN ARRAY v_constraint_cols LOOP SELECT con.conname INTO v_existing_constraint_name 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 WHERE c.relname = v_child_tablename AND n.nspname = v_child_schemaname AND con.conname LIKE 'partmanconstr_%' AND con.contype = 'c' AND a.attname = v_col AND ARRAY[a.attnum] <@ con.conkey AND a.attisdropped = false; IF v_existing_constraint_name IS NOT NULL THEN v_exists := TRUE; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, format('Dropping constraint on column: %s', v_col)); END IF; v_sql := format('ALTER TABLE %I.%I DROP CONSTRAINT %I', v_child_schemaname, v_child_tablename, v_existing_constraint_name); IF p_debug THEN RAISE NOTICE 'Constraint drop query: %', v_sql; END IF; EXECUTE v_sql; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', format('Drop constraint query: %s', v_sql)); END IF; END IF; END LOOP; IF v_jobmon_schema IS NOT NULL AND v_exists IS FALSE THEN v_step_id := add_step(v_job_id, format('No constraints found to drop on child table: %s', p_child_table)); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', v_old_search_path, 'false'); END IF; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN DROP CONSTRAINT: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * 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. */ CREATE OR REPLACE FUNCTION 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 SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_adv_lock boolean; v_control text; v_drop_count int := 0; v_id_position int; v_index record; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_max bigint; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_partition_interval bigint; v_partition_id bigint; v_retention bigint; v_retention_keep_index boolean; v_retention_keep_table boolean; v_retention_schema text; v_row record; v_row_max_id record; v_step_id bigint; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman drop_partition_id')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'drop_partition_id already running.'; RETURN 0; END IF; -- Allow override of configuration options IF p_retention IS NULL THEN SELECT partition_interval::bigint , control , retention::bigint , retention_keep_table , retention_keep_index , retention_schema , jobmon INTO v_partition_interval , v_control , v_retention , v_retention_keep_table , v_retention_keep_index , v_retention_schema , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND partition_type = 'id' 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_interval::bigint , control , retention_keep_table , retention_keep_index , retention_schema , jobmon INTO v_partition_interval , v_control , v_retention_keep_table , v_retention_keep_index , v_retention_schema , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND partition_type = 'id'; v_retention := p_retention; IF v_partition_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', '@extschema@,'||v_jobmon_schema, 'false'); END IF; END IF; IF p_keep_table IS NOT NULL THEN v_retention_keep_table = p_keep_table; END IF; IF p_keep_index IS NOT NULL THEN v_retention_keep_index = p_keep_index; END IF; IF p_retention_schema IS NOT NULL THEN v_retention_schema = p_retention_schema; END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = p_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. FOR v_row_max_id IN SELECT partition_schemaname, partition_tablename FROM @extschema@.show_partitions(p_parent_table, 'DESC') LOOP EXECUTE format('SELECT max(%I) FROM %I.%I', v_control, v_row_max_id.partition_schemaname, v_row_max_id.partition_tablename) INTO v_max; IF v_max IS NOT NULL THEN EXIT; END IF; END LOOP; -- Loop through child tables of the given parent FOR v_row IN SELECT partition_schemaname, partition_tablename FROM @extschema@.show_partitions(p_parent_table, 'ASC') LOOP v_id_position := (length(v_row.partition_tablename) - position('p_' in reverse(v_row.partition_tablename))) + 2; v_partition_id := substring(v_row.partition_tablename from v_id_position)::bigint; -- Add one interval since partition names contain the start of the constraint period IF v_retention <= (v_max - (v_partition_id + v_partition_interval)) THEN -- Only create a jobmon entry if there's actual retention work done IF v_jobmon_schema IS NOT NULL AND v_job_id IS NULL THEN v_job_id := add_job(format('PARTMAN DROP ID PARTITION: %s', p_parent_table)); END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, format('Uninherit table %s.%s from %s', v_row.partition_schemaname, v_row.partition_tablename, p_parent_table)); END IF; 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; 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; EXECUTE format('DROP TABLE %I.%I CASCADE', 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 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 AND n1.nspname = v_row.partition_schema ) 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; 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' , v_row.partition_schemaname , v_row.partition_tablename , v_retention_schema)); END IF; EXECUTE format('ALTER TABLE %I.%I SET SCHEMA %I' , v_row.partition_schemaname , v_row.partition_tablename , v_retention_schema); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; -- End retention schema if -- If child table is a subpartition, remove it from part_config & part_config_sub (should cascade due to FK) DELETE FROM @extschema@.part_config WHERE parent_table = v_row.partition_schemaname ||'.'||v_row.partition_tablename; v_drop_count := v_drop_count + 1; END IF; -- End retention check IF END LOOP; -- End child table loop IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Finished partition drop maintenance'); PERFORM update_step(v_step_id, 'OK', format('%s partitions dropped.', v_drop_count)); PERFORM close_job(v_job_id); END IF; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', v_old_search_path, 'false'); END IF; RETURN v_drop_count; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN DROP ID PARTITION: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Function to drop child tables from a time-based partition set. * Options to move table to different schema, drop only indexes or actually drop the table from the database. */ CREATE OR REPLACE FUNCTION 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) RETURNS int LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_adv_lock boolean; v_datetime_string text; v_drop_count int := 0; v_index record; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_partition_interval interval; v_partition_timestamp timestamp; v_quarter text; v_retention interval; v_retention_keep_index boolean; v_retention_keep_table boolean; v_retention_schema text; v_row record; v_step_id bigint; v_time_position int; v_type text; v_year text; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman drop_partition_time')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'drop_partition_time already running.'; RETURN 0; END IF; -- Allow override of configuration options IF p_retention IS NULL THEN SELECT partition_type , partition_interval::interval , retention::interval , retention_keep_table , retention_keep_index , datetime_string , retention_schema , jobmon INTO v_type , v_partition_interval , v_retention , v_retention_keep_table , v_retention_keep_index , v_datetime_string , v_retention_schema , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (partition_type = 'time' OR partition_type = 'time-custom') 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 , retention_keep_table , retention_keep_index , datetime_string , retention_schema , jobmon INTO v_type , v_partition_interval , v_retention_keep_table , v_retention_keep_index , v_datetime_string , v_retention_schema , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (partition_type = 'time' OR partition_type = 'time-custom'); v_retention := p_retention; IF v_partition_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', '@extschema@,'||v_jobmon_schema, 'false'); END IF; END IF; IF p_keep_table IS NOT NULL THEN v_retention_keep_table = p_keep_table; END IF; IF p_keep_index IS NOT NULL THEN v_retention_keep_index = p_keep_index; END IF; IF p_retention_schema IS NOT NULL THEN v_retention_schema = p_retention_schema; END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; -- Loop through child tables of the given parent FOR v_row IN SELECT partition_schemaname, partition_tablename FROM @extschema@.show_partitions(p_parent_table, 'DESC') LOOP -- pull out datetime portion of partition's tablename to make the next one v_time_position := (length(v_row.partition_tablename) - position('p_' in reverse(v_row.partition_tablename))) + 2; IF v_partition_interval <> '3 months' OR (v_partition_interval = '3 months' AND v_type = 'time-custom') THEN v_partition_timestamp := to_timestamp(substring(v_row.partition_tablename from v_time_position), v_datetime_string); ELSE -- to_timestamp doesn't recognize 'Q' date string formater. Handle it v_year := split_part(substring(v_row.partition_tablename from v_time_position), 'q', 1); v_quarter := split_part(substring(v_row.partition_tablename from v_time_position), 'q', 2); CASE WHEN v_quarter = '1' THEN v_partition_timestamp := to_timestamp(v_year || '-01-01', 'YYYY-MM-DD'); WHEN v_quarter = '2' THEN v_partition_timestamp := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); WHEN v_quarter = '3' THEN v_partition_timestamp := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); WHEN v_quarter = '4' THEN v_partition_timestamp := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); END CASE; END IF; -- Add one interval since partition names contain the start of the constraint period IF v_retention < (CURRENT_TIMESTAMP - (v_partition_timestamp + v_partition_interval)) THEN -- Only create a jobmon entry if there's actual retention work done IF v_jobmon_schema IS NOT NULL AND v_job_id IS NULL THEN v_job_id := add_job(format('PARTMAN DROP TIME PARTITION: %s', p_parent_table)); END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, format('Uninherit table %s.%s from %s' , v_row.partition_schemaname , v_row.partition_tablename , p_parent_table)); END IF; 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_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; 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; EXECUTE format('DROP TABLE %I.%I CASCADE', 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 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 AND n1.nspname = v_row.partition_schemaname ) 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_parent_schema, 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; 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' , v_row.partition_schemaname , v_row.partition_tablename , v_retention_schema)); END IF; EXECUTE format('ALTER TABLE %I.%I SET SCHEMA %I', v_row.partition_schemaname, v_row.partition_tablename, v_retention_schema); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; -- End retention schema if -- If child table is a subpartition, remove it from part_config & part_config_sub (should cascade due to FK) DELETE FROM @extschema@.part_config WHERE parent_table = v_row.partition_schemaname||'.'||v_row.partition_tablename; v_drop_count := v_drop_count + 1; END IF; -- End retention check IF END LOOP; -- End child table loop IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Finished partition drop maintenance'); PERFORM update_step(v_step_id, 'OK', format('%s partitions dropped.', v_drop_count)); PERFORM close_job(v_job_id); END IF; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', v_old_search_path, 'false'); END IF; RETURN v_drop_count; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN DROP TIME PARTITION: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Populate the child table(s) of an id-based partition set with old data from the original parent */ CREATE OR REPLACE FUNCTION partition_data_id(p_parent_table text, p_batch_count int DEFAULT 1, p_batch_interval int DEFAULT NULL, p_lock_wait numeric DEFAULT 0, p_order text DEFAULT 'ASC') RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_control text; v_current_partition_name text; 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_rowcount bigint; v_sql text; v_start_control bigint; v_total_rows bigint := 0; BEGIN SELECT partition_interval::bigint , control INTO v_partition_interval , v_control FROM @extschema@.part_config WHERE parent_table = p_parent_table AND partition_type = 'id'; IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; IF p_batch_interval IS NULL OR p_batch_interval > v_partition_interval THEN p_batch_interval := v_partition_interval; END IF; FOR i IN 1..p_batch_count LOOP IF p_order = 'ASC' THEN EXECUTE format('SELECT min(%I) FROM ONLY %I.%I', v_control, v_parent_schema, v_parent_tablename) INTO v_start_control; IF v_start_control IS NULL THEN EXIT; END IF; v_min_partition_id = v_start_control - (v_start_control % v_partition_interval); v_partition_id := ARRAY[v_min_partition_id]; -- Check if custom batch interval overflows current partition maximum IF (v_start_control + p_batch_interval) >= (v_min_partition_id + v_partition_interval) THEN v_max_partition_id := v_min_partition_id + v_partition_interval; ELSE v_max_partition_id := v_start_control + p_batch_interval; END IF; ELSIF p_order = 'DESC' THEN EXECUTE 'SELECT max('||v_control||') FROM ONLY '||p_parent_table INTO v_start_control; IF v_start_control IS NULL THEN EXIT; END IF; v_min_partition_id = v_start_control - (v_start_control % v_partition_interval); -- Must be greater than max value still in parent table since query below grabs < max v_max_partition_id := v_min_partition_id + v_partition_interval; v_partition_id := ARRAY[v_min_partition_id]; -- Make sure minimum doesn't underflow current partition minimum IF (v_start_control - p_batch_interval) >= v_min_partition_id THEN v_min_partition_id = v_start_control - p_batch_interval; END IF; ELSE RAISE EXCEPTION 'Invalid value for p_order. Must be ASC or DESC'; END IF; -- do some locking with timeout, if required IF p_lock_wait > 0 THEN v_lock_iter := 0; WHILE v_lock_iter <= 5 LOOP v_lock_iter := v_lock_iter + 1; BEGIN v_sql := format('SELECT * FROM ONLY %I.%I WHERE %I >= %s AND %I < %s FOR UPDATE NOWAIT' , v_parent_schema , v_parent_tablename , v_control , v_min_partition_id , v_control , v_max_partition_id); EXECUTE v_sql; 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 RETURN -1; END IF; END IF; PERFORM @extschema@.create_partition_id(p_parent_table, v_partition_id); v_current_partition_name := @extschema@.check_name_length(v_parent_tablename, v_min_partition_id::text, TRUE); EXECUTE format('WITH partition_data AS ( DELETE FROM ONLY %I.%I WHERE %I >= %s AND %I < %s RETURNING *) INSERT INTO %I.%I SELECT * FROM partition_data' , v_parent_schema , v_parent_tablename , v_control , v_min_partition_id , v_control , v_max_partition_id , v_parent_schema , v_current_partition_name); GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total_rows := v_total_rows + v_rowcount; IF v_rowcount = 0 THEN EXIT; END IF; END LOOP; PERFORM @extschema@.create_function_id(p_parent_table); RETURN v_total_rows; END $$; /* * Populate the child table(s) of a time-based partition set with old data from the original parent */ CREATE OR REPLACE FUNCTION 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') RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_control text; v_datetime_string text; v_current_partition_name text; v_max_partition_timestamp timestamp; v_last_partition text; v_lock_iter int := 1; v_lock_obtained boolean := FALSE; v_min_partition_timestamp timestamp; v_parent_schema text; v_parent_tablename text; v_partition_interval interval; v_partition_suffix text; v_partition_timestamp timestamp[]; v_quarter text; v_rowcount bigint; v_sql text; v_start_control timestamp; v_time_position int; v_total_rows bigint := 0; v_type text; v_year text; BEGIN SELECT partition_type , partition_interval::interval , control , datetime_string INTO v_type , v_partition_interval , v_control , v_datetime_string FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (partition_type = 'time' OR partition_type = 'time-custom'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF p_batch_interval IS NULL OR p_batch_interval > v_partition_interval THEN p_batch_interval := v_partition_interval; END IF; SELECT partition_tablename INTO v_last_partition FROM @extschema@.show_partitions(p_parent_table, 'DESC') LIMIT 1; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; FOR i IN 1..p_batch_count LOOP IF p_order = 'ASC' THEN EXECUTE format('SELECT min(%I) FROM ONLY %I.%I', v_control, v_parent_schema, v_parent_tablename) INTO v_start_control; ELSIF p_order = 'DESC' THEN EXECUTE format('SELECT max(%I) FROM ONLY %I.%I', v_control, v_parent_schema, v_parent_tablename) INTO v_start_control; ELSE RAISE EXCEPTION 'Invalid value for p_order. Must be ASC or DESC'; END IF; IF v_start_control IS NULL THEN EXIT; END IF; IF v_type = 'time' 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_type = 'time-custom' THEN -- Keep going backwards, checking if the time interval encompases the current v_start_control value v_time_position := (length(v_last_partition) - position('p_' in reverse(v_last_partition))) + 2; v_min_partition_timestamp := to_timestamp(substring(v_last_partition from v_time_position), v_datetime_string); 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 v_max_partition_timestamp := v_min_partition_timestamp; BEGIN v_min_partition_timestamp := v_min_partition_timestamp - v_partition_interval; 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_interval; END; END IF; END LOOP; END IF; v_partition_timestamp := ARRAY[v_min_partition_timestamp]; IF p_order = 'ASC' THEN IF (v_start_control + p_batch_interval) >= (v_min_partition_timestamp + v_partition_interval) THEN v_max_partition_timestamp := v_min_partition_timestamp + v_partition_interval; ELSE v_max_partition_timestamp := v_start_control + p_batch_interval; END IF; ELSIF p_order = 'DESC' THEN -- Must be greater than max value still in parent table since query below grabs < max v_max_partition_timestamp := v_min_partition_timestamp + v_partition_interval; -- Make sure minimum doesn't underflow current partition minimum IF (v_start_control - p_batch_interval) >= v_min_partition_timestamp THEN v_min_partition_timestamp = v_start_control - p_batch_interval; END IF; ELSE RAISE EXCEPTION 'Invalid value for p_order. Must be ASC or DESC'; END IF; -- do some locking with timeout, if required IF p_lock_wait > 0 THEN v_lock_iter := 0; WHILE v_lock_iter <= 5 LOOP v_lock_iter := v_lock_iter + 1; BEGIN v_sql := format('SELECT * FROM ONLY %I.%I WHERE %I >= %L AND %I < %L FOR UPDATE NOWAIT' , v_parent_schema , v_parent_tablename , v_control , v_min_partition_timestamp , v_control , v_max_partition_timestamp); EXECUTE v_sql; 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 RETURN -1; END IF; END IF; PERFORM @extschema@.create_partition_time(p_parent_table, v_partition_timestamp); -- This suffix generation code is in create_partition_time() as well v_partition_suffix := to_char(v_min_partition_timestamp, v_datetime_string); v_current_partition_name := @extschema@.check_name_length(v_parent_tablename, v_partition_suffix, TRUE); v_sql := format('WITH partition_data AS ( DELETE FROM ONLY %I.%I WHERE %I >= %L AND %I < %L RETURNING *) INSERT INTO %I.%I SELECT * FROM partition_data' , v_parent_schema , v_parent_tablename , v_control , v_min_partition_timestamp , v_control , v_max_partition_timestamp , v_parent_schema , v_current_partition_name); EXECUTE v_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total_rows := v_total_rows + v_rowcount; IF v_rowcount = 0 THEN EXIT; END IF; END LOOP; PERFORM @extschema@.create_function_time(p_parent_table); RETURN v_total_rows; END $$; /* * Function to re-apply ownership & privileges on all child tables in a partition set using parent table as reference */ CREATE OR REPLACE FUNCTION reapply_privileges(p_parent_table text) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_all text[] := ARRAY['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER']; v_child_owner text; v_child_grant record; v_grant text; v_grantees text[]; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_match boolean; v_old_search_path text; v_parent_owner text; v_parent_schema text; v_parent_tablename text; v_owner_sql text; v_revoke text; v_row record; v_row_revoke record; v_parent_grant record; v_sql text; v_step_id bigint; BEGIN SELECT jobmon INTO v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon IS NULL THEN RAISE EXCEPTION 'Given table is not managed by this extention: %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', '@extschema@,'||v_jobmon_schema, 'false'); END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job(format('PARTMAN RE-APPLYING PRIVILEGES TO ALL CHILD TABLES OF: %s', p_parent_table)); v_step_id := add_step(v_job_id, 'Setting new child table privileges'); END IF; SELECT schemaname, tablename, tableowner INTO v_parent_schema, v_parent_tablename, v_parent_owner FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; FOR v_row IN SELECT partition_schemaname, partition_tablename FROM @extschema@.show_partitions(p_parent_table, 'ASC') LOOP IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'PENDING', format('Currently on child partition in ascending order: %s.%s' , v_row.partition_schemaname , v_row.partition_tablename)); END IF; v_grantees := NULL; FOR v_parent_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types , grantee FROM information_schema.table_privileges WHERE table_schema = v_parent_schema AND table_name = v_parent_tablename GROUP BY grantee LOOP -- Compare parent & child grants. Don't re-apply if it already exists v_match := false; FOR v_child_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types , grantee FROM information_schema.table_privileges WHERE table_schema = v_row.partition_schemaname AND table_name = v_row.partition_tablename GROUP BY grantee LOOP IF v_parent_grant.types = v_child_grant.types AND v_parent_grant.grantee = v_child_grant.grantee THEN v_match := true; END IF; END LOOP; IF v_match = false THEN EXECUTE format('GRANT %s ON %I.%I TO %I' , array_to_string(v_parent_grant.types, ',') , v_row.partition_schemaname , v_row.partition_tablename , v_parent_grant.grantee); SELECT string_agg(r, ',') INTO v_revoke FROM (SELECT unnest(v_all) AS r EXCEPT SELECT unnest(v_parent_grant.types)) x; IF v_revoke IS NOT NULL THEN EXECUTE format('REVOKE %s ON %I.%I FROM %I CASCADE' , v_revoke , v_row.partition_schemaname , v_row.partition_tablename , v_parent_grant.grantee); END IF; END IF; v_grantees := array_append(v_grantees, v_parent_grant.grantee::text); END LOOP; -- Revoke all privileges from roles that have none on the parent IF v_grantees IS NOT NULL THEN FOR v_row_revoke IN SELECT role FROM ( SELECT DISTINCT grantee::text AS role FROM information_schema.table_privileges WHERE table_schema = v_row.partition_schemaname AND table_name = v_row.partition_tablename EXCEPT SELECT unnest(v_grantees)) x LOOP IF v_row_revoke.role IS NOT NULL THEN EXECUTE format('REVOKE ALL ON %I.%I FROM %I' , v_row.partition_schemaname , v_row.partition_tablename , v_row_revoke.role); END IF; END LOOP; END IF; SELECT tableowner INTO v_child_owner FROM pg_tables WHERE schemaname = v_row.partition_schemaname AND tablename = v_row.partition_tablename; IF v_parent_owner <> v_child_owner THEN EXECUTE format('ALTER TABLE %I.%I OWNER TO %I' , v_row.partition_schemaname , v_row.partition_tablename , v_parent_owner); END IF; END LOOP; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); PERFORM close_job(v_job_id); END IF; IF v_jobmon_schema IS NOT NULL THEN EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', v_old_search_path, 'false'); END IF; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN RE-APPLYING PRIVILEGES TO ALL CHILD TABLES OF: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Function to manage pre-creation of the next partitions in a set. * 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_run_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. * Be aware that constraint exclusion may not work properly until an analyze on the partition set is run. */ CREATE OR REPLACE FUNCTION run_maintenance(p_parent_table text DEFAULT NULL, p_analyze boolean DEFAULT true, p_jobmon boolean DEFAULT true) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_adv_lock boolean; v_create_count int := 0; v_current_partition text; v_current_partition_id bigint; v_current_partition_timestamp timestamp; v_datetime_string text; v_drop_count int := 0; v_id_position int; 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 timestamp; v_next_partition_id bigint; v_next_partition_timestamp timestamp; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_premade_count int; v_premake_id_max bigint; v_premake_id_min bigint; v_premake_timestamp_min timestamp; v_premake_timestamp_max timestamp; v_quarter text; v_row record; v_row_max_id record; v_row_sub record; v_skip_maint boolean; v_step_id bigint; v_step_overflow_id bigint; v_step_serial_id bigint; v_sub_id_max bigint; v_sub_id_max_suffix bigint; v_sub_id_min bigint; v_sub_parent text; v_sub_timestamp_max timestamp; v_sub_timestamp_max_suffix timestamp; v_sub_timestamp_min timestamp; v_tablename text; v_tables_list_sql text; v_time_position int; v_year text; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman run_maintenance')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'Partman maintenance already running.'; RETURN; END IF; IF p_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', '@extschema@,'||v_jobmon_schema, 'false'); END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN RUN MAINTENANCE'); v_step_id := add_step(v_job_id, 'Running maintenance loop'); END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; v_tables_list_sql := 'SELECT parent_table , partition_type , partition_interval , control , premake , datetime_string , undo_in_progress FROM @extschema@.part_config WHERE sub_partition_set_full = false'; IF p_parent_table IS NULL THEN v_tables_list_sql := v_tables_list_sql || ' AND use_run_maintenance = true'; ELSE v_tables_list_sql := v_tables_list_sql || format(' AND parent_table = %L', p_parent_table); END IF; FOR v_row IN EXECUTE v_tables_list_sql LOOP CONTINUE WHEN v_row.undo_in_progress; v_skip_maint := true; -- reset every loop SELECT partition_tablename INTO v_last_partition FROM @extschema@.show_partitions(v_row.parent_table, 'DESC') LIMIT 1; IF v_row.partition_type = 'time' OR v_row.partition_type = 'time-custom' THEN v_time_position := (length(v_last_partition) - position('p_' in reverse(v_last_partition))) + 2; IF v_row.partition_interval::interval <> '3 months' OR (v_row.partition_interval::interval = '3 months' AND v_row.partition_type = 'time-custom') THEN v_last_partition_timestamp := to_timestamp(substring(v_last_partition from v_time_position), v_row.datetime_string); ELSE -- to_timestamp doesn't recognize 'Q' date string formater. Handle it v_year := split_part(substring(v_last_partition FROM v_time_position), 'q', 1); v_quarter := split_part(substring(v_last_partition FROM v_time_position), 'q', 2); CASE WHEN v_quarter = '1' THEN v_last_partition_timestamp := to_timestamp(v_year || '-01-01', 'YYYY-MM-DD'); WHEN v_quarter = '2' THEN v_last_partition_timestamp := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); WHEN v_quarter = '3' THEN v_last_partition_timestamp := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); WHEN v_quarter = '4' THEN v_last_partition_timestamp := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); END CASE; END IF; -- If this is a subpartition, determine if the last child table has been made. If so, mark it as full so future maintenance runs can skip it SELECT sub_min::timestamp, sub_max::timestamp INTO v_sub_timestamp_min, v_sub_timestamp_max FROM @extschema@.check_subpartition_limits(v_row.parent_table, 'time'); IF v_sub_timestamp_max IS NOT NULL THEN SELECT suffix_timestamp INTO v_sub_timestamp_max_suffix FROM @extschema@.show_partition_name(v_row.parent_table, v_sub_timestamp_max::text); IF v_sub_timestamp_max_suffix = v_last_partition_timestamp THEN -- Final partition for this set is created. Set full and skip it UPDATE @extschema@.part_config SET sub_partition_set_full = true WHERE parent_table = v_row.parent_table; CONTINUE; END IF; END IF; SELECT suffix_timestamp INTO v_current_partition_timestamp FROM @extschema@.show_partition_name(v_row.parent_table, CURRENT_TIMESTAMP::text); -- Check and see how many premade partitions there are. 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; -- Loop premaking until config setting is met. Allows it to catch up if it fell behind or if premake changed. -- premake_count can be negative with subpartitioning when running against old or future sub-partition sets. -- If so, do not run to avoid recreating dropped partitions due to retention or pre-creating before they're needed WHILE (v_premade_count >= 0) AND (v_premade_count < v_row.premake) LOOP IF v_next_partition_timestamp < v_sub_timestamp_min OR v_next_partition_timestamp > v_sub_timestamp_max THEN -- With subpartitioning, no need to run if the timestamp is not in the parent table's range EXIT; END IF; 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 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', 'Child partition creation skippd for parent table '||v_partition_time); END IF; RAISE WARNING 'Attempted partition time interval is outside PostgreSQL''s supported time range. Child partition creation skipped for parent table %', v_row.parent_table; CONTINUE; END; v_last_partition_created := @extschema@.create_partition_time(v_row.parent_table, ARRAY[v_next_partition_timestamp], p_analyze); IF v_last_partition_created THEN v_create_count := v_create_count + 1; PERFORM @extschema@.create_function_time(v_row.parent_table, v_job_id); -- Manage additonal constraints if set PERFORM @extschema@.apply_constraints(p_parent_table := v_row.parent_table, p_job_id := v_job_id); 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)); END LOOP; ELSIF v_row.partition_type = 'id' THEN -- 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') LOOP EXECUTE format('SELECT %I - (%I %% %L) FROM %I.%I WHERE %I = (SELECT max(%I) FROM %I.%I)' , v_row.control , v_row.control , v_row.partition_interval::int , v_row_max_id.partition_schemaname , v_row_max_id.partition_tablename , v_row.control , v_row.control , v_row_max_id.partition_schemaname , v_row_max_id.partition_tablename ) INTO v_current_partition_id; IF v_current_partition_id IS NOT NULL THEN EXIT; END IF; END LOOP; v_id_position := (length(v_last_partition) - position('p_' in reverse(v_last_partition))) + 2; v_last_partition_id = substring(v_last_partition from v_id_position)::bigint; -- Determine if this table is a child of a subpartition parent. If so, get limits to see if run_maintenance even needs to run for it. SELECT sub_min::bigint, sub_max::bigint INTO v_sub_id_min, v_sub_id_max FROM @extschema@.check_subpartition_limits(v_row.parent_table, 'id'); IF v_sub_id_max IS NOT NULL THEN SELECT suffix_id INTO v_sub_id_max_suffix FROM @extschema@.show_partition_name(v_row.parent_table, v_sub_id_max::text); IF v_sub_id_max_suffix = v_last_partition_id THEN -- Final partition for this set is created. Set full and skip it UPDATE @extschema@.part_config SET sub_partition_set_full = true WHERE parent_table = v_row.parent_table; CONTINUE; END IF; END IF; 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. -- premake_count can be negative with subpartitioning when running against old or future sub-partition sets. -- If so, do not run to avoid recreating dropped partitions due to retention or pre-creating before they're needed WHILE (v_premade_count >= 0) AND (v_premade_count < v_row.premake) LOOP 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], p_analyze); IF v_last_partition_created THEN v_create_count := v_create_count + 1; PERFORM @extschema@.create_function_id(v_row.parent_table, v_job_id); PERFORM @extschema@.apply_constraints(p_parent_table := v_row.parent_table, p_job_id := v_job_id); 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 END LOOP; -- end of creation loop -- Manage dropping old partitions if retention option is set FOR v_row IN SELECT parent_table FROM @extschema@.part_config WHERE retention IS NOT NULL AND undo_in_progress = false AND (partition_type = 'time' OR partition_type = 'time-custom') LOOP IF p_parent_table IS NULL THEN v_drop_count := v_drop_count + @extschema@.drop_partition_time(v_row.parent_table); ELSE -- Only run retention on table given in parameter IF p_parent_table <> v_row.parent_table THEN CONTINUE; ELSE v_drop_count := v_drop_count + @extschema@.drop_partition_time(v_row.parent_table); END IF; END IF; IF v_drop_count > 0 THEN PERFORM @extschema@.create_function_time(v_row.parent_table, v_job_id); END IF; END LOOP; FOR v_row IN SELECT parent_table FROM @extschema@.part_config WHERE retention IS NOT NULL AND undo_in_progress = false AND partition_type = 'id' LOOP IF p_parent_table IS NULL THEN v_drop_count := v_drop_count + @extschema@.drop_partition_id(v_row.parent_table); ELSE -- Only run retention on table given in parameter IF p_parent_table <> v_row.parent_table THEN CONTINUE; ELSE v_drop_count := v_drop_count + @extschema@.drop_partition_id(v_row.parent_table); END IF; END IF; IF v_drop_count > 0 THEN PERFORM @extschema@.create_function_id(v_row.parent_table, v_job_id); END IF; END LOOP; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', format('Partition maintenance finished. %s partitons made. %s partitions dropped.', v_create_count, v_drop_count)); IF v_step_overflow_id IS NOT NULL OR v_step_serial_id IS NOT NULL THEN PERFORM fail_job(v_job_id); ELSE PERFORM close_job(v_job_id); END IF; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', v_old_search_path, 'false'); END IF; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN RUN MAINTENANCE'')', v_jobmon_schema) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Given a parent table and partition value, return the name of the child partition it would go in. * Also returns just the suffix value and true if the child table exists or false if it does not */ CREATE FUNCTION show_partition_name(p_parent_table text, p_value text, OUT partition_table text, OUT suffix_timestamp timestamp, OUT suffix_id bigint, OUT table_exists boolean) RETURNS record LANGUAGE plpgsql STABLE AS $$ DECLARE v_child_exists text; v_datetime_string text; v_max_range timestamptz; v_min_range timestamptz; v_parent_schema text; v_parent_tablename text; v_partition_interval text; v_time_position int; v_type text; BEGIN SELECT partition_type , partition_interval , datetime_string INTO v_type , v_partition_interval , v_datetime_string FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_type IS NULL THEN RAISE EXCEPTION 'Parent table given is not managed by pg_partman (%)', p_parent_table; END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; IF v_parent_tablename IS NULL THEN RAISE EXCEPTION 'Parent table given does not exist (%)', p_parent_table; END IF; IF v_type = 'time' THEN CASE WHEN v_partition_interval::interval = '15 mins' THEN suffix_timestamp := date_trunc('hour', p_value::timestamp) + '15min'::interval * floor(date_part('minute', p_value::timestamp) / 15.0); WHEN v_partition_interval::interval = '30 mins' THEN suffix_timestamp := date_trunc('hour', p_value::timestamp) + '30min'::interval * floor(date_part('minute', p_value::timestamp) / 30.0); WHEN v_partition_interval::interval = '1 hour' THEN suffix_timestamp := date_trunc('hour', p_value::timestamp); WHEN v_partition_interval::interval = '1 day' THEN suffix_timestamp := date_trunc('day', p_value::timestamp); WHEN v_partition_interval::interval = '1 week' THEN suffix_timestamp := date_trunc('week', p_value::timestamp); WHEN v_partition_interval::interval = '1 month' THEN suffix_timestamp := date_trunc('month', p_value::timestamp); WHEN v_partition_interval::interval = '3 months' THEN suffix_timestamp := date_trunc('quarter', p_value::timestamp); WHEN v_partition_interval::interval = '1 year' THEN suffix_timestamp := date_trunc('year', p_value::timestamp); END CASE; partition_table := v_parent_schema||'.'||@extschema@.check_name_length(v_parent_tablename, to_char(suffix_timestamp, v_datetime_string), TRUE); ELSIF v_type = 'id' THEN suffix_id := (p_value::bigint - (p_value::bigint % v_partition_interval::bigint)); partition_table := v_parent_schema||'.'||@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::timestamp >= 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::timestamp < 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::timestamp < 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::timestamp >= 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 := v_parent_schema||'.'||@extschema@.check_name_length(v_parent_tablename, to_char(suffix_timestamp, v_datetime_string), TRUE); END IF; END IF; SELECT schemaname, tablename INTO v_child_exists FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = partition_table; IF v_child_exists IS NOT NULL THEN table_exists := true; ELSE table_exists := false; END IF; RETURN; END $$; /* * Function to list all child partitions in a set. */ CREATE FUNCTION show_partitions (p_parent_table text, p_order text DEFAULT 'ASC') RETURNS TABLE (partition_schemaname text, partition_tablename text) LANGUAGE plpgsql STABLE SECURITY DEFINER AS $$ DECLARE v_datetime_string text; v_parent_schema text; v_parent_tablename text; v_partition_interval text; v_type text; BEGIN IF p_order NOT IN ('ASC', 'DESC') THEN RAISE EXCEPTION 'p_order paramter must be one of the following values: ASC, DESC'; END IF; SELECT partition_type , partition_interval , datetime_string INTO v_type , v_partition_interval , v_datetime_string FROM @extschema@.part_config WHERE parent_table = p_parent_table; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname||'.'||tablename = p_parent_table; IF v_type IN ('time', 'time-custom') THEN RETURN QUERY EXECUTE 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 ORDER BY to_timestamp(substring(c.relname from ((length(c.relname) - position(''p_'' in reverse(c.relname))) + 2) ), %L) %s' , v_parent_schema , v_parent_tablename , v_datetime_string , p_order); ELSIF v_type = 'id' THEN RETURN QUERY EXECUTE 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 ORDER BY substring(c.relname from ((length(c.relname) - position(''p_'' in reverse(c.relname))) + 2) )::bigint %s' , v_parent_schema , v_parent_tablename , p_order); END IF; END $$; /* * Function to undo partitioning. * Will actually work on any parent/child table set, not just ones created by pg_partman. */ CREATE OR REPLACE FUNCTION undo_partition(p_parent_table text, p_batch_count int DEFAULT 1, p_keep_table boolean DEFAULT true, p_jobmon boolean DEFAULT true, p_lock_wait numeric DEFAULT 0) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_adv_lock boolean; v_batch_loop_count bigint := 0; v_child_count bigint; v_child_table text; v_copy_sql text; v_function_name text; v_job_id bigint; v_jobmon_schema text; v_lock_iter int := 1; v_lock_obtained boolean := FALSE; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_partition_interval interval; v_rowcount bigint; v_step_id bigint; v_total bigint := 0; v_trig_name text; v_undo_count int := 0; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman undo_partition')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'undo_partition already running.'; RETURN 0; END IF; IF p_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', '@extschema@,'||v_jobmon_schema, 'false'); END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job(format('PARTMAN UNDO PARTITIONING: ', p_parent_table)); v_step_id := add_step(v_job_id, format('Undoing partitioning for table ', p_parent_table)); END IF; -- Stops new time partitons 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; -- Stop data going into child tables and stop new id partitions from being made. SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; 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); SELECT tgname INTO v_trig_name FROM pg_catalog.pg_trigger t WHERE tgname = v_trig_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'; RETURN -1; 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 EXECUTE format('DROP FUNCTION IF EXISTS %I.%I()', v_parent_schema, v_function_name); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Stopped partition creation process. Removed trigger & trigger function'); END IF; WHILE v_batch_loop_count < p_batch_count LOOP -- Get ordered list of child table in set. Store in variable one at a time per loop until none are left. WITH parent_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_parent_tablename AND n1.nspname = v_parent_schema ) SELECT c.relname INTO v_child_table FROM pg_catalog.pg_inherits i JOIN pg_catalog.pg_class c ON i.inhrelid = c.oid JOIN parent_info p ON i.inhparent = p.oid ORDER BY i.inhrelid ASC; EXIT WHEN v_child_table IS NULL; EXECUTE format('SELECT count(*) FROM %I.%I', v_parent_schema, v_child_table) INTO v_child_count; IF v_child_count = 0 THEN -- No rows left in this child table. Remove from partition set. -- lockwait timeout for table 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_child_table); 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 child table for removal from partition set'; RETURN -1; END IF; END IF; -- END p_lock_wait IF v_lock_obtained := FALSE; -- reset for reuse later EXECUTE format('ALTER TABLE %I.%I NO INHERIT %I.%I', v_parent_schema, v_child_table, v_parent_schema, v_parent_tablename); IF p_keep_table = false THEN EXECUTE format('DROP TABLE %I.%I', v_parent_schema, v_child_table); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', format('Child table DROPPED. Moved %s rows to parent', COALESCE(v_rowcount, 0))); END IF; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', format('Child table UNINHERITED, not DROPPED. Copied %s rows to parent', COALESCE(v_rowcount, 0))); END IF; END IF; v_undo_count := v_undo_count + 1; CONTINUE; END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, format('Removing child partition: %s.%s', v_parent_schema, v_child_table)); END IF; -- do some locking with timeout, if required 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('SELECT * FROM %I.%I FOR UPDATE NOWAIT', v_parent_schema, v_child_table); 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 batch of rows to move'; RETURN -1; END IF; END IF; v_copy_sql := format('INSERT INTO %I.%I SELECT * FROM %I.%I' , v_parent_schema , v_parent_tablename , v_parent_schema , v_child_table); EXECUTE v_copy_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total := v_total + v_rowcount; EXECUTE format('ALTER TABLE %I.%I NO INHERIT %I.%I' , v_parent_schema , v_child_table , v_parent_schema , v_parent_tablename); IF p_keep_table = false THEN EXECUTE format('DROP TABLE %I.%I', v_parent_schema, v_child_table); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', format('Child table DROPPED. Moved %s rows to parent', v_rowcount)); END IF; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', format('Child table UNINHERITED, not DROPPED. Copied %s rows to parent', v_rowcount)); END IF; END IF; v_batch_loop_count := v_batch_loop_count + 1; v_undo_count := v_undo_count + 1; END LOOP; IF v_undo_count = 0 THEN -- FOR loop never ran, so there's no child tables left. DELETE FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing config from pg_partman (if it existed)'); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; RAISE NOTICE 'Copied % row(s) from % child table(s) to the parent: %', v_total, v_undo_count, p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Final stats'); PERFORM update_step(v_step_id, 'OK', format('Copied %s row(s) from %s child table(s) to the parent', v_total, v_undo_count)); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', v_old_search_path, 'false'); END IF; RETURN v_total; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN UNDO PARTITIONING: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Function to undo id-based partitioning created by this extension */ CREATE OR REPLACE FUNCTION undo_partition_id(p_parent_table text, p_batch_count int DEFAULT 1, p_batch_interval bigint DEFAULT NULL, p_keep_table boolean DEFAULT true, p_lock_wait numeric DEFAULT 0) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_adv_lock boolean; v_batch_loop_count int := 0; v_child_loop_total bigint := 0; v_child_min bigint; v_child_table text; v_control text; v_exists int; v_function_name text; v_inner_loop_count int; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_lock_iter int := 1; v_lock_obtained boolean := FALSE; v_move_sql text; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_partition_interval bigint; v_row record; v_rowcount bigint; v_step_id bigint; v_sub_count int; v_trig_name text; v_total bigint := 0; v_undo_count int := 0; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman undo_partition_id')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'undo_partition_id already running.'; RETURN 0; END IF; SELECT partition_interval::bigint , control , jobmon INTO v_partition_interval , v_control , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND partition_type = 'id'; IF v_partition_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; -- Check if any child tables are themselves partitioned or part of an inheritance tree. Prevent undo at this level if so. -- Need to either lock child tables at all levels or handle the proper removal of triggers on all child tables first -- before multi-level undo can be performed safely. FOR v_row IN SELECT partition_schemaname, partition_tablename FROM @extschema@.show_partitions(p_parent_table) LOOP SELECT count(*) INTO v_sub_count FROM pg_catalog.pg_inherits i JOIN pg_catalog.pg_class c ON i.inhparent = c.oid JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid WHERE c.relname = v_row.partition_tablename AND n.nspname = v_row.partition_schemaname; 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; END IF; END LOOP; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', '@extschema@,'||v_jobmon_schema, 'false'); END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job(format('PARTMAN UNDO PARTITIONING: %s', p_parent_table)); v_step_id := add_step(v_job_id, format('Undoing partitioning for table %s', p_parent_table)); END IF; IF p_batch_interval IS NULL THEN p_batch_interval := v_partition_interval; END IF; -- Stops new time partitons 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; -- Stop data going into child tables and stop new id partitions from being made. SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; 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); SELECT tgname INTO v_trig_name FROM pg_catalog.pg_trigger t WHERE tgname = v_trig_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'; RETURN -1; 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 EXECUTE format('DROP FUNCTION IF EXISTS %I.%I()', v_parent_schema, v_function_name); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Stopped partition creation process. Removed trigger & trigger function'); END IF; <> WHILE v_batch_loop_count < p_batch_count LOOP -- Get ordered list of child table in set. Store in variable one at a time per loop until none are left. WITH parent_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_parent_tablename AND n1.nspname = v_parent_schema ) SELECT c.relname INTO v_child_table FROM pg_catalog.pg_inherits i JOIN pg_catalog.pg_class c ON i.inhrelid = c.oid JOIN parent_info p ON i.inhparent = p.oid ORDER BY i.inhrelid ASC; EXIT WHEN v_child_table IS NULL; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, format('Removing child partition: %s.%s', v_parent_schema, v_child_table)); END IF; EXECUTE format('SELECT min(%I) FROM %I.%I', v_control, v_parent_schema, v_child_table) INTO v_child_min; IF v_child_min IS NULL THEN -- No rows left in this child table. Remove from partition set. -- lockwait timeout for table 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_child_table); 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 child table for removal from partition set'; RETURN -1; END IF; END IF; -- END p_lock_wait IF v_lock_obtained := FALSE; -- reset for reuse later EXECUTE format('ALTER TABLE %I.%I NO INHERIT %I.%I' , v_parent_schema , v_child_table , v_parent_schema , v_parent_tablename); IF p_keep_table = false THEN EXECUTE format('DROP TABLE %I.%I', v_parent_schema, v_child_table); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', format('Child table DROPPED. Moved %s rows to parent', v_child_loop_total)); END IF; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', format('Child table UNINHERITED, not DROPPED. Moved %s rows to parent', v_child_loop_total)); END IF; END IF; v_undo_count := v_undo_count + 1; CONTINUE outer_child_loop; END IF; v_inner_loop_count := 1; v_child_loop_total := 0; <> LOOP -- lockwait timeout for row batches 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('SELECT * FROM %I.%I WHERE %I <= %s FOR UPDATE NOWAIT' , v_parent_schema , v_child_table , v_control , v_child_min + (p_batch_interval * v_inner_loop_count)); 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 batch of rows to move'; RETURN -1; END IF; END IF; -- Get everything from the current child minimum up to the multiples of the given interval v_move_sql := format('WITH move_data AS ( DELETE FROM %I.%I WHERE %I <= %s RETURNING *) INSERT INTO %I.%I SELECT * FROM move_data' , v_parent_schema , v_child_table , v_control , v_child_min + (p_batch_interval * v_inner_loop_count) , v_parent_schema , v_parent_tablename); EXECUTE v_move_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total := v_total + v_rowcount; v_child_loop_total := v_child_loop_total + v_rowcount; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', format('Moved %s rows to parent.', v_child_loop_total)); END IF; EXIT inner_child_loop WHEN v_rowcount = 0; -- exit before loop incr if table is empty v_inner_loop_count := v_inner_loop_count + 1; v_batch_loop_count := v_batch_loop_count + 1; EXIT outer_child_loop WHEN v_batch_loop_count >= p_batch_count; -- Exit outer FOR loop if p_batch_count is reached END LOOP inner_child_loop; END LOOP outer_child_loop; IF v_batch_loop_count < p_batch_count THEN -- FOR loop never ran, so there's no child tables left. DELETE FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing config from pg_partman'); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; RAISE NOTICE 'Copied % row(s) to the parent. Removed % partitions.', v_total, v_undo_count; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Final stats'); PERFORM update_step(v_step_id, 'OK', format('Copied %s row(s) to the parent. Removed %s partitions.', v_total, v_undo_count)); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', v_old_search_path, 'false'); END IF; RETURN v_total; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN UNDO PARTITIONING: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Function to undo time-based partitioning created by this extension */ CREATE OR REPLACE FUNCTION undo_partition_time(p_parent_table text, p_batch_count int DEFAULT 1, p_batch_interval interval DEFAULT NULL, p_keep_table boolean DEFAULT true, p_lock_wait numeric DEFAULT 0) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_adv_lock boolean; v_batch_loop_count int := 0; v_child_min timestamptz; v_child_loop_total bigint := 0; v_child_table text; v_control text; v_function_name text; v_inner_loop_count int; v_lock_iter int := 1; v_lock_obtained boolean := FALSE; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_move_sql text; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_partition_interval interval; v_row record; v_rowcount bigint; v_step_id bigint; v_sub_count int; v_total bigint := 0; v_trig_name text; v_type text; v_undo_count int := 0; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman undo_partition_time')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'undo_partition_time already running.'; RETURN 0; END IF; SELECT partition_type , partition_interval::interval , control , jobmon INTO v_type , v_partition_interval , v_control , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (partition_type = 'time' OR partition_type = 'time-custom'); IF v_partition_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; -- Check if any child tables are themselves partitioned or part of an inheritance tree. Prevent undo at this level if so. -- Need to either lock child tables at all levels or handle the proper removal of triggers on all child tables first -- before multi-level undo can be performed safely. FOR v_row IN SELECT partition_schemaname, partition_tablename FROM @extschema@.show_partitions(p_parent_table) LOOP SELECT count(*) INTO v_sub_count FROM pg_catalog.pg_inherits i JOIN pg_catalog.pg_class c ON i.inhparent = c.oid JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid WHERE c.relname = v_row.partition_tablename AND n.nspname = v_row.partition_schemaname; 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; END IF; END LOOP; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', '@extschema@,'||v_jobmon_schema, 'false'); END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job(format('PARTMAN UNDO PARTITIONING: %s', p_parent_table)); v_step_id := add_step(v_job_id, format('Undoing partitioning for table %s', p_parent_table)); END IF; IF p_batch_interval IS NULL THEN p_batch_interval := v_partition_interval; END IF; -- Stops new time partitons 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; -- Stop data going into child tables. SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; 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); SELECT tgname INTO v_trig_name FROM pg_catalog.pg_trigger t WHERE tgname = v_trig_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'; RETURN -1; 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 EXECUTE format('DROP FUNCTION IF EXISTS %I.%I()', v_parent_schema, v_function_name); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Stopped partition creation process. Removed trigger & trigger function'); END IF; <> WHILE v_batch_loop_count < p_batch_count LOOP -- Get ordered list of child table in set. Store in variable one at a time per loop until none are left. WITH parent_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_parent_tablename AND n1.nspname = v_parent_schema ) SELECT c.relname INTO v_child_table FROM pg_catalog.pg_inherits i JOIN pg_catalog.pg_class c ON i.inhrelid = c.oid JOIN parent_info p ON i.inhparent = p.oid ORDER BY i.inhrelid ASC; EXIT WHEN v_child_table IS NULL; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, format('Removing child partition: %s.%s', v_parent_schema, v_child_table)); END IF; EXECUTE format('SELECT min(%I) FROM %I.%I', v_control, v_parent_schema, v_child_table) INTO v_child_min; IF v_child_min IS NULL THEN -- No rows left in this child table. Remove from partition set. -- lockwait timeout for table 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_child_table); 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 child table for removal from partition set'; RETURN -1; END IF; END IF; -- END p_lock_wait IF v_lock_obtained := FALSE; -- reset for reuse later EXECUTE format('ALTER TABLE %I.%I NO INHERIT %I.%I' , v_parent_schema , v_child_table , v_parent_schema , v_parent_tablename); IF p_keep_table = false THEN EXECUTE format('DROP TABLE %I.%I', v_parent_schema, v_child_table); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', format('Child table DROPPED. Moved %s rows to parent', v_child_loop_total)); END IF; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', format('Child table UNINHERITED, not DROPPED. Moved %s rows to parent', v_child_loop_total)); END IF; END IF; IF v_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; CONTINUE outer_child_loop; END IF; v_inner_loop_count := 1; v_child_loop_total := 0; <> LOOP -- do some locking with timeout, if required 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('SELECT * FROM %I.%I WHERE %I <= %L FOR UPDATE NOWAIT' , v_parent_schema , v_child_table , v_control , v_child_min + (p_batch_interval * v_inner_loop_count)); 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 batch of rows to move'; RETURN -1; END IF; END IF; -- Get everything from the current child minimum up to the multiples of the given interval v_move_sql := format('WITH move_data AS ( DELETE FROM %I.%I WHERE %I <= %L RETURNING *) INSERT INTO %I.%I SELECT * FROM move_data' , v_parent_schema , v_child_table , v_control , v_child_min + (p_batch_interval * v_inner_loop_count) , v_parent_schema , v_parent_tablename); EXECUTE v_move_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total := v_total + v_rowcount; v_child_loop_total := v_child_loop_total + v_rowcount; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', format('Moved %s rows to parent.', v_child_loop_total)); END IF; EXIT inner_child_loop WHEN v_rowcount = 0; -- exit before loop incr if table is empty v_inner_loop_count := v_inner_loop_count + 1; v_batch_loop_count := v_batch_loop_count + 1; EXIT outer_child_loop WHEN v_batch_loop_count >= p_batch_count; -- Exit outer FOR loop if p_batch_count is reached END LOOP inner_child_loop; END LOOP outer_child_loop; IF v_batch_loop_count < p_batch_count THEN -- FOR loop never ran, so there's no child tables left. DELETE FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing config from pg_partman'); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; RAISE NOTICE 'Copied % row(s) to the parent. Removed % partitions.', v_total, v_undo_count; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Final stats'); PERFORM update_step(v_step_id, 'OK', format('Copied %s row(s) to the parent. Removed %s partitions.', v_total, v_undo_count)); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', v_old_search_path, 'false'); END IF; RETURN v_total; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN UNDO PARTITIONING: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; -- Restore dropped object privileges DO $$ DECLARE v_row record; BEGIN FOR v_row IN SELECT statement FROM partman_preserve_privs_temp LOOP IF v_row.statement IS NOT NULL THEN EXECUTE v_row.statement; END IF; END LOOP; END $$; DROP TABLE IF EXISTS partman_preserve_privs_temp; pg_partman-2.2.2/updates/pg_partman--2.1.0--2.2.0.sql000066400000000000000000004563671262146621700214420ustar00rootroot00000000000000-- 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 following query 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. /* WITH parent_info AS ( SELECT c1.oid FROM pg_catalog.pg_class c1 JOIN pg_catalog.pg_namespace n1 ON c1.relnamespace = n1.oid WHERE n1.nspname||'.'||c1.relname = '<<>>' ) , child_tables AS ( SELECT n.nspname||'.'||c.relname AS 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 JOIN parent_info pi ON h.inhparent = pi.oid ) SELECT DISTINCT a.sub_partition_type , a.sub_control , a.sub_partition_interval , a.sub_constraint_cols , a.sub_premake , a.sub_inherit_fk , a.sub_retention , a.sub_retention_schema , a.sub_retention_keep_table , a.sub_retention_keep_index , a.sub_use_run_maintenance , a.sub_jobmon FROM <<>>.part_config_sub a JOIN child_tables b on a.sub_parent = b.tablename; */ CREATE TEMP TABLE partman_preserve_privs_temp (statement text); INSERT INTO partman_preserve_privs_temp SELECT 'GRANT EXECUTE ON FUNCTION @extschema@.run_maintenance(text, boolean, boolean, boolean) TO '||array_to_string(array_agg(grantee::text), ',')||';' FROM information_schema.routine_privileges WHERE routine_schema = '@extschema@' AND routine_name = 'run_maintenance'; INSERT INTO partman_preserve_privs_temp SELECT 'GRANT EXECUTE ON FUNCTION @extschema@.create_parent(text, text, text, text, text[], int, boolean, text, boolean, boolean, boolean, boolean) TO '||array_to_string(array_agg(grantee::text), ',')||';' FROM information_schema.routine_privileges WHERE routine_schema = '@extschema@' AND routine_name = 'create_parent'; INSERT INTO partman_preserve_privs_temp SELECT 'GRANT EXECUTE ON FUNCTION @extschema@.create_sub_parent(text, text, text, text, text[], int, text, boolean, boolean, boolean, boolean) TO '||array_to_string(array_agg(grantee::text), ',')||';' FROM information_schema.routine_privileges WHERE routine_schema = '@extschema@' AND routine_name = 'create_sub_parent'; INSERT INTO partman_preserve_privs_temp SELECT 'GRANT EXECUTE ON FUNCTION @extschema@.check_subpart_sameconfig(text) TO '||array_to_string(array_agg(grantee::text), ',')||';' FROM information_schema.routine_privileges WHERE routine_schema = '@extschema@' AND routine_name = 'check_subpart_sameconfig'; DROP FUNCTION run_maintenance(text, boolean, boolean); DROP FUNCTION @extschema@.create_parent(text, text, text, text, text[], int, boolean, text, boolean, boolean, boolean); DROP FUNCTION @extschema@.create_sub_parent(text, text, text, text, text[], int, text, boolean, boolean, boolean); ALTER TABLE @extschema@.part_config_sub DROP CONSTRAINT subpart_sameconfig_chk; DROP FUNCTION @extschema@.check_subpart_sameconfig(text); ALTER TABLE @extschema@.part_config ADD COLUMN epoch boolean NOT NULL DEFAULT false; ALTER TABLE @extschema@.part_config ADD COLUMN optimize_trigger int NOT NULL DEFAULT 4; ALTER TABLE @extschema@.part_config ADD COLUMN optimize_constraint int NOT NULL DEFAULT 30; UPDATE @extschema@.part_config SET optimize_trigger = premake; UPDATE @extschema@.part_config SET optimize_constraint = premake; ALTER TABLE @extschema@.part_config_sub ADD COLUMN sub_epoch boolean NOT NULL DEFAULT false; ALTER TABLE @extschema@.part_config_sub ADD COLUMN sub_optimize_trigger int NOT NULL DEFAULT 4; ALTER TABLE @extschema@.part_config_sub ADD COLUMN sub_optimize_constraint int NOT NULL DEFAULT 30; UPDATE @extschema@.part_config_sub SET sub_optimize_trigger = sub_premake; UPDATE @extschema@.part_config_sub SET sub_optimize_constraint = sub_premake; /* * 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. * If anyone can get a working constraint/trigger, please help! */ CREATE FUNCTION @extschema@.check_subpart_sameconfig(p_parent_table text) RETURNS TABLE (sub_partition_type text , sub_control text , sub_partition_interval text , sub_constraint_cols text[] , sub_premake int , sub_inherit_fk boolean , sub_retention text , sub_retention_schema text , sub_retention_keep_table boolean , sub_retention_keep_index boolean , sub_use_run_maintenance boolean , sub_epoch boolean , sub_optimize_trigger int , sub_optimize_constraint int , sub_jobmon boolean) LANGUAGE sql STABLE SECURITY DEFINER AS $$ WITH parent_info AS ( SELECT c1.oid FROM pg_catalog.pg_class c1 JOIN pg_catalog.pg_namespace n1 ON c1.relnamespace = n1.oid WHERE n1.nspname||'.'||c1.relname = p_parent_table ) , child_tables AS ( SELECT n.nspname||'.'||c.relname AS 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 JOIN parent_info pi ON h.inhparent = pi.oid ) -- Column order here must match the RETURNS TABLE definition SELECT DISTINCT a.sub_partition_type , a.sub_control , a.sub_partition_interval , a.sub_constraint_cols , a.sub_premake , a.sub_inherit_fk , a.sub_retention , a.sub_retention_schema , a.sub_retention_keep_table , a.sub_retention_keep_index , a.sub_use_run_maintenance , a.sub_epoch , a.sub_optimize_trigger , a.sub_optimize_constraint , a.sub_jobmon FROM @extschema@.part_config_sub a JOIN child_tables b on a.sub_parent = b.tablename; $$; -- Do a check here to ensure configuration is consistent using above new function DO $$ DECLARE v_count int; v_row record; BEGIN FOR v_row IN SELECT sub_parent FROM @extschema@.part_config_sub LOOP SELECT count(*) INTO v_count FROM @extschema@.check_subpart_sameconfig(v_row.sub_parent); IF v_count > 1 THEN RAISE EXCEPTION 'UPDATE ABORTED: Inconsistent data found in part_config_sub table for parent table %. Please see 2.2.0 release notes for why this occurred and how to fix it.', v_row.sub_parent; END IF; END LOOP; END $$; /* * Create the trigger function for the parent table of an id-based partition set */ CREATE OR REPLACE FUNCTION create_function_id(p_parent_table text, p_job_id bigint DEFAULT NULL) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_control text; v_count int; v_current_partition_name text; v_current_partition_id bigint; v_datetime_string text; v_final_partition_id bigint; v_function_name text; v_higher_parent text := p_parent_table; v_id_position int; v_job_id bigint; v_jobmon text; v_jobmon_schema text; v_last_partition text; v_max bigint; v_next_partition_id bigint; v_next_partition_name text; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_partition_interval bigint; v_premake int; v_prev_partition_id bigint; v_prev_partition_name text; v_row_max_id record; v_run_maint boolean; v_step_id bigint; v_top_parent text := p_parent_table; v_trig_func text; v_optimize_trigger int; BEGIN SELECT partition_interval::bigint , control , premake , optimize_trigger , use_run_maintenance , jobmon INTO v_partition_interval , v_control , v_premake , v_optimize_trigger , v_run_maint , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND partition_type = 'id'; IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; SELECT partition_tablename INTO v_last_partition FROM @extschema@.show_partitions(p_parent_table, 'DESC') LIMIT 1; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', '@extschema@,'||v_jobmon_schema, 'false'); END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN IF p_job_id IS NULL THEN v_job_id := add_job(format('PARTMAN CREATE FUNCTION: %s', p_parent_table)); ELSE v_job_id = p_job_id; END IF; v_step_id := add_step(v_job_id, format('Creating partition function for table %s', p_parent_table)); END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; v_function_name := @extschema@.check_name_length(v_parent_tablename, '_part_trig_func', FALSE); -- Get the highest level top parent if multi-level partitioned in order to get proper max() value below WHILE v_higher_parent IS NOT NULL LOOP -- initially set in DECLARE WITH top_oid AS ( SELECT i.inhparent AS top_parent_oid FROM pg_catalog.pg_inherits i JOIN pg_catalog.pg_class c ON c.oid = i.inhrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname||'.'||c.relname = v_higher_parent ) SELECT n.nspname||'.'||c.relname INTO v_higher_parent 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 JOIN @extschema@.part_config p ON p.parent_table = n.nspname||'.'||c.relname WHERE p.partition_type = 'id'; IF v_higher_parent IS NOT NULL THEN -- initially set in DECLARE v_top_parent := v_higher_parent; END IF; END 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 parent. FOR v_row_max_id IN SELECT partition_schemaname, partition_tablename FROM @extschema@.show_partitions(v_top_parent, 'DESC') LOOP EXECUTE format('SELECT max(%I) FROM %I.%I', v_control, v_row_max_id.partition_schemaname, v_row_max_id.partition_tablename) INTO v_max; IF v_max IS NOT NULL THEN EXIT; END IF; END LOOP; IF v_max IS NULL THEN v_max := 0; END IF; v_current_partition_id = v_max - (v_max % v_partition_interval); v_next_partition_id := v_current_partition_id + v_partition_interval; v_current_partition_name := @extschema@.check_name_length(v_parent_tablename, v_current_partition_id::text, TRUE); v_trig_func := format('CREATE OR REPLACE FUNCTION %I.%I() RETURNS trigger LANGUAGE plpgsql AS $t$ DECLARE v_count int; v_current_partition_id bigint; v_current_partition_name text; v_id_position int; v_last_partition text := %L; v_next_partition_id bigint; v_next_partition_name text; v_partition_created boolean; BEGIN IF TG_OP = ''INSERT'' THEN IF NEW.%I >= %s AND NEW.%I < %s THEN ' , v_parent_schema , v_function_name , v_last_partition , v_control , v_current_partition_id , v_control , v_next_partition_id ); SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname = v_parent_schema AND tablename = v_current_partition_name; IF v_count > 0 THEN v_trig_func := v_trig_func || format(' INSERT INTO %I.%I VALUES (NEW.*); ', v_parent_schema, v_current_partition_name); ELSE v_trig_func := v_trig_func || ' -- Child table for current values does not exist in this partition set, so write to parent RETURN NEW;'; END IF; FOR i IN 1..v_optimize_trigger LOOP v_prev_partition_id := v_current_partition_id - (v_partition_interval * i); v_next_partition_id := v_current_partition_id + (v_partition_interval * i); v_final_partition_id := v_next_partition_id + v_partition_interval; v_prev_partition_name := @extschema@.check_name_length(v_parent_tablename, v_prev_partition_id::text, TRUE); v_next_partition_name := @extschema@.check_name_length(v_parent_tablename, v_next_partition_id::text, TRUE); -- Check that child table exist before making a rule to insert to them. -- Handles optimize_trigger being larger than premake (to go back in time further) and edge case of changing optimize_trigger immediately after running create_parent(). SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname = v_parent_schema AND tablename = v_prev_partition_name; IF v_count > 0 THEN -- Only handle previous partitions if they're starting above zero IF v_prev_partition_id >= 0 THEN v_trig_func := v_trig_func ||format(' ELSIF NEW.%I >= %s AND NEW.%I < %s THEN INSERT INTO %I.%I VALUES (NEW.*); ' , v_control , v_prev_partition_id , v_control , v_prev_partition_id + v_partition_interval , v_parent_schema , v_prev_partition_name ); END IF; END IF; SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname ||'.'||tablename = v_next_partition_name; IF v_count > 0 THEN v_trig_func := v_trig_func ||format(' ELSIF NEW.%I >= %s AND NEW.%I < %s THEN INSERT INTO %I.%I VALUES (NEW.*);' , v_control , v_next_partition_id , v_control , v_final_partition_id , v_parent_schema , v_next_partition_name ); END IF; END LOOP; v_trig_func := v_trig_func ||format(' ELSE v_current_partition_id := NEW.%I - (NEW.%I %% %s); v_current_partition_name := @extschema@.check_name_length(%L, v_current_partition_id::text, TRUE); SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname = %L AND tablename = v_current_partition_name; IF v_count > 0 THEN EXECUTE format(''INSERT INTO %%I.%%I VALUES($1.*)'', %L, v_current_partition_name) USING NEW; ELSE RETURN NEW; END IF; END IF;' , v_control , v_control , v_partition_interval , v_parent_tablename , v_parent_schema , v_parent_schema ); IF v_run_maint IS FALSE THEN v_trig_func := v_trig_func ||format(' v_current_partition_id := NEW.%I - (NEW.%I %% %s); IF (NEW.%I %% %s) > (%s / 2) THEN v_id_position := (length(v_last_partition) - position(''p_'' in reverse(v_last_partition))) + 2; v_next_partition_id := (substring(v_last_partition from v_id_position)::bigint) + %s; WHILE ((v_next_partition_id - v_current_partition_id) / %s) <= %s LOOP v_partition_created := @extschema@.create_partition_id(%L, ARRAY[v_next_partition_id]); IF v_partition_created THEN PERFORM @extschema@.create_function_id(%L); PERFORM @extschema@.apply_constraints(%L); END IF; v_next_partition_id := v_next_partition_id + %s; END LOOP; END IF;' , v_control , v_control , v_partition_interval , v_control , v_partition_interval , v_partition_interval , v_partition_interval , v_partition_interval , v_premake , p_parent_table , p_parent_table , p_parent_table , v_partition_interval ); END IF; v_trig_func := v_trig_func ||' END IF; RETURN NULL; END $t$;'; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', format('Added function for current id interval: %s to %s', v_current_partition_id, v_final_partition_id-1)); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', v_old_search_path, 'false'); END IF; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN CREATE FUNCTION: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''Partition function maintenance for table %s failed'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Create the trigger function for the parent table of a time-based partition set */ CREATE OR REPLACE FUNCTION create_function_time(p_parent_table text, p_job_id bigint DEFAULT NULL) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_control text; v_count int; v_current_partition_name text; v_current_partition_timestamp timestamptz; v_datetime_string text; v_epoch boolean; v_final_partition_timestamp timestamptz; v_function_name text; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_new_length int; v_next_partition_name text; v_next_partition_timestamp timestamptz; v_parent_schema text; v_parent_tablename text; v_partition_interval interval; v_prev_partition_name text; v_prev_partition_timestamp timestamptz; v_step_id bigint; v_trig_func text; v_optimize_trigger int; v_type text; BEGIN SELECT partition_type , partition_interval::interval , epoch , control , optimize_trigger , datetime_string , jobmon INTO v_type , v_partition_interval , v_epoch , v_control , v_optimize_trigger , v_datetime_string , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (partition_type = 'time' OR partition_type = 'time-custom'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', '@extschema@,'||v_jobmon_schema, 'false'); END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN IF p_job_id IS NULL THEN v_job_id := add_job(format('PARTMAN CREATE FUNCTION: %s', p_parent_table)); ELSE v_job_id = p_job_id; END IF; v_step_id := add_step(v_job_id, format('Creating partition function for table %s', p_parent_table)); END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; v_function_name := @extschema@.check_name_length(v_parent_tablename, '_part_trig_func', FALSE); IF v_type = 'time' THEN v_trig_func := format('CREATE OR REPLACE FUNCTION %I.%I() RETURNS trigger LANGUAGE plpgsql AS $t$ DECLARE v_count int; v_partition_name text; v_partition_timestamp timestamptz; BEGIN IF TG_OP = ''INSERT'' THEN ' , v_parent_schema , v_function_name); IF v_epoch = false THEN CASE WHEN v_partition_interval = '15 mins' THEN v_trig_func := v_trig_func||format('v_partition_timestamp := date_trunc(''hour'', NEW.%I) + ''15min''::interval * floor(date_part(''minute'', NEW.%I) / 15.0);' , v_control , v_control); v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0); WHEN v_partition_interval = '30 mins' THEN v_trig_func := v_trig_func||format('v_partition_timestamp := date_trunc(''hour'', NEW.%I) + ''30min''::interval * floor(date_part(''minute'', NEW.%I) / 30.0);' , v_control , v_control); v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0); WHEN v_partition_interval = '1 hour' THEN v_trig_func := v_trig_func||format('v_partition_timestamp := date_trunc(''hour'', NEW.%I);', v_control); v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP); WHEN v_partition_interval = '1 day' THEN v_trig_func := v_trig_func||format('v_partition_timestamp := date_trunc(''day'', NEW.%I);', v_control); v_current_partition_timestamp := date_trunc('day', CURRENT_TIMESTAMP); WHEN v_partition_interval = '1 week' THEN v_trig_func := v_trig_func||format('v_partition_timestamp := date_trunc(''week'', NEW.%I);', v_control); v_current_partition_timestamp := date_trunc('week', CURRENT_TIMESTAMP); WHEN v_partition_interval = '1 month' THEN v_trig_func := v_trig_func||format('v_partition_timestamp := date_trunc(''month'', NEW.%I);', v_control); v_current_partition_timestamp := date_trunc('month', CURRENT_TIMESTAMP); WHEN v_partition_interval = '3 months' THEN v_trig_func := v_trig_func||format('v_partition_timestamp := date_trunc(''quarter'', NEW.%I);', v_control); v_current_partition_timestamp := date_trunc('quarter', CURRENT_TIMESTAMP); WHEN v_partition_interval = '1 year' THEN v_trig_func := v_trig_func||format('v_partition_timestamp := date_trunc(''year'', NEW.%I);', v_control); v_current_partition_timestamp := date_trunc('year', CURRENT_TIMESTAMP); END CASE; ELSE -- epoch is true CASE WHEN v_partition_interval = '15 mins' THEN v_trig_func := v_trig_func||format('v_partition_timestamp := date_trunc(''hour'', to_timestamp(NEW.%I)) + ''15min''::interval * floor(date_part(''minute'', NEW.%I) / 15.0);' , v_control , v_control); v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '15min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 15.0); WHEN v_partition_interval = '30 mins' THEN v_trig_func := v_trig_func||format('v_partition_timestamp := date_trunc(''hour'', to_timestamp(NEW.%I)) + ''30min''::interval * floor(date_part(''minute'', NEW.%I) / 30.0);' , v_control , v_control); v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP) + '30min'::interval * floor(date_part('minute', CURRENT_TIMESTAMP) / 30.0); WHEN v_partition_interval = '1 hour' THEN v_trig_func := v_trig_func||format('v_partition_timestamp := date_trunc(''hour'', to_timestamp(NEW.%I));', v_control); v_current_partition_timestamp := date_trunc('hour', CURRENT_TIMESTAMP); WHEN v_partition_interval = '1 day' THEN v_trig_func := v_trig_func||format('v_partition_timestamp := date_trunc(''day'', to_timestamp(NEW.%I));', v_control); v_current_partition_timestamp := date_trunc('day', CURRENT_TIMESTAMP); WHEN v_partition_interval = '1 week' THEN v_trig_func := v_trig_func||format('v_partition_timestamp := date_trunc(''week'', to_timestamp(NEW.%I));', v_control); v_current_partition_timestamp := date_trunc('week', CURRENT_TIMESTAMP); WHEN v_partition_interval = '1 month' THEN v_trig_func := v_trig_func||format('v_partition_timestamp := date_trunc(''month'', to_timestamp(NEW.%I));', v_control); v_current_partition_timestamp := date_trunc('month', CURRENT_TIMESTAMP); WHEN v_partition_interval = '3 months' THEN v_trig_func := v_trig_func||format('v_partition_timestamp := date_trunc(''quarter'', to_timestamp(NEW.%I));', v_control); v_current_partition_timestamp := date_trunc('quarter', CURRENT_TIMESTAMP); WHEN v_partition_interval = '1 year' THEN v_trig_func := v_trig_func||format('v_partition_timestamp := date_trunc(''year'', to_timestamp(NEW.%I));', v_control); v_current_partition_timestamp := date_trunc('year', CURRENT_TIMESTAMP); END CASE; END IF; v_current_partition_name := @extschema@.check_name_length(v_parent_tablename, to_char(v_current_partition_timestamp, v_datetime_string), TRUE); v_next_partition_timestamp := v_current_partition_timestamp + v_partition_interval::interval; IF v_epoch = false THEN v_trig_func := v_trig_func ||format(' IF NEW.%I >= %L AND NEW.%I < %L THEN ' , v_control , v_current_partition_timestamp , v_control , v_next_partition_timestamp); ELSE v_trig_func := v_trig_func ||format(' IF to_timestamp(NEW.%I) >= %L AND to_timestamp(NEW.%I) < %L THEN ' , v_control , v_current_partition_timestamp , v_control , v_next_partition_timestamp); END IF; SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname = v_parent_schema AND tablename = v_current_partition_name; IF v_count > 0 THEN v_trig_func := v_trig_func || format(' INSERT INTO %I.%I VALUES (NEW.*); ', v_parent_schema, v_current_partition_name); ELSE v_trig_func := v_trig_func || ' -- Child table for current values does not exist in this partition set, so write to parent RETURN NEW;'; END IF; FOR i IN 1..v_optimize_trigger LOOP v_prev_partition_timestamp := v_current_partition_timestamp - (v_partition_interval::interval * i); v_next_partition_timestamp := v_current_partition_timestamp + (v_partition_interval::interval * i); v_final_partition_timestamp := v_next_partition_timestamp + (v_partition_interval::interval); v_prev_partition_name := @extschema@.check_name_length(v_parent_tablename, to_char(v_prev_partition_timestamp, v_datetime_string), TRUE); v_next_partition_name := @extschema@.check_name_length(v_parent_tablename, to_char(v_next_partition_timestamp, v_datetime_string), TRUE); -- Check that child table exist before making a rule to insert to them. -- Handles optimize_trigger being larger than premake (to go back in time further) and edge case of changing optimize_trigger immediately after running create_parent(). SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname = v_parent_schema AND tablename = v_prev_partition_name; IF v_count > 0 THEN IF v_epoch = false THEN v_trig_func := v_trig_func ||format(' ELSIF NEW.%I >= %L AND NEW.%I < %L THEN INSERT INTO %I.%I VALUES (NEW.*);' , v_control , v_prev_partition_timestamp , v_control , v_prev_partition_timestamp + v_partition_interval::interval , v_parent_schema , v_prev_partition_name); ELSE v_trig_func := v_trig_func ||format(' ELSIF to_timestamp(NEW.%I) >= %L AND to_timestamp(NEW.%I) < %L THEN INSERT INTO %I.%I VALUES (NEW.*);' , v_control , v_prev_partition_timestamp , v_control , v_prev_partition_timestamp + v_partition_interval::interval , v_parent_schema , v_prev_partition_name); END IF; END IF; SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname = v_parent_schema AND tablename = v_next_partition_name; IF v_count > 0 THEN IF v_epoch = false THEN v_trig_func := v_trig_func ||format(' ELSIF NEW.%I >= %L AND NEW.%I < %L THEN INSERT INTO %I.%I VALUES (NEW.*);' , v_control , v_next_partition_timestamp , v_control , v_final_partition_timestamp , v_parent_schema , v_next_partition_name); ELSE v_trig_func := v_trig_func ||format(' ELSIF to_timestamp(NEW.%I) >= %L AND to_timestamp(NEW.%I) < %L THEN INSERT INTO %I.%I VALUES (NEW.*);' , v_control , v_next_partition_timestamp , v_control , v_final_partition_timestamp , v_parent_schema , v_next_partition_name); END IF; END IF; END LOOP; v_trig_func := v_trig_func||format(' ELSE v_partition_name := @extschema@.check_name_length(%L, to_char(v_partition_timestamp, %L), TRUE); SELECT count(*) INTO v_count FROM pg_catalog.pg_tables WHERE schemaname = %L AND tablename = v_partition_name; IF v_count > 0 THEN EXECUTE format(''INSERT INTO %%I.%%I VALUES($1.*)'', %L, v_partition_name) USING NEW; ELSE RETURN NEW; END IF; END IF;' , v_parent_tablename , v_datetime_string , v_parent_schema , v_parent_schema); v_trig_func := v_trig_func ||' END IF; RETURN NULL; END $t$;'; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', format('Added function for current time interval: %s to %s' , v_current_partition_timestamp , v_final_partition_timestamp-'1sec'::interval)); END IF; ELSIF v_type = 'time-custom' THEN v_trig_func := format('CREATE OR REPLACE FUNCTION %I.%I() RETURNS trigger LANGUAGE plpgsql AS $t$ DECLARE v_child_schemaname text; v_child_table text; v_child_tablename text; BEGIN ' , v_parent_schema , v_function_name); IF v_epoch = false THEN v_trig_func := v_trig_func || format(' SELECT child_table INTO v_child_table FROM @extschema@.custom_time_partitions WHERE partition_range @> NEW.%I AND parent_table = %L;' , v_control , v_parent_schema||'.'||v_parent_tablename); ELSE -- epoch true v_trig_func := v_trig_func || format(' SELECT child_table INTO v_child_table FROM @extschema@.custom_time_partitions WHERE partition_range @> to_timestamp(NEW.%I) AND parent_table = %L;' , v_control , v_parent_schema||'.'||v_parent_tablename); END IF; v_trig_func := v_trig_func || ' SELECT schemaname, tablename INTO v_child_schemaname, v_child_tablename FROM pg_catalog.pg_tables WHERE schemaname ||''.''|| tablename = v_child_table; IF v_child_schemaname IS NOT NULL AND v_child_tablename IS NOT NULL THEN EXECUTE format(''INSERT INTO %I.%I VALUES ($1.*)'', v_child_schemaname, v_child_tablename) USING NEW; ELSE RETURN NEW; END IF; RETURN NULL; END $t$;'; EXECUTE v_trig_func; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', format('Added function for custom time table: %s', p_parent_table)); END IF; ELSE RAISE EXCEPTION 'ERROR: Invalid time partitioning type given: %', v_type; END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', v_old_search_path, 'false'); END IF; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN CREATE FUNCTION: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''Partition function maintenance for table %s failed'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Apply constraints managed by partman extension */ CREATE OR REPLACE FUNCTION apply_constraints(p_parent_table text, p_child_table text DEFAULT NULL, p_analyze boolean DEFAULT FALSE, p_job_id bigint DEFAULT NULL, p_debug boolean DEFAULT FALSE) RETURNS void LANGUAGE plpgsql AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_child_table text; v_child_tablename text; v_col text; v_constraint_cols text[]; v_constraint_col_type text; v_constraint_name text; v_constraint_values record; v_control text; v_datetime_string text; v_epoch boolean; v_existing_constraint_name text; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_last_partition text; v_last_partition_id int; v_last_partition_timestamp timestamp; v_max_id bigint; v_max_timestamp timestamp; v_old_search_path text; v_optimize_constraint int; v_parent_schema text; v_parent_tablename text; v_partition_interval text; v_partition_suffix text; v_row_max record; v_sql text; v_step_id bigint; v_suffix_position int; v_type text; BEGIN SELECT partition_type , control , epoch , partition_interval , optimize_constraint , datetime_string , constraint_cols , jobmon INTO v_type , v_control , v_epoch , v_partition_interval , v_optimize_constraint , v_datetime_string , v_constraint_cols , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND constraint_cols IS NOT NULL; IF v_constraint_cols IS NULL THEN IF p_debug THEN RAISE NOTICE 'Given parent table (%) not set up for constraint management (constraint_cols is NULL)', p_parent_table; END IF; -- Returns silently to allow this function to be simply called by maintenance processes without having to check if config options are set. RETURN; END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', '@extschema@,'||v_jobmon_schema, 'false'); END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN IF p_job_id IS NULL THEN v_job_id := add_job(format('PARTMAN CREATE CONSTRAINT: %s', p_parent_table)); ELSE v_job_id = p_job_id; END IF; END IF; -- If p_child_table is null, figure out the partition that is the one right before the optimize_constraint value backwards. IF p_child_table IS NULL THEN -- 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 IN SELECT partition_schemaname, partition_tablename FROM @extschema@.show_partitions(p_parent_table, 'DESC') LOOP IF (v_type = 'time' OR v_type = 'time-custom') THEN IF v_epoch = false THEN EXECUTE format('SELECT max(%I) FROM %I.%I', v_control, v_row_max.partition_schemaname, v_row_max.partition_tablename) INTO v_max_timestamp; ELSE EXECUTE format('SELECT to_timestamp(max(%I)) FROM %I.%I', v_control, v_row_max.partition_schemaname, v_row_max.partition_tablename) INTO v_max_timestamp; END IF; IF v_max_timestamp IS NOT NULL THEN SELECT suffix_timestamp FROM @extschema@.show_partition_name(p_parent_table, v_max_timestamp::text) INTO v_last_partition_timestamp; v_partition_suffix := to_char(v_last_partition_timestamp - (v_partition_interval::interval * (v_optimize_constraint + 1) ), v_datetime_string); EXIT; END IF; ELSIF v_type = 'id' THEN EXECUTE format('SELECT max(%I) FROM %I.%I', v_control, v_row_max.partition_schemaname, v_row_max.partition_tablename) INTO v_max_id; IF v_max_id IS NOT NULL THEN SELECT suffix_id FROM @extschema@.show_partition_name(p_parent_table, v_max_id::text) INTO v_last_partition_id; v_last_partition_id := v_last_partition_id - (v_partition_interval::int * (v_optimize_constraint + 1) ); IF v_last_partition_id < 0 THEN v_last_partition_id := 0; END IF; v_partition_suffix := v_last_partition_id::text; EXIT; END IF; ELSE RAISE EXCEPTION 'Unknown type encountered in apply_constraints()'; END IF; IF p_debug THEN RAISE NOTICE 'apply_constraint: p_parent_table: %, partition_schemaname: %, partition_tablename: %', p_parent_table , v_row_max.partition_schemaname, v_row_max.partition_tablename; END IF; END LOOP; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Applying additional constraints: Automatically determining most recent child on which to apply constraints'); END IF; IF p_debug THEN RAISE NOTICE 'apply_constraint: v_parent_tablename: % , v_partition_suffix: %', v_parent_tablename, v_partition_suffix; END IF; IF v_partition_suffix IS NULL THEN IF p_debug THEN RAISE NOTICE 'No values for control column found in any child table. Unable to automatically determine which child table to apply additional constraints to'; END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'WARNING', 'No values for control column found in any child table. Unable to automatically determine which child table to apply additional constraints to'); IF p_job_id IS NULL THEN -- If not part of a sub-job, fail this one with a warning PERFORM fail_job(v_job_id, 2); END IF; END IF; -- Return cleanly so that if maintenance calls under this condition it produces no errors that will interfere with other partition sets. RETURN; END IF; v_child_table := v_parent_schema ||'.'|| @extschema@.check_name_length(v_parent_tablename, v_partition_suffix, TRUE); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', format('Target child table: %s.%s', v_parent_schema, v_child_table)); END IF; ELSE v_child_table := p_child_table; 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; SELECT tablename INTO v_child_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_child_table; IF v_child_tablename IS NULL THEN IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'NOTICE', format('Target child table (%s) does not exist. Skipping constraint creation.', v_child_table)); IF p_job_id IS 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'); END IF; IF p_debug THEN RAISE NOTICE 'Target child table (%) does not exist. Skipping constraint creation.', v_child_table; END IF; RETURN; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; FOREACH v_col IN ARRAY v_constraint_cols LOOP SELECT con.conname INTO v_existing_constraint_name 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 WHERE c.relname = v_child_tablename AND n.nspname = v_parent_schema AND con.conname LIKE 'partmanconstr_%' AND con.contype = 'c' AND a.attname = v_col AND ARRAY[a.attnum] <@ con.conkey AND a.attisdropped = false; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, format('Applying additional constraints: Applying new constraint on column: %s', v_col)); END IF; IF v_existing_constraint_name IS NOT NULL THEN IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'NOTICE', format('Partman managed constraint already exists on this table (%s) and column (%s). Skipping creation.', v_child_table, v_col)); END IF; IF p_debug THEN RAISE NOTICE 'Partman managed constraint already exists on this table (%) and column (%). Skipping creation.', v_child_table, v_col ; END IF; CONTINUE; END IF; -- 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; IF v_constraint_values IS NOT NULL THEN v_sql := format('ALTER TABLE %I.%I ADD CONSTRAINT %I CHECK (%I >= %L AND %I <= %L)' , v_parent_schema , v_child_tablename , v_constraint_name , v_col , v_constraint_values.min , v_col , v_constraint_values.max); IF p_debug THEN RAISE NOTICE 'Constraint creation query: %', v_sql; END IF; EXECUTE v_sql; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', format('New constraint created: %s', v_sql)); END IF; ELSE IF p_debug THEN RAISE NOTICE 'Given column (%) contains all NULLs. No constraint created', v_col; END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'NOTICE', format('Given column (%s) contains all NULLs. No constraint created', v_col)); END IF; END IF; END LOOP; IF p_analyze THEN IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, format('Applying additional constraints: Running analyze on partition set: %s', p_parent_table)); END IF; IF p_debug THEN RAISE NOTICE 'Running analyze on partition set: %', 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 PERFORM close_job(v_job_id); EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', v_old_search_path, 'false'); END IF; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN CREATE CONSTRAINT: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Function to turn a table into the parent of a partition set */ CREATE OR REPLACE FUNCTION create_parent( p_parent_table text , p_control text , p_type text , p_interval text , p_constraint_cols text[] DEFAULT NULL , p_premake int DEFAULT 4 , p_use_run_maintenance boolean DEFAULT NULL , p_start_partition text DEFAULT NULL , p_inherit_fk boolean DEFAULT true , p_epoch boolean DEFAULT false , p_jobmon boolean DEFAULT true , p_debug boolean DEFAULT false) RETURNS boolean LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_base_timestamp timestamp; v_count int := 1; v_datetime_string text; v_higher_parent text := p_parent_table; v_id_interval bigint; v_id_position int; v_job_id bigint; v_jobmon_schema text; v_last_partition_created boolean; v_max bigint; v_notnull boolean; v_old_search_path text; v_parent_partition_id bigint; v_parent_partition_timestamp timestamp; v_parent_schema text; v_parent_tablename text; v_partition_time timestamp; v_partition_time_array timestamp[]; v_partition_id_array bigint[]; v_row record; v_run_maint boolean; v_sql text; v_start_time timestamp; v_starting_partition_id bigint; v_step_id bigint; v_step_overflow_id bigint; v_sub_parent text; v_success boolean := false; v_time_interval interval; v_time_position int; v_top_datetime_string text; v_top_parent text := p_parent_table; v_top_schemaname text; v_top_tablename text; BEGIN IF position('.' in p_parent_table) = 0 THEN RAISE EXCEPTION 'Parent table must be schema qualified'; END IF; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname || '.' || tablename = p_parent_table; IF v_parent_tablename IS NULL THEN RAISE EXCEPTION 'Please create given parent table first: %', p_parent_table; END IF; 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 AND n.nspname = v_parent_schema AND a.attname = p_control; 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; IF p_type = 'id' AND p_epoch = true THEN RAISE EXCEPTION 'p_epoch can only be used with time-based partitioning'; END IF; IF NOT @extschema@.check_partition_type(p_type) THEN RAISE EXCEPTION '% is not a valid partitioning type', p_type; END IF; EXECUTE format('LOCK TABLE %I.%I IN ACCESS EXCLUSIVE MODE', v_parent_schema, v_parent_tablename); IF p_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', '@extschema@,'||v_jobmon_schema, 'false'); END IF; END IF; IF p_use_run_maintenance IS NOT NULL THEN IF p_use_run_maintenance IS FALSE AND (p_type = 'time' OR p_type = 'time-custom') THEN RAISE EXCEPTION 'p_run_maintenance cannot be set to false for time based partitioning'; END IF; v_run_maint := p_use_run_maintenance; ELSIF p_type = 'time' OR p_type = 'time-custom' THEN v_run_maint := TRUE; ELSIF p_type = 'id' THEN v_run_maint := FALSE; ELSE RAISE EXCEPTION 'use_run_maintenance value cannot be set NULL'; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job(format('PARTMAN SETUP PARENT: %s', p_parent_table)); v_step_id := add_step(v_job_id, format('Creating initial partitions on new parent table: %s', p_parent_table)); 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 WITH parent_table AS ( SELECT h.inhparent AS parent_oid 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 c.relname = v_parent_tablename AND n.nspname = v_parent_schema ), sibling_children AS ( SELECT i.inhrelid::regclass::text AS tablename FROM pg_inherits i JOIN parent_table p ON i.inhparent = p.parent_oid ) 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_use_run_maintenance , sub_epoch , sub_optimize_trigger , sub_optimize_constraint , sub_jobmon FROM @extschema@.part_config_sub a JOIN sibling_children b on a.sub_parent = b.tablename LIMIT 1 LOOP INSERT INTO @extschema@.part_config_sub ( sub_parent , 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_use_run_maintenance , sub_epoch , sub_optimize_trigger , sub_optimize_constraint , sub_jobmon) VALUES ( p_parent_table , v_row.sub_partition_type , v_row.sub_control , 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_use_run_maintenance , v_row.sub_epoch , v_row.sub_optimize_trigger , v_row.sub_optimize_constraint , v_row.sub_jobmon); END LOOP; IF p_type = 'time' OR p_type = 'time-custom' 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 <> 'time-custom' THEN RAISE EXCEPTION 'Must use a predefined time interval if not using type "time-custom". See documentation.'; 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; -- First partition is either the min premake or p_start_partition v_start_time := COALESCE(p_start_partition::timestamp, CURRENT_TIMESTAMP - (v_time_interval * p_premake)); 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 v_datetime_string := 'YYYY'; 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 v_partition_time_array := array_append(v_partition_time_array, v_base_timestamp); LOOP -- If current loop value is less than or equal to the value of the max premake, add time to array. IF (v_base_timestamp + (v_time_interval * v_count)) < (CURRENT_TIMESTAMP + (v_time_interval * p_premake)) THEN BEGIN v_partition_time := (v_base_timestamp + (v_time_interval * v_count))::timestamp; 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. 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'); CONTINUE; END; ELSE EXIT; -- all needed partitions added to array. Exit the loop. END IF; v_count := v_count + 1; END LOOP; INSERT INTO @extschema@.part_config ( parent_table , partition_type , partition_interval , epoch , control , premake , constraint_cols , datetime_string , use_run_maintenance , inherit_fk , jobmon) VALUES ( p_parent_table , p_type , v_time_interval , p_epoch , p_control , p_premake , p_constraint_cols , v_datetime_string , v_run_maint , p_inherit_fk , p_jobmon); v_last_partition_created := @extschema@.create_partition_time(p_parent_table, v_partition_time_array, false); 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 ( SELECT i.inhparent AS top_parent_oid FROM pg_catalog.pg_inherits i JOIN pg_catalog.pg_class c ON c.oid = i.inhrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE c.relname = v_parent_tablename AND n.nspname = v_parent_schema ) SELECT n.nspname||'.'||c.relname, p.datetime_string INTO v_top_parent, v_top_datetime_string 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 JOIN @extschema@.part_config p ON p.parent_table = n.nspname||'.'||c.relname; IF v_top_parent IS NOT NULL THEN -- If so create the lowest possible partition that is within the boundary of the parent v_time_position := (length(p_parent_table) - position('p_' in reverse(p_parent_table))) + 2; v_parent_partition_timestamp := to_timestamp(substring(p_parent_table from v_time_position), v_top_datetime_string); IF v_base_timestamp >= v_parent_partition_timestamp THEN WHILE v_base_timestamp >= v_parent_partition_timestamp LOOP v_base_timestamp := v_base_timestamp - v_time_interval; END LOOP; v_base_timestamp := v_base_timestamp + v_time_interval; -- add one back since while loop set it one lower than is needed ELSIF v_base_timestamp < v_parent_partition_timestamp THEN WHILE v_base_timestamp < v_parent_partition_timestamp LOOP v_base_timestamp := v_base_timestamp + v_time_interval; END LOOP; -- Don't need to remove one since new starting time will fit in top parent interval 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); ELSE -- Currently unknown edge case if code gets here RAISE EXCEPTION 'No child tables created. Unexpected edge case encountered. Please report this error to author with conditions that led to it.'; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', format('Time partitions premade: %s', p_premake)); END IF; END IF; IF p_type = 'id' THEN v_id_interval := p_interval::bigint; IF v_id_interval < 10 THEN RAISE EXCEPTION 'Interval for serial partitioning must be greater than or equal to 10'; END IF; -- Check if parent table is a subpartition of an already existing id partition set managed by pg_partman. WHILE v_higher_parent IS NOT NULL LOOP -- initially set in DECLARE WITH top_oid AS ( SELECT i.inhparent AS top_parent_oid FROM pg_catalog.pg_inherits i JOIN pg_catalog.pg_class c ON c.oid = i.inhrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname||'.'||c.relname = v_higher_parent ) SELECT n.nspname||'.'||c.relname INTO v_higher_parent 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 JOIN @extschema@.part_config p ON p.parent_table = n.nspname||'.'||c.relname WHERE p.partition_type = 'id'; IF v_higher_parent IS NOT NULL THEN -- v_top_parent initially set in DECLARE v_top_parent := v_higher_parent; END IF; END LOOP; -- If custom start partition is set, use that. -- If custom start is not set and there is already data, start partitioning with the highest current value and ensure it's grabbed from highest top parent table SELECT schemaname, tablename INTO v_top_schemaname, v_top_tablename FROM pg_catalog.pg_tables WHERE schemaname||'.'||tablename = v_top_parent; v_sql := format('SELECT COALESCE(%L, max(%I)::bigint, 0) FROM %I.%I LIMIT 1' , p_start_partition::bigint , p_control , v_top_schemaname , v_top_tablename); EXECUTE v_sql INTO v_max; 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 THEN v_partition_id_array = array_append(v_partition_id_array, (v_starting_partition_id - v_id_interval*i)); END IF; v_partition_id_array = array_append(v_partition_id_array, (v_id_interval*i) + v_starting_partition_id); END LOOP; INSERT INTO @extschema@.part_config ( parent_table , partition_type , partition_interval , control , premake , constraint_cols , use_run_maintenance , inherit_fk , jobmon) VALUES ( p_parent_table , p_type , v_id_interval , p_control , p_premake , p_constraint_cols , v_run_maint , p_inherit_fk , p_jobmon); v_last_partition_created := @extschema@.create_partition_id(p_parent_table, v_partition_id_array, false); 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 -- See if it's actually a subpartition of a parent id partition WITH top_oid AS ( SELECT i.inhparent AS top_parent_oid FROM pg_catalog.pg_inherits i JOIN pg_catalog.pg_class c ON c.oid = i.inhrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE c.relname = v_parent_tablename AND n.nspname = v_parent_schema ) SELECT n.nspname||'.'||c.relname INTO v_top_parent 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 JOIN @extschema@.part_config p ON p.parent_table = n.nspname||'.'||c.relname WHERE p.partition_type = 'id'; IF v_top_parent IS NOT NULL THEN -- Create the lowest possible partition that is within the boundary of the parent v_id_position := (length(p_parent_table) - position('p_' in reverse(p_parent_table))) + 2; v_parent_partition_id = substring(p_parent_table from v_id_position)::bigint; IF v_starting_partition_id >= v_parent_partition_id THEN WHILE v_starting_partition_id >= v_parent_partition_id LOOP v_starting_partition_id := v_starting_partition_id - v_id_interval; END LOOP; v_starting_partition_id := v_starting_partition_id + v_id_interval; -- add one back since while loop set it one lower than is needed ELSIF v_starting_partition_id < v_parent_partition_id THEN WHILE v_starting_partition_id < v_parent_partition_id LOOP v_starting_partition_id := v_starting_partition_id + v_id_interval; END LOOP; -- Don't need to remove one since new starting id will fit in top parent interval 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); ELSE -- Currently unknown edge case if code gets here RAISE EXCEPTION 'No child tables created. Unexpected edge case encountered. Please report this error to author with conditions that led to it.'; END IF; END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Creating partition function'); END IF; IF p_type = 'time' OR p_type = 'time-custom' 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 p_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); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); IF v_step_overflow_id IS NOT NULL THEN PERFORM fail_job(v_job_id); ELSE PERFORM close_job(v_job_id); END IF; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', v_old_search_path, 'false'); END IF; v_success := true; RETURN v_success; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN CREATE PARENT: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''Partition creation for table '||p_parent_table||' failed'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Create a partition set that is a subpartition of an already existing partition set. * Given the parent table of any current partition set, it will turn all existing children into parent tables of their own partition sets * using the configuration options given as parameters to this function. * Uses another config table that allows for turning all future child partitions into a new parent automatically. * To avoid logical complications and contention issues, ALL subpartitions must be maintained using run_maintenance(). * This means the automatic, trigger based partition creation for serial partitioning will not work if it is a subpartition. */ CREATE FUNCTION create_sub_parent( p_top_parent text , p_control text , p_type text , p_interval text , 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 boolean DEFAULT false , p_jobmon boolean DEFAULT true , p_debug boolean DEFAULT false) RETURNS boolean LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_last_partition text; v_row record; v_row_last_part record; v_run_maint boolean; v_sql text; v_success boolean := false; v_top_type text; BEGIN SELECT use_run_maintenance INTO v_run_maint FROM @extschema@.part_config WHERE parent_table = p_top_parent; IF v_run_maint 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; ELSIF v_run_maint = false THEN RAISE EXCEPTION 'Any parent table that will be part of a sub-partitioned set (on any level) must have use_run_maintenance set to true in part_config table, even for serial partitioning. See documentation for more info.'; END IF; FOR v_row IN -- Loop through all current children to turn them into partitioned tables SELECT partition_schemaname||'.'||partition_tablename AS child_table FROM @extschema@.show_partitions(p_top_parent) LOOP -- Just call existing create_parent() function but add the given parameters to the part_config_sub table as well v_sql := format('SELECT @extschema@.create_parent( p_parent_table := %L , p_control := %L , p_type := %L , p_interval := %L , p_constraint_cols := %L , p_premake := %L , p_use_run_maintenance := %L , p_start_partition := %L , p_inherit_fk := %L , p_epoch := %L , p_jobmon := %L , p_debug := %L )' , v_row.child_table , p_control , p_type , p_interval , p_constraint_cols , p_premake , true , p_start_partition , p_inherit_fk , p_epoch , p_jobmon , p_debug); EXECUTE v_sql; END LOOP; INSERT INTO @extschema@.part_config_sub ( sub_parent , sub_control , sub_partition_type , sub_partition_interval , sub_constraint_cols , sub_premake , sub_inherit_fk , sub_use_run_maintenance , sub_epoch , sub_jobmon) VALUES ( p_top_parent , p_control , p_type , p_interval , p_constraint_cols , p_premake , p_inherit_fk , true , p_epoch , p_jobmon); v_success := true; RETURN v_success; END $$; /* * Function to create a child table in a time-based partition set */ CREATE OR REPLACE FUNCTION create_partition_time (p_parent_table text, p_partition_times timestamp[], p_analyze boolean DEFAULT true) RETURNS boolean LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE 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_datetime_string text; v_exists text; v_epoch boolean; v_grantees text[]; v_hasoids boolean; v_inherit_fk boolean; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_parent_grant record; v_parent_owner text; v_parent_schema text; v_parent_tablename text; v_partition_created boolean := false; v_partition_name text; v_partition_suffix text; v_parent_tablespace text; v_partition_interval interval; v_partition_timestamp_end timestamp; v_partition_timestamp_start timestamp; v_quarter text; v_revoke text; v_row record; v_sql text; v_step_id bigint; v_step_overflow_id bigint; v_sub_timestamp_max timestamp; v_sub_timestamp_min timestamp; v_trunc_value text; v_time timestamp; v_type text; v_unlogged char; v_year text; BEGIN SELECT partition_type , control , partition_interval , epoch , inherit_fk , jobmon , datetime_string INTO v_type , v_control , v_partition_interval , v_epoch , v_inherit_fk , v_jobmon , v_datetime_string FROM @extschema@.part_config WHERE parent_table = p_parent_table AND partition_type = 'time' OR partition_type = 'time-custom'; IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', '@extschema@,'||v_jobmon_schema, 'false'); END IF; END IF; -- Determine if this table is a child of a subpartition parent. If so, get limits of what child tables can be created based on parent suffix SELECT sub_min::timestamp, sub_max::timestamp INTO v_sub_timestamp_min, v_sub_timestamp_max FROM @extschema@.check_subpartition_limits(p_parent_table, 'time'); SELECT tableowner, schemaname, tablename, tablespace INTO v_parent_owner, v_parent_schema, v_parent_tablename, v_parent_tablespace FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job(format('PARTMAN CREATE TABLE: %s', p_parent_table)); END IF; 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. 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'); CONTINUE; END; -- 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 CONTINUE; END IF; END IF; -- 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); SELECT tablename INTO v_exists FROM pg_catalog.pg_tables WHERE schemaname = v_parent_schema AND tablename = v_partition_name; IF v_exists IS NOT NULL 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 , v_partition_name , v_partition_timestamp_start , v_partition_timestamp_end-'1sec'::interval)); END IF; 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 AND n.nspname = v_parent_schema; v_sql := 'CREATE'; IF v_unlogged = 'u' THEN v_sql := v_sql || ' UNLOGGED'; END IF; v_sql := v_sql || format(' TABLE %I.%I (LIKE %I.%I INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING STORAGE INCLUDING COMMENTS)' , v_parent_schema , v_partition_name , v_parent_schema , v_parent_tablename); 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 AND n.nspname = v_parent_schema; IF v_hasoids IS TRUE THEN v_sql := v_sql || ' WITH (OIDS)'; END IF; EXECUTE v_sql; 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; IF v_epoch = false 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_check' , v_control , v_partition_timestamp_start , v_control , v_partition_timestamp_end); ELSE EXECUTE format('ALTER TABLE %I.%I ADD CONSTRAINT %I CHECK (to_timestamp(%I) >= %L AND to_timestamp(%I) < %L)' , v_parent_schema , v_partition_name , v_partition_name||'_partition_check' , v_control , v_partition_timestamp_start , v_control , v_partition_timestamp_end); 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_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; FOR v_parent_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = p_parent_table GROUP BY grantee LOOP EXECUTE format('GRANT %s ON %I.%I TO %I' , array_to_string(v_parent_grant.types, ',') , v_parent_schema , v_partition_name , v_parent_grant.grantee); SELECT string_agg(r, ',') INTO v_revoke FROM (SELECT unnest(v_all) AS r EXCEPT SELECT unnest(v_parent_grant.types)) x; IF v_revoke IS NOT NULL THEN EXECUTE format('REVOKE %s ON %I.%I FROM %I CASCADE' , v_revoke , v_parent_schema , v_partition_name , v_parent_grant.grantee); END IF; v_grantees := array_append(v_grantees, v_parent_grant.grantee::text); END LOOP; -- Revoke all privileges from roles that have none on the parent IF v_grantees IS NOT NULL THEN FOR v_row IN SELECT role FROM ( SELECT DISTINCT grantee::text AS role FROM information_schema.table_privileges WHERE table_schema = v_parent_schema AND table_name = v_partition_name EXCEPT SELECT unnest(v_grantees)) x LOOP IF v_row.role IS NOT NULL THEN EXECUTE format('REVOKE ALL ON %I.%I FROM %I' , v_parent_schema , v_partition_name , v_row.role); END IF; END LOOP; END IF; EXECUTE format('ALTER TABLE %I.%I OWNER TO %I', v_parent_schema, v_partition_name, v_parent_owner); IF v_inherit_fk THEN PERFORM @extschema@.apply_foreign_keys(p_parent_table, v_parent_schema||'.'||v_partition_name, v_job_id); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); 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 FOR v_row IN SELECT sub_parent , sub_control , sub_partition_type , 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_use_run_maintenance , sub_epoch , sub_optimize_trigger , sub_optimize_constraint , sub_jobmon FROM @extschema@.part_config_sub WHERE sub_parent = p_parent_table LOOP IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, format('Subpartitioning %s.%s', v_parent_schema, v_partition_name)); END IF; v_sql := format('SELECT @extschema@.create_parent( p_parent_table := %L , p_control := %L , p_type := %L , p_interval := %L , p_constraint_cols := %L , p_premake := %L , p_use_run_maintenance := %L , p_inherit_fk := %L , p_epoch := %L , p_jobmon := %L )' , v_parent_schema||'.'||v_partition_name , v_row.sub_control , v_row.sub_partition_type , v_row.sub_partition_interval , v_row.sub_constraint_cols , v_row.sub_premake , v_row.sub_use_run_maintenance , v_row.sub_inherit_fk , v_row.sub_epoch , v_row.sub_jobmon); EXECUTE v_sql; 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 WHERE parent_table = v_parent_schema||'.'||v_partition_name; END LOOP; -- end sub partitioning LOOP 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 v_step_id := add_step(v_job_id, format('No partitions created for partition set: %s. Attempted intervals: %s', p_parent_table, p_partition_times)); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; IF v_step_overflow_id IS NOT NULL THEN PERFORM fail_job(v_job_id); ELSE PERFORM close_job(v_job_id); END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', v_old_search_path, 'false'); END IF; RETURN v_partition_created; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN CREATE TABLE: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Function to create id partitions */ CREATE OR REPLACE FUNCTION create_partition_id(p_parent_table text, p_partition_ids bigint[], p_analyze boolean DEFAULT true) RETURNS boolean LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE 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_exists text; v_grantees text[]; v_hasoids boolean; v_id bigint; v_inherit_fk boolean; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_old_search_path text; v_parent_grant record; v_parent_owner text; 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_revoke text; v_row record; v_sql text; v_step_id bigint; v_sub_id_max bigint; v_sub_id_min bigint; v_unlogged char; BEGIN SELECT control , partition_interval , inherit_fk , jobmon INTO v_control , v_partition_interval , v_inherit_fk , v_jobmon FROM @extschema@.part_config WHERE parent_table = p_parent_table AND partition_type = 'id'; IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', '@extschema@,'||v_jobmon_schema, 'false'); END IF; END IF; -- Determine if this table is a child of a subpartition parent. If so, get limits of what child tables can be created based on parent suffix SELECT sub_min::bigint, sub_max::bigint INTO v_sub_id_min, v_sub_id_max FROM @extschema@.check_subpartition_limits(p_parent_table, 'id'); SELECT tableowner, schemaname, tablename, tablespace INTO v_parent_owner, v_parent_schema, v_parent_tablename, v_parent_tablespace FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job(format('PARTMAN CREATE TABLE: %s', p_parent_table)); 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. 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; END IF; END IF; v_partition_name := @extschema@.check_name_length(v_parent_tablename, v_id::text, TRUE); -- If child table already exists, skip creation SELECT tablename INTO v_exists FROM pg_catalog.pg_tables WHERE schemaname = v_parent_schema AND tablename = v_partition_name; IF v_exists IS NOT NULL 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, 'Creating new partition '||v_partition_name||' with interval from '||v_id||' to '||(v_id + v_partition_interval)-1); END IF; 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 AND n.nspname = v_parent_schema; v_sql := 'CREATE'; IF v_unlogged = 'u' THEN v_sql := v_sql || ' UNLOGGED'; END IF; v_sql := v_sql || format(' TABLE %I.%I (LIKE %I.%I INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING STORAGE INCLUDING COMMENTS)' , v_parent_schema , v_partition_name , v_parent_schema , v_parent_tablename); 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 AND n.nspname = v_parent_schema; IF v_hasoids IS TRUE THEN v_sql := v_sql || ' WITH (OIDS)'; END IF; EXECUTE v_sql; 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); EXECUTE format('ALTER TABLE %I.%I INHERIT %I.%I', v_parent_schema, v_partition_name, v_parent_schema, v_parent_tablename); FOR v_parent_grant IN SELECT array_agg(DISTINCT privilege_type::text ORDER BY privilege_type::text) AS types, grantee FROM information_schema.table_privileges WHERE table_schema ||'.'|| table_name = p_parent_table GROUP BY grantee LOOP EXECUTE format('GRANT %s ON %I.%I TO %I' , array_to_string(v_parent_grant.types, ',') , v_parent_schema , v_partition_name , v_parent_grant.grantee); SELECT string_agg(r, ',') INTO v_revoke FROM (SELECT unnest(v_all) AS r EXCEPT SELECT unnest(v_parent_grant.types)) x; IF v_revoke IS NOT NULL THEN EXECUTE format('REVOKE %s ON %I.%I FROM %I CASCADE' , v_revoke , v_parent_schema , v_partition_name , v_parent_grant.grantee); END IF; v_grantees := array_append(v_grantees, v_parent_grant.grantee::text); END LOOP; -- Revoke all privileges from roles that have none on the parent IF v_grantees IS NOT NULL THEN FOR v_row IN SELECT role FROM ( SELECT DISTINCT grantee::text AS role FROM information_schema.table_privileges WHERE table_schema = v_parent_schema AND table_name = v_partition_name EXCEPT SELECT unnest(v_grantees)) x LOOP IF v_row.role IS NOT NULL THEN EXECUTE format('REVOKE ALL ON %I.%I FROM %I' , v_parent_schema , v_partition_name , v_row.role); END IF; END LOOP; END IF; EXECUTE format('ALTER TABLE %I.%I OWNER TO %I', v_parent_schema, v_partition_name, v_parent_owner); IF v_inherit_fk THEN PERFORM @extschema@.apply_foreign_keys(p_parent_table, v_parent_schema||'.'||v_partition_name, v_job_id); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); 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 FOR v_row IN SELECT sub_parent , sub_control , sub_partition_type , 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_use_run_maintenance , sub_epoch , sub_optimize_trigger , sub_optimize_constraint , sub_jobmon FROM @extschema@.part_config_sub WHERE sub_parent = p_parent_table LOOP IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Subpartitioning '||v_partition_name); END IF; v_sql := format('SELECT @extschema@.create_parent( p_parent_table := %L , p_control := %L , p_type := %L , p_interval := %L , p_constraint_cols := %L , p_premake := %L , p_use_run_maintenance := %L , p_inherit_fk := %L , p_epoch := %L , p_jobmon := %L )' , v_parent_schema||'.'||v_partition_name , v_row.sub_control , v_row.sub_partition_type , v_row.sub_partition_interval , v_row.sub_constraint_cols , v_row.sub_premake , v_row.sub_use_run_maintenance , v_row.sub_inherit_fk , v_row.sub_epoch , v_row.sub_jobmon); EXECUTE v_sql; 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 WHERE parent_table = v_parent_schema||'.'||v_partition_name; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END LOOP; -- end sub partitioning LOOP 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 v_step_id := add_step(v_job_id, format('No partitions created for partition set: %s', p_parent_table)); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; PERFORM close_job(v_job_id); END IF; IF v_jobmon_schema IS NOT NULL THEN EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', v_old_search_path, 'false'); END IF; RETURN v_partition_created; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN CREATE TABLE: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Function to manage pre-creation of the next partitions in a set. * 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_run_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. * Be aware that constraint exclusion may not work properly until an analyze on the partition set is run. */ CREATE FUNCTION run_maintenance(p_parent_table text DEFAULT NULL, p_analyze boolean DEFAULT true, p_jobmon boolean DEFAULT true, p_debug boolean DEFAULT false) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_adv_lock boolean; v_check_subpart int; v_create_count int := 0; v_current_partition text; v_current_partition_id bigint; v_current_partition_timestamp timestamp; v_datetime_string text; v_drop_count int := 0; v_id_position int; 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 timestamp; v_max_id_parent bigint; v_max_time_parent timestamp; v_next_partition_id bigint; v_next_partition_timestamp timestamp; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_premade_count int; v_premake_id_max bigint; v_premake_id_min bigint; v_premake_timestamp_min timestamp; v_premake_timestamp_max timestamp; v_quarter text; v_row record; v_row_max_id record; v_row_max_time record; v_row_sub record; v_skip_maint boolean; v_step_id bigint; v_step_overflow_id bigint; v_step_serial_id bigint; v_sub_id_max bigint; v_sub_id_max_suffix bigint; v_sub_id_min bigint; v_sub_parent text; v_sub_timestamp_max timestamp; v_sub_timestamp_max_suffix timestamp; v_sub_timestamp_min timestamp; v_tablename text; v_tables_list_sql text; v_time_position int; v_year text; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman run_maintenance')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'Partman maintenance already running.'; RETURN; END IF; IF p_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', '@extschema@,'||v_jobmon_schema, 'false'); END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job('PARTMAN RUN MAINTENANCE'); v_step_id := add_step(v_job_id, 'Running maintenance loop'); 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! -- Don't have to worry about this in the serial trigger maintenance since subpartitioning requires run_maintenance(). FOR v_row IN SELECT sub_parent FROM @extschema@.part_config_sub LOOP SELECT count(*) INTO v_check_subpart FROM @extschema@.check_subpart_sameconfig(v_row.sub_parent); 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, results are all children of the given parent. Update the differing values to be consistent for your desired values.', v_row.sub_parent; END IF; END LOOP; v_row := NULL; -- Ensure it's reset v_tables_list_sql := 'SELECT parent_table , partition_type , partition_interval , control , premake , datetime_string , undo_in_progress , sub_partition_set_full , epoch FROM @extschema@.part_config WHERE sub_partition_set_full = false'; IF p_parent_table IS NULL THEN v_tables_list_sql := v_tables_list_sql || ' AND use_run_maintenance = true'; ELSE v_tables_list_sql := v_tables_list_sql || format(' AND parent_table = %L', p_parent_table); END IF; FOR v_row IN EXECUTE v_tables_list_sql LOOP CONTINUE WHEN v_row.undo_in_progress; v_skip_maint := true; -- reset every loop SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_row.parent_table; SELECT partition_tablename INTO v_last_partition FROM @extschema@.show_partitions(v_row.parent_table, 'DESC') LIMIT 1; IF p_debug THEN RAISE NOTICE 'run_maint: parent_table: %, v_last_partition: %', v_row.parent_table, v_last_partition; END IF; IF v_row.partition_type = 'time' OR v_row.partition_type = 'time-custom' THEN v_time_position := (length(v_last_partition) - position('p_' in reverse(v_last_partition))) + 2; IF v_row.partition_interval::interval <> '3 months' OR (v_row.partition_interval::interval = '3 months' AND v_row.partition_type = 'time-custom') THEN v_last_partition_timestamp := to_timestamp(substring(v_last_partition from v_time_position), v_row.datetime_string); ELSE -- to_timestamp doesn't recognize 'Q' date string formater. Handle it v_year := split_part(substring(v_last_partition FROM v_time_position), 'q', 1); v_quarter := split_part(substring(v_last_partition FROM v_time_position), 'q', 2); CASE WHEN v_quarter = '1' THEN v_last_partition_timestamp := to_timestamp(v_year || '-01-01', 'YYYY-MM-DD'); WHEN v_quarter = '2' THEN v_last_partition_timestamp := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); WHEN v_quarter = '3' THEN v_last_partition_timestamp := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); WHEN v_quarter = '4' THEN v_last_partition_timestamp := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); END CASE; 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_time IN SELECT partition_schemaname, partition_tablename FROM @extschema@.show_partitions(v_row.parent_table, 'DESC') LOOP IF v_row.epoch = false THEN EXECUTE format('SELECT max(%I)::text FROM %I.%I' , v_row.control , v_row_max_time.partition_schemaname , v_row_max_time.partition_tablename ) INTO v_current_partition_timestamp; ELSE EXECUTE format('SELECT to_timestamp(max(%I))::text FROM %I.%I' , v_row.control , v_row_max_time.partition_schemaname , v_row_max_time.partition_tablename ) INTO v_current_partition_timestamp; END IF; IF v_current_partition_timestamp IS NOT NULL THEN SELECT suffix_timestamp INTO v_current_partition_timestamp FROM show_partition_name(v_row.parent_table, v_current_partition_timestamp::text); EXIT; END IF; END LOOP; -- Check for values in the parent 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 IF v_row.epoch = false THEN EXECUTE format('SELECT max(%I) FROM ONLY %I.%I', v_row.control, v_parent_schema, v_parent_tablename) INTO v_max_time_parent; ELSE EXECUTE format('SELECT to_timestamp(max(%I)) FROM ONLY %I.%I', v_row.control, v_parent_schema, v_parent_tablename) INTO v_max_time_parent; END IF; IF p_debug THEN RAISE NOTICE 'run_maint: v_current_partition_timestamp: %, v_max_time_parent: %', v_current_partition_timestamp, v_max_time_parent; END IF; IF v_max_time_parent > v_current_partition_timestamp THEN SELECT suffix_timestamp INTO v_current_partition_timestamp FROM show_partition_name(v_row.parent_table, v_max_time_parent::text); END IF; IF v_current_partition_timestamp IS NULL THEN -- Partition set is completely empty. Nothing to do CONTINUE; END IF; -- If this is a subpartition, determine if the last child table has been made. If so, mark it as full so future maintenance runs can skip it SELECT sub_min::timestamp, sub_max::timestamp INTO v_sub_timestamp_min, v_sub_timestamp_max FROM @extschema@.check_subpartition_limits(v_row.parent_table, 'time'); IF v_sub_timestamp_max IS NOT NULL THEN SELECT suffix_timestamp INTO v_sub_timestamp_max_suffix FROM @extschema@.show_partition_name(v_row.parent_table, v_sub_timestamp_max::text); IF v_sub_timestamp_max_suffix = v_last_partition_timestamp THEN -- Final partition for this set is created. Set full and skip it UPDATE @extschema@.part_config SET sub_partition_set_full = true WHERE parent_table = v_row.parent_table; CONTINUE; END IF; END IF; -- Check and see how many premade partitions there are. 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; IF p_debug THEN RAISE NOTICE 'run_maint before loop: current_partition_timestamp: %, v_premade_count: %, v_sub_timestamp_min: %, v_sub_timestamp_max: %' , v_current_partition_timestamp , v_premade_count , v_sub_timestamp_min , v_sub_timestamp_max; END IF; -- 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 IF p_debug THEN RAISE NOTICE 'run_maint: parent_table: %, v_premade_count: %, v_next_partition_timestamp: %', v_row.parent_table, v_premade_count, v_next_partition_timestamp; END IF; IF v_next_partition_timestamp < v_sub_timestamp_min OR v_next_partition_timestamp > v_sub_timestamp_max THEN -- With subpartitioning, no need to run if the timestamp is not in the parent table's range EXIT; END IF; 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 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', 'Child partition creation skippd for parent table '||v_partition_time); END IF; RAISE WARNING 'Attempted partition time interval is outside PostgreSQL''s supported time range. Child partition creation skipped for parent table %', v_row.parent_table; CONTINUE; END; v_last_partition_created := @extschema@.create_partition_time(v_row.parent_table, ARRAY[v_next_partition_timestamp], p_analyze); IF v_last_partition_created THEN v_create_count := v_create_count + 1; PERFORM @extschema@.create_function_time(v_row.parent_table, v_job_id); 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)); END LOOP; ELSIF v_row.partition_type = 'id' THEN -- 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') LOOP EXECUTE format('SELECT max(%I)::text FROM %I.%I' , v_row.control , v_row_max_id.partition_schemaname , v_row_max_id.partition_tablename) INTO v_current_partition_id; IF v_current_partition_id IS NOT NULL THEN SELECT suffix_id INTO v_current_partition_id FROM show_partition_name(v_row.parent_table, v_current_partition_id::text); EXIT; END IF; END LOOP; -- Check for values in the parent 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_parent_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 show_partition_name(v_row.parent_table, v_max_id_parent::text); END IF; IF v_current_partition_id IS NULL THEN -- Partition set is completely empty. Nothing to do CONTINUE; END IF; v_id_position := (length(v_last_partition) - position('p_' in reverse(v_last_partition))) + 2; v_last_partition_id = substring(v_last_partition from v_id_position)::bigint; -- Determine if this table is a child of a subpartition parent. If so, get limits to see if run_maintenance even needs to run for it. SELECT sub_min::bigint, sub_max::bigint INTO v_sub_id_min, v_sub_id_max FROM @extschema@.check_subpartition_limits(v_row.parent_table, 'id'); IF v_sub_id_max IS NOT NULL THEN SELECT suffix_id INTO v_sub_id_max_suffix FROM @extschema@.show_partition_name(v_row.parent_table, v_sub_id_max::text); IF v_sub_id_max_suffix = v_last_partition_id THEN -- Final partition for this set is created. Set full and skip it UPDATE @extschema@.part_config SET sub_partition_set_full = true WHERE parent_table = v_row.parent_table; CONTINUE; END IF; END IF; 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 IF p_debug THEN RAISE NOTICE 'run_maint: parent_table: %, v_premade_count: %, v_next_partition_id: %', v_row.parent_table, v_premade_count, v_next_partition_id; END IF; 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], p_analyze); IF v_last_partition_created THEN v_create_count := v_create_count + 1; PERFORM @extschema@.create_function_id(v_row.parent_table, v_job_id); 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 -- Manage additonal constraints if set PERFORM @extschema@.apply_constraints(p_parent_table := v_row.parent_table, p_job_id := v_job_id, p_debug := p_debug); END LOOP; -- end of creation loop -- Manage dropping old partitions if retention option is set FOR v_row IN SELECT parent_table FROM @extschema@.part_config WHERE retention IS NOT NULL AND undo_in_progress = false AND (partition_type = 'time' OR partition_type = 'time-custom') LOOP IF p_parent_table IS NULL THEN v_drop_count := v_drop_count + @extschema@.drop_partition_time(v_row.parent_table); ELSE -- Only run retention on table given in parameter IF p_parent_table <> v_row.parent_table THEN CONTINUE; ELSE v_drop_count := v_drop_count + @extschema@.drop_partition_time(v_row.parent_table); END IF; END IF; IF v_drop_count > 0 THEN PERFORM @extschema@.create_function_time(v_row.parent_table, v_job_id); END IF; END LOOP; FOR v_row IN SELECT parent_table FROM @extschema@.part_config WHERE retention IS NOT NULL AND undo_in_progress = false AND partition_type = 'id' LOOP IF p_parent_table IS NULL THEN v_drop_count := v_drop_count + @extschema@.drop_partition_id(v_row.parent_table); ELSE -- Only run retention on table given in parameter IF p_parent_table <> v_row.parent_table THEN CONTINUE; ELSE v_drop_count := v_drop_count + @extschema@.drop_partition_id(v_row.parent_table); END IF; END IF; IF v_drop_count > 0 THEN PERFORM @extschema@.create_function_id(v_row.parent_table, v_job_id); END IF; END LOOP; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', format('Partition maintenance finished. %s partitons made. %s partitions dropped.', v_create_count, v_drop_count)); IF v_step_overflow_id IS NOT NULL OR v_step_serial_id IS NOT NULL THEN PERFORM fail_job(v_job_id); ELSE PERFORM close_job(v_job_id); END IF; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', v_old_search_path, 'false'); END IF; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN RUN MAINTENANCE'')', v_jobmon_schema) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Populate the child table(s) of a time-based partition set with old data from the original parent */ CREATE OR REPLACE FUNCTION 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') RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE v_control text; v_datetime_string text; v_current_partition_name text; v_epoch boolean; v_last_partition text; v_lock_iter int := 1; v_lock_obtained boolean := FALSE; v_max_partition_timestamp timestamp; v_min_partition_timestamp timestamp; v_parent_schema text; v_parent_tablename text; v_partition_interval interval; v_partition_suffix text; v_partition_timestamp timestamp[]; v_quarter text; v_rowcount bigint; v_sql text; v_start_control timestamp; v_time_position int; v_total_rows bigint := 0; v_type text; v_year text; BEGIN SELECT partition_type , partition_interval::interval , control , datetime_string , epoch INTO v_type , v_partition_interval , v_control , v_datetime_string , v_epoch FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (partition_type = 'time' OR partition_type = 'time-custom'); IF NOT FOUND THEN RAISE EXCEPTION 'ERROR: no config found for %', p_parent_table; END IF; IF p_batch_interval IS NULL OR p_batch_interval > v_partition_interval THEN p_batch_interval := v_partition_interval; END IF; SELECT partition_tablename INTO v_last_partition FROM @extschema@.show_partitions(p_parent_table, 'DESC') LIMIT 1; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; FOR i IN 1..p_batch_count LOOP IF v_epoch = false THEN IF p_order = 'ASC' THEN EXECUTE format('SELECT min(%I) FROM ONLY %I.%I', v_control, v_parent_schema, v_parent_tablename) INTO v_start_control; ELSIF p_order = 'DESC' THEN EXECUTE format('SELECT max(%I) FROM ONLY %I.%I', v_control, v_parent_schema, v_parent_tablename) INTO v_start_control; ELSE RAISE EXCEPTION 'Invalid value for p_order. Must be ASC or DESC'; END IF; ELSE IF p_order = 'ASC' THEN EXECUTE format('SELECT to_timestamp(min(%I)) FROM ONLY %I.%I', v_control, v_parent_schema, v_parent_tablename) INTO v_start_control; ELSIF p_order = 'DESC' THEN EXECUTE format('SELECT to_timestamp(max(%I)) FROM ONLY %I.%I', v_control, v_parent_schema, v_parent_tablename) INTO v_start_control; ELSE RAISE EXCEPTION 'Invalid value for p_order. Must be ASC or DESC'; END IF; END IF; IF v_start_control IS NULL THEN EXIT; END IF; IF v_type = 'time' 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_type = 'time-custom' THEN -- Keep going backwards, checking if the time interval encompases the current v_start_control value v_time_position := (length(v_last_partition) - position('p_' in reverse(v_last_partition))) + 2; v_min_partition_timestamp := to_timestamp(substring(v_last_partition from v_time_position), v_datetime_string); 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 v_max_partition_timestamp := v_min_partition_timestamp; BEGIN v_min_partition_timestamp := v_min_partition_timestamp - v_partition_interval; 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_interval; END; END IF; END LOOP; END IF; v_partition_timestamp := ARRAY[v_min_partition_timestamp]; IF p_order = 'ASC' THEN IF (v_start_control + p_batch_interval) >= (v_min_partition_timestamp + v_partition_interval) THEN v_max_partition_timestamp := v_min_partition_timestamp + v_partition_interval; ELSE v_max_partition_timestamp := v_start_control + p_batch_interval; END IF; ELSIF p_order = 'DESC' THEN -- Must be greater than max value still in parent table since query below grabs < max v_max_partition_timestamp := v_min_partition_timestamp + v_partition_interval; -- Make sure minimum doesn't underflow current partition minimum IF (v_start_control - p_batch_interval) >= v_min_partition_timestamp THEN v_min_partition_timestamp = v_start_control - p_batch_interval; END IF; ELSE RAISE EXCEPTION 'Invalid value for p_order. Must be ASC or DESC'; END IF; -- do some locking with timeout, if required IF p_lock_wait > 0 THEN v_lock_iter := 0; WHILE v_lock_iter <= 5 LOOP v_lock_iter := v_lock_iter + 1; BEGIN IF v_epoch = false THEN v_sql := format('SELECT * FROM ONLY %I.%I WHERE %I >= %L AND %I < %L FOR UPDATE NOWAIT' , v_parent_schema , v_parent_tablename , v_control , v_min_partition_timestamp , v_control , v_max_partition_timestamp); ELSE v_sql := format('SELECT * FROM ONLY %I.%I WHERE to_timestamp(%I) >= %L AND to_timestamp(%I) < %L FOR UPDATE NOWAIT' , v_parent_schema , v_parent_tablename , v_control , v_min_partition_timestamp , v_control , v_max_partition_timestamp); END IF; EXECUTE v_sql; 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 RETURN -1; END IF; END IF; PERFORM @extschema@.create_partition_time(p_parent_table, v_partition_timestamp); -- This suffix generation code is in create_partition_time() as well v_partition_suffix := to_char(v_min_partition_timestamp, v_datetime_string); v_current_partition_name := @extschema@.check_name_length(v_parent_tablename, v_partition_suffix, TRUE); IF v_epoch = false THEN v_sql := format('WITH partition_data AS ( DELETE FROM ONLY %I.%I WHERE %I >= %L AND %I < %L RETURNING *) INSERT INTO %I.%I SELECT * FROM partition_data' , v_parent_schema , v_parent_tablename , v_control , v_min_partition_timestamp , v_control , v_max_partition_timestamp , v_parent_schema , v_current_partition_name); ELSE v_sql := format('WITH partition_data AS ( DELETE FROM ONLY %I.%I WHERE to_timestamp(%I) >= %L AND to_timestamp(%I) < %L RETURNING *) INSERT INTO %I.%I SELECT * FROM partition_data' , v_parent_schema , v_parent_tablename , v_control , v_min_partition_timestamp , v_control , v_max_partition_timestamp , v_parent_schema , v_current_partition_name); END IF; EXECUTE v_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total_rows := v_total_rows + v_rowcount; IF v_rowcount = 0 THEN EXIT; END IF; END LOOP; PERFORM @extschema@.create_function_time(p_parent_table); RETURN v_total_rows; END $$; /* * Function to undo time-based partitioning created by this extension */ CREATE OR REPLACE FUNCTION undo_partition_time(p_parent_table text, p_batch_count int DEFAULT 1, p_batch_interval interval DEFAULT NULL, p_keep_table boolean DEFAULT true, p_lock_wait numeric DEFAULT 0) RETURNS bigint LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_adv_lock boolean; v_batch_loop_count int := 0; v_child_min timestamptz; v_child_loop_total bigint := 0; v_child_table text; v_control text; v_epoch boolean; v_function_name text; v_inner_loop_count int; v_lock_iter int := 1; v_lock_obtained boolean := FALSE; v_job_id bigint; v_jobmon boolean; v_jobmon_schema text; v_move_sql text; v_old_search_path text; v_parent_schema text; v_parent_tablename text; v_partition_interval interval; v_row record; v_rowcount bigint; v_step_id bigint; v_sub_count int; v_total bigint := 0; v_trig_name text; v_type text; v_undo_count int := 0; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman undo_partition_time')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'undo_partition_time already running.'; RETURN 0; END IF; SELECT partition_type , partition_interval::interval , control , jobmon , epoch INTO v_type , v_partition_interval , v_control , v_jobmon , v_epoch FROM @extschema@.part_config WHERE parent_table = p_parent_table AND (partition_type = 'time' OR partition_type = 'time-custom'); IF v_partition_interval IS NULL THEN RAISE EXCEPTION 'Configuration for given parent table not found: %', p_parent_table; END IF; -- Check if any child tables are themselves partitioned or part of an inheritance tree. Prevent undo at this level if so. -- Need to either lock child tables at all levels or handle the proper removal of triggers on all child tables first -- before multi-level undo can be performed safely. FOR v_row IN SELECT partition_schemaname, partition_tablename FROM @extschema@.show_partitions(p_parent_table) LOOP SELECT count(*) INTO v_sub_count FROM pg_catalog.pg_inherits i JOIN pg_catalog.pg_class c ON i.inhparent = c.oid JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid WHERE c.relname = v_row.partition_tablename AND n.nspname = v_row.partition_schemaname; 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; END IF; END LOOP; IF v_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_namespace n, pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; IF v_jobmon_schema IS NOT NULL THEN SELECT current_setting('search_path') INTO v_old_search_path; EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', '@extschema@,'||v_jobmon_schema, 'false'); END IF; END IF; IF v_jobmon_schema IS NOT NULL THEN v_job_id := add_job(format('PARTMAN UNDO PARTITIONING: %s', p_parent_table)); v_step_id := add_step(v_job_id, format('Undoing partitioning for table %s', p_parent_table)); END IF; IF p_batch_interval IS NULL THEN p_batch_interval := v_partition_interval; END IF; -- Stops new time partitons 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; -- Stop data going into child tables. SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_tables WHERE schemaname ||'.'|| tablename = p_parent_table; 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); SELECT tgname INTO v_trig_name FROM pg_catalog.pg_trigger t WHERE tgname = v_trig_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'; RETURN -1; 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 EXECUTE format('DROP FUNCTION IF EXISTS %I.%I()', v_parent_schema, v_function_name); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', 'Stopped partition creation process. Removed trigger & trigger function'); END IF; <> WHILE v_batch_loop_count < p_batch_count LOOP -- Get ordered list of child table in set. Store in variable one at a time per loop until none are left. SELECT partition_tablename INTO v_child_table FROM @extschema@.show_partitions(p_parent_table, 'ASC'); EXIT WHEN v_child_table IS NULL; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, format('Removing child partition: %s.%s', v_parent_schema, v_child_table)); END IF; IF v_epoch = false THEN EXECUTE format('SELECT min(%I) FROM %I.%I', v_control, v_parent_schema, v_child_table) INTO v_child_min; ELSE EXECUTE format('SELECT to_timestamp(min(%I)) FROM %I.%I', v_control, v_parent_schema, v_child_table) INTO v_child_min; END IF; IF v_child_min IS NULL THEN -- No rows left in this child table. Remove from partition set. -- lockwait timeout for table 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_child_table); 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 child table for removal from partition set'; RETURN -1; END IF; END IF; -- END p_lock_wait IF v_lock_obtained := FALSE; -- reset for reuse later EXECUTE format('ALTER TABLE %I.%I NO INHERIT %I.%I' , v_parent_schema , v_child_table , v_parent_schema , v_parent_tablename); IF p_keep_table = false THEN EXECUTE format('DROP TABLE %I.%I', v_parent_schema, v_child_table); IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', format('Child table DROPPED. Moved %s rows to parent', v_child_loop_total)); END IF; ELSE IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', format('Child table UNINHERITED, not DROPPED. Moved %s rows to parent', v_child_loop_total)); END IF; END IF; IF v_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; CONTINUE outer_child_loop; END IF; v_inner_loop_count := 1; v_child_loop_total := 0; <> LOOP -- do some locking with timeout, if required 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('SELECT * FROM %I.%I WHERE %I <= %L FOR UPDATE NOWAIT' , v_parent_schema , v_child_table , v_control , v_child_min + (p_batch_interval * v_inner_loop_count)); 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 batch of rows to move'; RETURN -1; END IF; END IF; -- Get everything from the current child minimum up to the multiples of the given interval IF v_epoch = false THEN v_move_sql := format('WITH move_data AS ( DELETE FROM %I.%I WHERE %I <= %L RETURNING *) INSERT INTO %I.%I SELECT * FROM move_data' , v_parent_schema , v_child_table , v_control , v_child_min + (p_batch_interval * v_inner_loop_count) , v_parent_schema , v_parent_tablename); ELSE v_move_sql := format('WITH move_data AS ( DELETE FROM %I.%I WHERE to_timestamp(%I) <= %L RETURNING *) INSERT INTO %I.%I SELECT * FROM move_data' , v_parent_schema , v_child_table , v_control , v_child_min + (p_batch_interval * v_inner_loop_count) , v_parent_schema , v_parent_tablename); END IF; EXECUTE v_move_sql; GET DIAGNOSTICS v_rowcount = ROW_COUNT; v_total := v_total + v_rowcount; v_child_loop_total := v_child_loop_total + v_rowcount; IF v_jobmon_schema IS NOT NULL THEN PERFORM update_step(v_step_id, 'OK', format('Moved %s rows to parent.', v_child_loop_total)); END IF; EXIT inner_child_loop WHEN v_rowcount = 0; -- exit before loop incr if table is empty v_inner_loop_count := v_inner_loop_count + 1; v_batch_loop_count := v_batch_loop_count + 1; EXIT outer_child_loop WHEN v_batch_loop_count >= p_batch_count; -- Exit outer FOR loop if p_batch_count is reached END LOOP inner_child_loop; END LOOP outer_child_loop; IF v_batch_loop_count < p_batch_count THEN -- FOR loop never ran, so there's no child tables left. DELETE FROM @extschema@.part_config WHERE parent_table = p_parent_table; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Removing config from pg_partman'); PERFORM update_step(v_step_id, 'OK', 'Done'); END IF; END IF; RAISE NOTICE 'Copied % row(s) to the parent. Removed % partitions.', v_total, v_undo_count; IF v_jobmon_schema IS NOT NULL THEN v_step_id := add_step(v_job_id, 'Final stats'); PERFORM update_step(v_step_id, 'OK', format('Copied %s row(s) to the parent. Removed %s partitions.', v_total, v_undo_count)); END IF; IF v_jobmon_schema IS NOT NULL THEN PERFORM close_job(v_job_id); EXECUTE format('SELECT set_config(%L, %L, %L)', 'search_path', v_old_search_path, 'false'); END IF; RETURN v_total; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN UNDO PARTITIONING: %s'')', v_jobmon_schema, p_parent_table) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; /* * Function to list all child partitions in a set. */ CREATE OR REPLACE FUNCTION show_partitions (p_parent_table text, p_order text DEFAULT 'ASC') RETURNS TABLE (partition_schemaname text, partition_tablename text) LANGUAGE plpgsql STABLE SECURITY DEFINER AS $$ DECLARE v_datetime_string text; v_parent_schema text; v_parent_tablename text; v_partition_interval text; v_quarter text; v_type text; v_year text; BEGIN IF upper(p_order) NOT IN ('ASC', 'DESC') THEN RAISE EXCEPTION 'p_order paramter must be one of the following values: ASC, DESC'; END IF; SELECT partition_type , partition_interval , datetime_string INTO v_type , v_partition_interval , v_datetime_string FROM @extschema@.part_config WHERE parent_table = p_parent_table; SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname||'.'||tablename = p_parent_table; IF v_type IN ('time', 'time-custom') THEN IF v_partition_interval::interval <> '3 months' OR (v_partition_interval::interval = '3 months' AND v_type = 'time-custom') THEN RETURN QUERY EXECUTE 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 ORDER BY to_timestamp(substring(c.relname from ((length(c.relname) - position(''p_'' in reverse(c.relname))) + 2) ), %L) %s' , v_parent_schema , v_parent_tablename , v_datetime_string , p_order); ELSE -- 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) RETURN QUERY EXECUTE 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 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' , v_parent_schema , v_parent_tablename , p_order , p_order); END IF; ELSIF v_type = 'id' THEN RETURN QUERY EXECUTE 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 ORDER BY substring(c.relname from ((length(c.relname) - position(''p_'' in reverse(c.relname))) + 2) )::bigint %s' , v_parent_schema , v_parent_tablename , p_order); END IF; END $$; -- Restore dropped object privileges DO $$ DECLARE v_row record; BEGIN FOR v_row IN SELECT statement FROM partman_preserve_privs_temp LOOP IF v_row.statement IS NOT NULL THEN EXECUTE v_row.statement; END IF; END LOOP; END $$; DROP TABLE IF EXISTS partman_preserve_privs_temp; pg_partman-2.2.2/updates/pg_partman--2.2.0--2.2.1.sql000066400000000000000000000517731262146621700214340ustar00rootroot00000000000000-- 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) /* * Function to manage pre-creation of the next partitions in a set. * 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_run_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. * Be aware that constraint exclusion may not work properly until an analyze on the partition set is run. */ CREATE OR REPLACE FUNCTION run_maintenance(p_parent_table text DEFAULT NULL, p_analyze boolean DEFAULT true, p_jobmon boolean DEFAULT true, p_debug boolean DEFAULT false) RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$ DECLARE ex_context text; ex_detail text; ex_hint text; ex_message text; v_adv_lock boolean; v_check_subpart int; v_create_count int := 0; v_current_partition text; v_current_partition_id bigint; v_current_partition_timestamp timestamp; v_datetime_string text; v_drop_count int := 0; v_id_position int; 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 timestamp; v_max_id_parent bigint; v_max_time_parent timestamp; v_next_partition_id bigint; v_next_partition_timestamp timestamp; v_parent_schema text; v_parent_tablename text; v_premade_count int; v_premake_id_max bigint; v_premake_id_min bigint; v_premake_timestamp_min timestamp; v_premake_timestamp_max timestamp; v_quarter text; v_row record; v_row_max_id record; v_row_max_time record; v_row_sub record; v_skip_maint boolean; v_step_id bigint; v_step_overflow_id bigint; v_step_serial_id bigint; v_sub_id_max bigint; v_sub_id_max_suffix bigint; v_sub_id_min bigint; v_sub_parent text; v_sub_timestamp_max timestamp; v_sub_timestamp_max_suffix timestamp; v_sub_timestamp_min timestamp; v_tablename text; v_tables_list_sql text; v_time_position int; v_year text; BEGIN v_adv_lock := pg_try_advisory_xact_lock(hashtext('pg_partman run_maintenance')); IF v_adv_lock = 'false' THEN RAISE NOTICE 'Partman maintenance already running.'; RETURN; END IF; IF p_jobmon THEN SELECT nspname INTO v_jobmon_schema FROM pg_catalog.pg_namespace n, pg_catalog.pg_extension e WHERE e.extname = 'pg_jobmon' AND e.extnamespace = n.oid; END IF; IF v_jobmon_schema IS NOT NULL THEN EXECUTE format('SELECT %I.add_job(%L)', v_jobmon_schema, 'PARTMAN RUN MAINTENANCE') INTO v_job_id; EXECUTE format('SELECT %I.add_step(%L, %L)', v_jobmon_schema, v_job_id, 'Running maintenance loop') INTO v_step_id; 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! -- Don't have to worry about this in the serial trigger maintenance since subpartitioning requires run_maintenance(). FOR v_row IN SELECT sub_parent FROM @extschema@.part_config_sub LOOP SELECT count(*) INTO v_check_subpart FROM @extschema@.check_subpart_sameconfig(v_row.sub_parent); 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, results are all children of the given parent. Update the differing values to be consistent for your desired values.', v_row.sub_parent; END IF; END LOOP; v_row := NULL; -- Ensure it's reset v_tables_list_sql := 'SELECT parent_table , partition_type , partition_interval , control , premake , datetime_string , undo_in_progress , sub_partition_set_full , epoch FROM @extschema@.part_config WHERE sub_partition_set_full = false'; IF p_parent_table IS NULL THEN v_tables_list_sql := v_tables_list_sql || ' AND use_run_maintenance = true'; ELSE v_tables_list_sql := v_tables_list_sql || format(' AND parent_table = %L', p_parent_table); END IF; FOR v_row IN EXECUTE v_tables_list_sql LOOP CONTINUE WHEN v_row.undo_in_progress; v_skip_maint := true; -- reset every loop SELECT schemaname, tablename INTO v_parent_schema, v_parent_tablename FROM pg_catalog.pg_tables WHERE schemaname ||'.'|| tablename = v_row.parent_table; SELECT partition_tablename INTO v_last_partition FROM @extschema@.show_partitions(v_row.parent_table, 'DESC') LIMIT 1; IF p_debug THEN RAISE NOTICE 'run_maint: parent_table: %, v_last_partition: %', v_row.parent_table, v_last_partition; END IF; IF v_row.partition_type = 'time' OR v_row.partition_type = 'time-custom' THEN v_time_position := (length(v_last_partition) - position('p_' in reverse(v_last_partition))) + 2; IF v_row.partition_interval::interval <> '3 months' OR (v_row.partition_interval::interval = '3 months' AND v_row.partition_type = 'time-custom') THEN v_last_partition_timestamp := to_timestamp(substring(v_last_partition from v_time_position), v_row.datetime_string); ELSE -- to_timestamp doesn't recognize 'Q' date string formater. Handle it v_year := split_part(substring(v_last_partition FROM v_time_position), 'q', 1); v_quarter := split_part(substring(v_last_partition FROM v_time_position), 'q', 2); CASE WHEN v_quarter = '1' THEN v_last_partition_timestamp := to_timestamp(v_year || '-01-01', 'YYYY-MM-DD'); WHEN v_quarter = '2' THEN v_last_partition_timestamp := to_timestamp(v_year || '-04-01', 'YYYY-MM-DD'); WHEN v_quarter = '3' THEN v_last_partition_timestamp := to_timestamp(v_year || '-07-01', 'YYYY-MM-DD'); WHEN v_quarter = '4' THEN v_last_partition_timestamp := to_timestamp(v_year || '-10-01', 'YYYY-MM-DD'); END CASE; 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_time IN SELECT partition_schemaname, partition_tablename FROM @extschema@.show_partitions(v_row.parent_table, 'DESC') LOOP IF v_row.epoch = false THEN EXECUTE format('SELECT max(%I)::text FROM %I.%I' , v_row.control , v_row_max_time.partition_schemaname , v_row_max_time.partition_tablename ) INTO v_current_partition_timestamp; ELSE EXECUTE format('SELECT to_timestamp(max(%I))::text FROM %I.%I' , v_row.control , v_row_max_time.partition_schemaname , v_row_max_time.partition_tablename ) INTO v_current_partition_timestamp; 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 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 IF v_row.epoch = false THEN EXECUTE format('SELECT max(%I) FROM ONLY %I.%I', v_row.control, v_parent_schema, v_parent_tablename) INTO v_max_time_parent; ELSE EXECUTE format('SELECT to_timestamp(max(%I)) FROM ONLY %I.%I', v_row.control, v_parent_schema, v_parent_tablename) INTO v_max_time_parent; END IF; IF p_debug THEN RAISE NOTICE 'run_maint: v_current_partition_timestamp: %, v_max_time_parent: %', v_current_partition_timestamp, v_max_time_parent; END IF; IF v_max_time_parent > v_current_partition_timestamp THEN SELECT suffix_timestamp INTO v_current_partition_timestamp FROM @extschema@.show_partition_name(v_row.parent_table, v_max_time_parent::text); END IF; IF v_current_partition_timestamp IS NULL THEN -- Partition set is completely empty. Nothing to do CONTINUE; END IF; -- If this is a subpartition, determine if the last child table has been made. If so, mark it as full so future maintenance runs can skip it SELECT sub_min::timestamp, sub_max::timestamp INTO v_sub_timestamp_min, v_sub_timestamp_max FROM @extschema@.check_subpartition_limits(v_row.parent_table, 'time'); IF v_sub_timestamp_max IS NOT NULL THEN SELECT suffix_timestamp INTO v_sub_timestamp_max_suffix FROM @extschema@.show_partition_name(v_row.parent_table, v_sub_timestamp_max::text); IF v_sub_timestamp_max_suffix = v_last_partition_timestamp THEN -- Final partition for this set is created. Set full and skip it UPDATE @extschema@.part_config SET sub_partition_set_full = true WHERE parent_table = v_row.parent_table; CONTINUE; END IF; END IF; -- Check and see how many premade partitions there are. 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; IF p_debug THEN RAISE NOTICE 'run_maint before loop: current_partition_timestamp: %, v_premade_count: %, v_sub_timestamp_min: %, v_sub_timestamp_max: %' , v_current_partition_timestamp , v_premade_count , v_sub_timestamp_min , v_sub_timestamp_max; END IF; -- 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 IF p_debug THEN RAISE NOTICE 'run_maint: parent_table: %, v_premade_count: %, v_next_partition_timestamp: %', v_row.parent_table, v_premade_count, v_next_partition_timestamp; END IF; IF v_next_partition_timestamp < v_sub_timestamp_min OR v_next_partition_timestamp > v_sub_timestamp_max THEN -- With subpartitioning, no need to run if the timestamp is not in the parent table's range EXIT; END IF; 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 IF v_jobmon_schema IS NOT NULL THEN EXECUTE format('SELECT %I.add_step(%L, %L)', v_jobmon_schema, v_job_id, 'Attempted partition time interval is outside PostgreSQL''s supported time range.') INTO v_step_overflow_id; EXECUTE format('SELECT %I.update_step(%L, %L, %L)', v_jobmon_schema, v_step_overflow_id, 'CRITICAL', 'Child partition creation skippd for parent table '||v_partition_time); END IF; RAISE WARNING 'Attempted partition time interval is outside PostgreSQL''s supported time range. Child partition creation skipped for parent table %', v_row.parent_table; CONTINUE; END; v_last_partition_created := @extschema@.create_partition_time(v_row.parent_table, ARRAY[v_next_partition_timestamp], p_analyze); IF v_last_partition_created THEN v_create_count := v_create_count + 1; PERFORM @extschema@.create_function_time(v_row.parent_table, v_job_id); 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)); END LOOP; ELSIF v_row.partition_type = 'id' THEN -- 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') LOOP EXECUTE format('SELECT max(%I)::text FROM %I.%I' , v_row.control , v_row_max_id.partition_schemaname , v_row_max_id.partition_tablename) INTO v_current_partition_id; IF v_current_partition_id IS NOT NULL THEN SELECT suffix_id INTO v_current_partition_id FROM @extschema@.show_partition_name(v_row.parent_table, v_current_partition_id::text); EXIT; END IF; END LOOP; -- Check for values in the parent 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_parent_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); END IF; IF v_current_partition_id IS NULL THEN -- Partition set is completely empty. Nothing to do CONTINUE; END IF; v_id_position := (length(v_last_partition) - position('p_' in reverse(v_last_partition))) + 2; v_last_partition_id = substring(v_last_partition from v_id_position)::bigint; -- Determine if this table is a child of a subpartition parent. If so, get limits to see if run_maintenance even needs to run for it. SELECT sub_min::bigint, sub_max::bigint INTO v_sub_id_min, v_sub_id_max FROM @extschema@.check_subpartition_limits(v_row.parent_table, 'id'); IF v_sub_id_max IS NOT NULL THEN SELECT suffix_id INTO v_sub_id_max_suffix FROM @extschema@.show_partition_name(v_row.parent_table, v_sub_id_max::text); IF v_sub_id_max_suffix = v_last_partition_id THEN -- Final partition for this set is created. Set full and skip it UPDATE @extschema@.part_config SET sub_partition_set_full = true WHERE parent_table = v_row.parent_table; CONTINUE; END IF; END IF; 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 IF p_debug THEN RAISE NOTICE 'run_maint: parent_table: %, v_premade_count: %, v_next_partition_id: %', v_row.parent_table, v_premade_count, v_next_partition_id; END IF; 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], p_analyze); IF v_last_partition_created THEN v_create_count := v_create_count + 1; PERFORM @extschema@.create_function_id(v_row.parent_table, v_job_id); 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 -- Manage additonal constraints if set PERFORM @extschema@.apply_constraints(p_parent_table := v_row.parent_table, p_job_id := v_job_id, p_debug := p_debug); END LOOP; -- end of creation loop -- Manage dropping old partitions if retention option is set FOR v_row IN SELECT parent_table FROM @extschema@.part_config WHERE retention IS NOT NULL AND undo_in_progress = false AND (partition_type = 'time' OR partition_type = 'time-custom') LOOP IF p_parent_table IS NULL THEN v_drop_count := v_drop_count + @extschema@.drop_partition_time(v_row.parent_table); ELSE -- Only run retention on table given in parameter IF p_parent_table <> v_row.parent_table THEN CONTINUE; ELSE v_drop_count := v_drop_count + @extschema@.drop_partition_time(v_row.parent_table); END IF; END IF; IF v_drop_count > 0 THEN PERFORM @extschema@.create_function_time(v_row.parent_table, v_job_id); END IF; END LOOP; FOR v_row IN SELECT parent_table FROM @extschema@.part_config WHERE retention IS NOT NULL AND undo_in_progress = false AND partition_type = 'id' LOOP IF p_parent_table IS NULL THEN v_drop_count := v_drop_count + @extschema@.drop_partition_id(v_row.parent_table); ELSE -- Only run retention on table given in parameter IF p_parent_table <> v_row.parent_table THEN CONTINUE; ELSE v_drop_count := v_drop_count + @extschema@.drop_partition_id(v_row.parent_table); END IF; END IF; IF v_drop_count > 0 THEN PERFORM @extschema@.create_function_id(v_row.parent_table, v_job_id); END IF; END LOOP; IF v_jobmon_schema IS NOT NULL THEN EXECUTE format('SELECT %I.update_step(%L, %L, ''Partition maintenance finished. %s partitons made. %s partitions dropped.'')' , v_jobmon_schema , v_step_id , 'OK' , v_create_count , v_drop_count); IF v_step_overflow_id IS NOT NULL OR v_step_serial_id IS NOT NULL THEN EXECUTE format('SELECT %I.fail_job(%L)', v_jobmon_schema, v_job_id); ELSE EXECUTE format('SELECT %I.close_job(%L)', v_jobmon_schema, v_job_id); END IF; END IF; EXCEPTION WHEN OTHERS THEN GET STACKED DIAGNOSTICS ex_message = MESSAGE_TEXT, ex_context = PG_EXCEPTION_CONTEXT, ex_detail = PG_EXCEPTION_DETAIL, ex_hint = PG_EXCEPTION_HINT; IF v_jobmon_schema IS NOT NULL THEN IF v_job_id IS NULL THEN EXECUTE format('SELECT %I.add_job(''PARTMAN RUN MAINTENANCE'')', v_jobmon_schema) INTO v_job_id; EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before job logging started'')', v_jobmon_schema, v_job_id, p_parent_table) INTO v_step_id; ELSIF v_step_id IS NULL THEN EXECUTE format('SELECT %I.add_step(%s, ''EXCEPTION before first step logged'')', v_jobmon_schema, v_job_id) INTO v_step_id; END IF; EXECUTE format('SELECT %I.update_step(%s, ''CRITICAL'', %L)', v_jobmon_schema, v_step_id, 'ERROR: '||coalesce(SQLERRM,'unknown')); EXECUTE format('SELECT %I.fail_job(%s)', v_jobmon_schema, v_job_id); END IF; RAISE EXCEPTION '% CONTEXT: % DETAIL: % HINT: %', ex_message, ex_context, ex_detail, ex_hint; END $$; pg_partman-2.2.2/updates/pg_partman--2.2.1--2.2.2.sql000066400000000000000000000006161262146621700214240ustar00rootroot00000000000000-- NOTE: No changes to the core extension code contained in this update. This file is only here for version upgrade continuity. -- 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).