When tables in PolarDB for PostgreSQL grow large, managing partitions manually becomes complex and time-consuming. The pg_partman extension for PostgreSQL addresses this issue by automating partition management. The extension automatically creates and maintains table partitions based on time or a serial ID and can enforce data retention policies. This process simplifies the lifecycle management of partitioned tables, improving database maintainability and query performance.
Applicability
The following versions of PolarDB for PostgreSQL are supported:
PostgreSQL 17 (minor engine version 2.0.17.7.5.0 or later)
PostgreSQL 16 (minor engine version 2.0.16.10.11.0 or later)
PostgreSQL 14 (minor engine version 2.0.14.19.39.0 or later)
You can view the minor engine version number in the console or by running the SHOW polardb_version; statement. If your version does not meet the requirements, upgrade the minor engine version.
Features
The core feature of the pg_partman extension is the automated management of partitioned tables based on time or a numeric ID. Starting from version 5.0.1, pg_partman is based entirely on the built-in declarative partitioning feature of PostgreSQL and no longer uses the legacy trigger-based method.
The mechanism works as follows:
Automatic partition creation: A background maintenance task pre-creates future partitions based on a set interval, such as daily, monthly, or every 1 million IDs. This ensures that new data is written seamlessly.
Data retention policy: Automatically detaches (DETACH) or drops (DROP) expired data partitions. This effectively manages the data lifecycle and storage costs.
Default partition: Creates a default partition for each partition set to store any data that does not fit into an existing child partition. This prevents data loss. You can use helper functions to migrate this data to the correct partitions.
Subpartition support: Supports multi-level partitioning. For example, you can further divide annual partitions into daily partitions to meet complex data organization needs.
Quick start
This section provides a complete example of partitioning a log table by day to help you quickly learn the basic usage of pg_partman. You will create a log table named log_table, create a new partition daily based on the created_at field, and automatically delete data older than seven days.
Step 1: Install the extension and create the parent table
First, install the pg_partman extension in your database. Then, create a parent table for partitioning.
-- 1. Enable the pg_partman extension in the current database.
CREATE EXTENSION pg_partman;
-- 2. Create a parent table to store log data.
-- PARTITION BY RANGE (created_at) specifies that this table is a partitioned table and uses the created_at column for range partitioning.
CREATE TABLE public.log_table (
id bigint GENERATED BY DEFAULT AS IDENTITY,
created_at timestamptz NOT NULL,
message text
) PARTITION BY RANGE (created_at);Step 2: Initialize the partition set
Call the create_parent() function to register log_table with the pg_partman management system and define the partitioning rules.
-- Call the create_parent function to initialize partitioning.
-- public.log_table: The parent table to manage.
-- created_at: The partition key column.
-- '1 day': The partition interval. A new partition is created every day.
SELECT create_parent(
p_parent_table := 'public.log_table',
p_control := 'created_at',
p_interval := '1 day'
);After the statement is successfully executed, pg_partman automatically creates partitions for the current day and the next few days. You can run the \d+ public.log_table command to view the created partitions.
Step 3: Insert test data
Insert some data into the parent table log_table. The data is automatically routed to the corresponding partition based on the value of created_at.
INSERT INTO public.log_table (created_at, message)
SELECT generate_series(
now() - interval '4 days',
now(),
interval '1 hour'
),
'This is a log message.';Step 4: Configure the retention policy and verify cleanup
Set a data retention policy to let pg_partman automatically clean up expired data. Configure log_table to retain only the last two days of data and automatically DROP expired partitions.
-- 1. Update the part_config table to set a retention policy for log_table.
UPDATE part_config
SET
retention = '2 days', -- Retention period of 2 days.
retention_keep_table = false -- Do not keep the table structure. Directly DROP expired partitions.
WHERE parent_table = 'public.log_table';
-- 2. Manually run the maintenance task to trigger the retention policy.
CALL run_maintenance_proc();
-- 3. Verification: Check the partition list again to confirm that partitions older than 2 days are deleted.
SELECT partition_schemaname, partition_tablename FROM show_partitions('public.log_table', 'DESC');You have now successfully configured a log table that is partitioned by day and has an automatic cleanup policy.
Notes
Before you use pg_partman, understand the following limitations and risks to avoid data loss or service interruptions.
Attribute inheritance:
pg_partmanuses a template table to manage some attributes that cannot be directly inherited from the parent table, such as specific indexes or theUNLOGGEDstate. Changes to the template table only affect newly created partitions. Existing partitions must be updated manually.Time zones: When you partition by time, ensure that your database system, clients, and all environments that call maintenance tasks use a consistent time zone. We recommend using UTC to prevent issues such as daylight saving time (DST) from causing partition creation to fail or be skipped.
Subpartitions:
The main purpose of subpartitions is data organization and lifecycle management. They offer limited performance improvement. If you want to optimize performance, we recommend adjusting the partition interval instead of relying on subpartitions. Too many subpartitions increase management overhead and may exhaust
max_locks_per_transactionresources.Subpartitions do not support logical replication (
PUBLICATION/SUBSCRIPTION). If you plan to use logical replication for a partitioned table, avoid using the subpartition feature.Subpartition creation (
create_sub_parent) is a destructive operation. It deletes and recreates existing partitions to add the next level of partitioning. Before you run this operation, we recommend that you back up the relevant data.
Object name length: PostgreSQL object names are limited to 63 bytes.
pg_partmanautomatically truncates table names to fit the suffix. However, if parent table names are too long and similar, partition names under different partition sets may conflict. We recommend that you keep parent table names concise.UNIQUE constraints: You cannot create a global unique constraint or primary key on columns other than the partition key. This is a native limitation of PostgreSQL declarative partitioning.
pg_partmanlets you create local unique constraints on each partition, but this does not guarantee global uniqueness across the entire partition set.Default partition: Handle data that falls into the default partition with care.
pg_partmancreates a default partition to store data that does not fit into any existing partition. By default, partition maintenance does not check the data in the default partition to create new partitions. If data is continuously written to the default partition, use thepartition_data_*series of functions to migrate it to the correct partition as soon as possible. Also, investigate the cause of the abnormal data writes.Undo partitioning: All partition maintenance is paused while
undo_partition()is running. Until the undo operation is complete,run_maintenance()will not create new partitions or run retention policies for that partition set.Locks and resources: Managing many partitions can increase lock resource consumption. If a single partition set contains hundreds or thousands of partitions,
run_maintenance()or other maintenance operations may require you to adjust themax_locks_per_transactionparameter to avoid running out of memory. In this case, we recommend using therun_maintenance_proc()procedure. It commits the transaction after maintaining each partition set, which reduces lock contention from long-running transactions.
Best practices
Scenario 1: Daily partitioned log table with auto-cleanup
This scenario is suitable for data that grows rapidly and has a clear lifecycle, such as logs, audit records, and Internet of Things (IoT) time series data.
Business scenario
Partition the audit_logs table by day, retain data for 30 days, and automatically archive expired data to another schema instead of deleting it directly.
Procedure
Create the parent table and initialize the partition set:
-- Create the parent table. CREATE TABLE public.audit_logs ( log_id uuid NOT NULL, event_time timestamptz NOT NULL, user_id text, details jsonb, PRIMARY KEY (event_time, log_id) ) PARTITION BY RANGE (event_time); -- Initialize the partition set to partition by day. SELECT create_parent( p_parent_table := 'public.audit_logs', p_control := 'event_time', p_interval := '1 day' );Configure the retention and archiving policy: Move old partition tables older than 30 days to the
archiveschema for later batch analytics or backup.-- Create a schema for archiving. CREATE SCHEMA IF NOT EXISTS archive; -- Update the configuration to set the retention policy. UPDATE part_config SET retention = '30 days', -- Retain for 30 days. retention_schema = 'archive' -- Move to the archive schema after expiration. WHERE parent_table = 'public.audit_logs';When
run_maintenance()runs, any partition table that is completely older than 30 days, such asaudit_logs_p2023_10_01, will be detached from the main partition set. Its schema will be changed frompublictoarchive, becomingarchive.audit_logs_p2023_10_01.
Scenario 2: Business table partitioned by ID
This scenario is suitable for businesses that use a numeric ID as the primary key for tables such as user tables or product tables and want to separate hot and cold data through partitioning.
Business scenario
Partition the orders table by order_id. Each partition contains 10 million IDs.
Recommendation
The choice of p_interval is critical. Estimate it based on the growth rate of business IDs and a reasonable size for a single partition. If 1 million new orders are added daily, an interval of 10000000 means a new partition is created approximately every 10 days.
Procedure
Create the parent table and initialize the partition set:
-- Create the parent table. CREATE TABLE public.orders ( order_id bigint NOT NULL, customer_id bigint, order_date timestamptz, amount numeric ) PARTITION BY RANGE (order_id); -- Initialize the partition set to partition by ID range. -- Note: p_interval must be provided as text, even if it is a number. SELECT create_parent( p_parent_table := 'public.orders', p_control := 'order_id', p_interval := '10000000' -- Each partition contains 10 million IDs. );Configure the retention policy (optional): If old order data can be archived or deleted, you can configure an ID-based retention policy.
-- Retain the most recent 50 million orders. -- Assume the current maximum order_id is 100,000,000. -- This policy deletes all partitions where order_id < 50,000,000. UPDATE part_config SET retention = '50000000', retention_keep_table = false WHERE parent_table = 'public.orders';
Scenario 3: Migrating an existing large table to a partitioned table
This scenario is for smoothly migrating an existing, massive monolithic table (for example, terabyte-scale) to a partitioned structure managed by pg_partman.
Business scenario
Migrate a 1 TB sensor_data table to a table partitioned by month with minimal impact on online business.
Core tool
The partition_data_proc() procedure. It can migrate data in small batches and independent transactions, which avoids long table locks and transaction backlogs.
Procedure
Preparations: Create the parent table and initialize the partition set.
-- 1. Rename the original table to serve as the data source. ALTER TABLE public.sensor_data RENAME TO sensor_data_source; -- 2. Create a new parent table with the same structure. CREATE TABLE public.sensor_data ( reading_id bigserial, device_id text, ts timestamptz NOT NULL, value double precision ) PARTITION BY RANGE (ts); -- 3. Initialize the partition set. SELECT create_parent('public.sensor_data','ts','1 month');Migrate the data: Use
partition_data_procto migrate data in batches fromsensor_data_sourceto the new partitioned tablesensor_data.-- Call the data migration procedure. -- p_source_table: Specifies the source table. -- p_interval: The time range of data to process in each batch. We recommend that this be less than or equal to the partition interval. Here it is set to '1 day', meaning one day's worth of data is processed in a single transaction. -- p_wait: The waiting time (in seconds) between each batch. This is used to reduce sustained pressure on I/O and CPU. CALL partition_data_proc( p_parent_table := 'public.sensor_data', p_source_table := 'public.sensor_data_source', p_interval := '1 day', p_wait := 1 );This procedure runs in a loop until all data from the
sensor_data_sourcetable is migrated. During the migration,pg_partmanautomatically creates the required partitions as needed.Final steps: After the migration is complete, verify data consistency. Then, you can safely delete the original data source table
sensor_data_source. During the migration, the application can continue to write new data to the new partitioned parent tablesensor_data.
Core concepts
Partition type and interval
Partition type
pg_partman is based on PostgreSQL's declarative partitioning and mainly supports the following partitioning methods:
Range Partitioning: Suitable for continuous data, such as timestamps or serial IDs. This is the most common partition type.
List Partitioning: Supported only for numeric ID-based partitioning with an interval of
1.
Partition interval (p_interval)
This parameter defines the data range covered by each child partition.
Time-based: Accepts any valid
intervalvalue, such as'1 hour','1 day','1 week', or'1 month'.NoteThe minimum supported time interval is 1 second. The upper limit is constrained by the minimum and maximum timestamp values supported by PostgreSQL.
When you first run
create_parent()to create a partition set, intervals of less than one day are rounded down to determine the first partition to create.Intervals less than 24 hours but greater than 1 minute are rounded down to the nearest hour.
Intervals less than 1 minute are rounded down to the nearest minute.
The system creates enough partitions to support the current real time. This means that when you run
create_parent(), more past partitions than expected may be created, and not all future partitions may be created. The first time you runrun_maintenance(), it creates any missing future partitions. This behavior is necessary to support custom time intervals. For intervals of 24 hours or more, the setup proceeds as expected.
For intervals of 100 years or more, the extension uses the actual start of the century or millennium to determine partition names and constraint rules. For example, the 21st century and the 3rd millennium began on January 1, 2001 (not 2000). This also means there is no year 0.
Numeric/ID-based: Accepts an integer representing the range size, but it must be provided as text, such as
'1000000'.
Weekly partitioning: The start day of the week depends on the
p_start_partitionparameter or the current date whencreate_parent()is executed. To ensure that Monday is the start day, we recommend explicitly specifying it with thedate_trunc()function. For example:p_start_partition := to_char(date_trunc('week', CURRENT_TIMESTAMP), 'YYYY-MM-DD HH24:MI:SS').Epoch time values: If the partition key is an integer column that stores a Unix epoch, you can set the
p_epochparameter (such as'seconds'or'milliseconds') to havepg_partmanmanage it as time-based partitioning.
Sub-partitioning
Sub-partitioning lets you create finer-grained partitions on existing partitions, forming a multi-level partition structure (such as partitioning by year, then sub-partitioning each year by month). It is mainly used for data organization and lifecycle management, such as archiving a large annual partition on a monthly basis. For performance improvement, sub-partitioning usually has a limited effect. Consider adjusting the interval of the top-level partitions first.
Subpartition creation (create_sub_parent) is a destructive operation. It deletes and recreates existing partitions to add the next level of partitioning. Before you run this operation, we recommend that you back up the relevant data.
Data retention policy
pg_partman automatically runs a data retention policy through run_maintenance() to manage the lifecycle of old partitions. The core configuration parameters are as follows:
Parameter | Description | Example |
| The retention period.
|
|
|
|
|
| Moves detached old partitions to a specified archive schema instead of keeping them in the original schema or dropping them. This setting takes precedence over |
|
| Whether to keep the indexes on old partitions when they are detached. The default is |
|
For sub-partition sets, when a partition of a parent table is dropped, if that partition is itself a partitioned table, the drop operation will CASCADE to all lower-level partitions in the entire inheritance tree and drop them all. Also note that a partition set managed by pg_partman must always retain at least one partition. Therefore, the retention policy will never drop the last partition in a partition set.
Constraint exclusion
Constraint exclusion is a key feature for improving the query performance of partitioned tables. When the WHERE clause of a query can clearly exclude certain partitions, the query optimizer skips scanning those partitions.
pg_partman provides the ability to add CHECK constraints on non-partition key columns to improve constraint exclusion. For example, for a table partitioned by created_at, if there are frequent queries with WHERE device_id = 'A', you can add a constraint to the device_id column of old partitions where data no longer changes.
The core configuration parameters are as follows:
constraint_cols: A text array that specifies on which columns to createCHECKconstraints. For example,'{device_id, user_id}'.optimize_constraint: An integer that defines how old a partition must be before the constraint is applied. The default value of30means the constraint is applied to partitions that are 30 partition intervals older than the current one.constraint_valid: Adding a constraint to a table may conflict with existing data in the table. It can also causepg_partman's maintenance operations to take a long time. You can set these constraints to be created as not valid. This allows the constraint creation to be almost instantaneous, but constraint exclusion cannot be used until they are validated. Therefore, by default, constraints are created as valid.
This feature restricts data updates on old partitions where constraints have been added. To modify old data, to allow
pg_partmanto manage these constraints correctly, do not rename the constraints it manages. You can usereapply_constraints_proc()to temporarily remove the constraints.constraint_validmay not work correctly with sub-partitioning. It can run correctly on the first level of partitions, but whether it is effective in deeper sub-partition sets depends on the combination of partition intervals used and theoptimize_constraintsetting. For example, when weekly partitions are further subdivided into daily partitions, and the daily partitions'optimize_constraintis set to 7 days, it may not achieve the expected effect. Although the weekly partition's constraints can be created correctly, the daily subpartitions may not have corresponding constraints created.
Template table and attribute inheritance
Because PostgreSQL's declarative partitioning has incomplete support for inheriting certain object attributes (such as primary keys/unique indexes on non-partition key columns, autovacuum settings, etc.), pg_partman uses a template table mechanism to ensure that newly created partitions can correctly inherit these attributes. The following table shows how pg_partman manages the inheritance of specific attributes. If an attribute is not listed in the table, it is managed through the parent table.
Feature | Inherited from parent | Inherited from template |
Primary key on non-partition column | - | PostgreSQL 14 and later |
Unique index on non-partition column | - | PostgreSQL 14 and later |
Tablespace of unique index on non-partition column | - | PostgreSQL 14 and later |
Relation-specific options (autovacuum, etc.) | - | PostgreSQL 14 and later |
UNLOGGED table state* | - | PostgreSQL 14 and later |
Non-unique indexes | PostgreSQL 14 and later | - |
Permissions/Ownership | PostgreSQL 14 and later | - |
Attributes: When you call
create_parent(),pg_partmanautomatically creates a table namedparent_table_template. AnyALTER TABLEmodifications you make to this template table (such as adding an index or changingautovacuumparameters) will be applied when new partitions are created in the future.NoteModifications to the template table are not retroactively applied to existing partitions. To modify them, you must manually run
ALTERoperations on the old partitions.Permissions and ownership: By default, permissions and ownership are not inherited. If you enable this feature through
pg_partman, note that this inheritance only occurs at partition creation time. Changes are not automatically applied afterward (they can be reapplied withreapply_privileges()). You usually do not need to enable this feature unless you need to access partitions directly. If you do, you can set theinherit_privilegesoption.NoteIf you use the
IDENTITYfeature to manage sequences, it only supports auto-generation of new sequence values when data is inserted through the parent table. It is not supported when inserting data directly into a partition.
The template table feature is a temporary solution designed to accelerate the adoption of declarative partitioning. As PostgreSQL core functionality improves,
pg_partmanwill gradually phase out the use of template tables. If a feature is supported in PostgreSQL core in the future, it will no longer be managed through the template table. Plan for related adjustments when upgrading to a major version.If you want to use the
REPLICA IDENTITYattribute with theUSING INDEXclause to support logical replication, note that this feature is only supported if the required index is created on the actual parent table, not on the template table. Because you cannot control theREPLICA IDENTITYsettings on both the parent and template tables, it is impossible to determine which one is the correct identity. To maintain consistency with other identity inheritance methods (FULL and NONE), we recommend selecting only the parent table as the source.Due to inconsistencies in how PostgreSQL handles enabling or disabling the
UNLOGGEDattribute on the parent table of a partition set,pg_partmanuses the template table to manage theUNLOGGEDstate. When anALTERcommand is run, the attribute on the parent table does not actually change, so new partitions will continue to use the attribute from before the change. This means if you want to change a partition set fromUNLOGGEDtoLOGGEDand have all future partitions inherit this change, it is not possible through standard commands. Because this attribute is now managed through the template table, changing the attribute on the template table causes newly created partitions to inherit the change. However, existing partitions still need to be changed manually, but this has always been the case. For more information, see Unable to alter partitioned table to set logged.
O&M and management
Automated maintenance
The core maintenance tasks of pg_partman (creating new partitions and running retention policies) are performed by the run_maintenance() function or the run_maintenance_proc() procedure. In PolarDB for PostgreSQL, we recommend using the built-in Background Worker to automate this process. It automatically calls run_maintenance_proc() based on the interval you set.
The Background Worker is disabled by default. For security reasons, you cannot enable it directly. To enable it, go to the Quota Center, find the quota ID polardb_pg_pg_cron, and in the Actions column, click Apply.
Manual maintenance
In some cases, you may need to manually trigger maintenance, such as when debugging or when you need fine-grained control over a specific table.
CALL run_maintenance_proc();We recommend using this procedure. It runs maintenance for all partition sets inpart_configwhereautomatic_maintenanceison. It also automatically commits the transaction after each partition set is maintained, which reduces lock contention.SELECT run_maintenance('public.my_table');If you only want to run maintenance on a single partition set, you can call this function and pass the parent table name.
Monitoring and alerting
In a production environment, you need to monitor whether partition maintenance is running correctly to prevent data write failures caused by partitions not being created in time. The maintenance_last_run field in the part_config table records the timestamp of the last successful maintenance run for each partition set. This is a key monitoring metric.
-- Query for all automatically maintained partition sets that have not run successfully in over twice the maintenance interval.
SELECT parent_table
FROM part_config
WHERE
automatic_maintenance = 'on'
AND maintenance_last_run < (now() - (SELECT setting::interval * 2 FROM pg_settings WHERE name = 'pg_partman_bgw.interval'));If this query returns any rows, it means that the maintenance for the corresponding partition set may have been interrupted and requires immediate investigation.
Reference functions
pg_partman provides its core capabilities through a series of functions. These functions are divided into four main categories based on their purpose: creation, migration, maintenance, and destruction.
Create partitions
These functions are used to initialize partition sets and define subpartitions.
create_parent()
This function initializes a partition set. It configures the partitioning policy, pre-creation rules, and maintenance method for an existing parent table that has been declared as partitioned. It places an ACCESS EXCLUSIVE lock on the parent table while it runs.
Apply all defaults, indexes, constraints, permissions, and ownership to the parent table before calling this function so they can be propagated to the child tables. For instructions on handling unique indexes and other table properties, see Template table and attribute inheritance.
By default, a default partition and a template table are created, unless configured otherwise.
Syntax
create_parent(
p_parent_table text
, p_control text
, p_interval text
, p_type text DEFAULT 'range'
, p_epoch text DEFAULT 'none'
, p_premake int DEFAULT 4
, p_start_partition text DEFAULT NULL
, p_default_table boolean DEFAULT true
, p_automatic_maintenance text DEFAULT 'on'
, p_constraint_cols text[] DEFAULT NULL
, p_template_table text DEFAULT NULL
, p_jobmon boolean DEFAULT true
, p_date_trunc_interval text DEFAULT NULL
, p_control_not_null boolean DEFAULT true
, p_time_encoder text DEFAULT NULL
, p_time_decoder text DEFAULT NULL
)
RETURNS booleanParameters
Parameter | Type | Required | Default | Description |
|
| Yes | None | The parent table of the partition set. It must be an existing table that is declared as partitioned and must include the schema name. |
|
| Yes | None | The control column used for partitioning, such as Note When the control column is of type |
|
| Yes | None | The partition interval. The parameter value should be passed as text. Valid values are:
|
|
| No |
| The partition type. Valid values are:
|
|
| No |
| Used when the partition key column is an integer representing time (epoch). Valid values are:
Note All table names will be based on time. In addition to creating a normal index on the control column, ensure you also create a time-based function-based index on the control column (such as |
|
| No |
| The number of partitions to create in advance. For example, for daily partitioning, Note Certain intervals may occasionally cause an extra partition to be created or one to be missed due to leap years, different month lengths, etc. This usually does not cause problems and should correct itself automatically (see Partition type and interval). If partitioning falls behind the value of |
|
| No |
| Lets you manually specify the starting value (timestamp or integer) for the first partition instead of having the system determine it automatically. Must be a valid timestamp (for time-based partitioning) or a positive integer (for numeric/ID-based partitioning) value. Note
|
|
| No |
| Whether to create a default partition for the partition set to receive data that does not belong to any child partition. |
|
| No |
| Whether this partition set is automatically maintained by the global
|
|
| No |
| An array of column names for which to add extra |
|
| No |
| Specifies a template table. Newly created partitions will inherit attributes (such as indexes and constraints) from this template. If not specified, one will be created automatically. |
|
| No |
| Used to align partition boundaries for non-standard time intervals. Valid values are the By default, For example, if you set a 9-week interval, by default |
|
| No |
| Whether to require the partition control column to be
|
|
| No |
| When the partition key is of type |
|
| No |
| When the partition key is of type |
create_sub_parent()
This function creates subpartitions for an existing partition set. This is a destructive operation because it requires deleting and recreating existing partitions to turn them into new parent tables.
If you plan to rely on table names for organization, we recommend using shorter table names for sub-partition sets. The suffix appended to the end of the table name is always guaranteed to exist, regardless of the partition type used by that partition set. Longer table names may cause the original parent table name to be truncated and may also truncate the top-level partition suffix. This truncation is automatic and ensures that the lowest-level partition suffix is preserved.
For the first level of subpartitions, the
p_parent_tableparameter value you initially passed tocreate_parent()should be exactly the same as the value passed tocreate_sub_parent(). To subpartition further, you should start passing a different value tocreate_sub_parent()(that is, a child table of the top-level partition set).
Syntax
create_sub_parent(
p_top_parent text
, p_control text
, p_interval text
, p_type text DEFAULT 'range'
, p_default_table boolean DEFAULT true
, p_declarative_check text DEFAULT NULL
, p_constraint_cols text[] DEFAULT NULL
, p_premake int DEFAULT 4
, p_start_partition text DEFAULT NULL
, p_epoch text DEFAULT 'none'
, p_jobmon boolean DEFAULT true
, p_date_trunc_interval text DEFAULT NULL
, p_control_not_null boolean DEFAULT true
, p_time_encoder text DEFAULT NULL
, p_time_decoder text DEFAULT NULL
)
RETURNS boolean
Parameters
Parameter | Type | Required | Default | Description |
|
| Yes | None | The name of the top-level parent table. This function will turn all partitions under this parent table into new subpartition parent tables. |
|
| Yes |
| A safety confirmation flag. Must be set to |
The remaining parameters (p_control, p_interval, etc.) have the same function as in create_parent(), but they define the policy for the subpartitions. For example, if you have an existing partition set partitioned by year and you want to partition each year's partition by day, you can use this function.
create_partition_time()/create_partition_id()
Manually create specific child partitions for a time-based or numeric/ID-based partition set. This is usually called automatically by run_maintenance(), but can also be used to create partitions on demand outside of the maintenance cycle.
Syntax
create_partition_time(
p_parent_table text
, p_partition_times timestamptz[]
, p_start_partition text DEFAULT NULL
)
RETURNS boolean
create_partition_id(
p_parent_table text
, p_partition_ids bigint[]
, p_start_partition text DEFAULT NULL
)
RETURNS booleanParameters
Parameter | Type | Required | Default | Description |
|
| Yes | None | The parent table for which to create child partitions. |
|
| Yes | None | An array of If the partition does not exist, it will be created. If it exists, the existing partition is used and the function will still exit normally. Note that the given value will be used as the lower bound of the partition and affects the partition's name. Therefore, ensure the given timestamp value is consistent with other partitions, otherwise you may encounter gaps where values are overwritten. |
|
| Yes | None | An array of If the partition does not exist, it will be created. If it exists, the existing partition is used and the function will still exit normally. Note that the given value will be used as the lower bound of the partition and affects the partition's name. Therefore, ensure the given integer value is consistent with other partitions, otherwise you may encounter gaps where values are overwritten. |
|
| No |
| Used in subpartitioning scenarios to specify the starting value of the subpartition. |
Migrate data
These functions are used to migrate historical data or data that has mistakenly entered the default partition into the correct partitions.
partition_data_time()/partition_data_id()
Migrates data from the parent table, default partition, or a specified source table to the corresponding partitions. If the target partition does not exist, the function automatically creates it. It can also move data that was inserted into the default table.
To automatically partition a large amount of data, we recommend using the
partition_data_procprocedure to commit data in smaller batches. This will effectively reduce problems caused by long-running transactions and data occupancy.For sub-partition sets, you must partition data layer by layer, starting from the highest level. This means you must first run this function, then run
create_sub_parent()to create additional partition levels. Subsequently, run this function again on each newly created sub-parent table.
Syntax
partition_data_time(
p_parent_table text
, p_batch_count int DEFAULT 1
, p_batch_interval interval DEFAULT NULL
, p_lock_wait numeric DEFAULT 0
, p_order text DEFAULT 'ASC'
, p_analyze boolean DEFAULT true
, p_source_table text DEFAULT NULL
, p_ignored_columns text[] DEFAULT NULL
)
RETURNS bigint
partition_data_id(
p_parent_table text
, p_batch_count int DEFAULT 1
, p_batch_interval bigint DEFAULT NULL
, p_lock_wait numeric DEFAULT 0
, p_order text DEFAULT 'ASC'
, p_analyze boolean DEFAULT true
, p_source_table text DEFAULT NULL
, p_ignored_columns text[] DEFAULT NULL
)
RETURNS bigint
Parameters
Parameter | Type | Required | Default | Description |
|
| Yes | None | The parent table of the partition set (must include the schema name). |
|
| No |
| The number of batches to process in a single function call. |
|
| No |
| The size of the data interval to migrate in each batch. If not specified, the partition table's own interval is used. Important If moving data from the default table, this value must not be smaller than the partition interval. If moving data from a default table source that is not part of a partition set, you can set this interval smaller than the partition interval to help avoid moving large amounts of data in a long-running transaction. |
|
| No |
| The number of seconds to wait for a row lock. |
|
| No |
| The order of data migration.
|
|
| No |
| Whether to run |
|
| No |
| Specifies a source table from which to move data into the partition set. If not specified, data is migrated from the default partition. |
|
| No |
| An array of column names to ignore during data migration. This is mainly used to handle columns with the |
partition_data_proc()
This function is a stored procedure that calls the partition_data_* functions in a loop with independent transaction batches. We recommend using this procedure when migrating large amounts of data to avoid long transactions and lock contention.
Syntax
partition_data_proc (
p_parent_table text
, p_loop_count int DEFAULT NULL
, p_interval text DEFAULT NULL
, p_lock_wait int DEFAULT 0
, p_lock_wait_tries int DEFAULT 10
, p_wait int DEFAULT 1
, p_order text DEFAULT 'ASC'
, p_source_table text DEFAULT NULL
, p_ignored_columns text[] DEFAULT NULL
, p_quiet boolean DEFAULT false
)Parameters
Parameter | Type | Required | Default | Description |
|
| Yes | None | The parent table of the partition set. |
|
| No |
| The number of times to loop. If not set, all data in the source table will be processed. |
|
| No |
| Passed as |
|
| No |
| The number of seconds to wait for a row lock. |
|
| No |
| The number of times to try to acquire a lock. |
|
| No |
| The number of seconds to pause after each batch commit to reduce write load. |
|
| No |
| The order of data migration.
|
|
| No |
| The data source table. |
|
| No |
| Columns to ignore during data migration. |
|
| No |
| Procedures cannot return values, so by default they issue a |
Maintain partitions
These functions are used for daily partition management, monitoring, and information querying.
run_maintenance()
The core maintenance function of pg_partman, which should be called periodically via cron or a Background Worker. It is responsible for automatically creating new partitions and running data retention policies.
Syntax
run_maintenance(
p_parent_table text DEFAULT NULL
, p_analyze boolean DEFAULT false
)
RETURNS voidParameters
Parameter | Type | Required | Default | Description |
|
| No |
| The partition set to maintain.
|
|
| No |
| Whether to run For large partition sets, |
run_maintenance_proc()
The procedure version of run_maintenance(). It uses independent transactions for the maintenance of each partition set, which can reduce lock contention caused by long transactions when managing many partition sets. The Background Worker does not use this procedure. It uses the standard run_maintenance() function.
Syntax
run_maintenance_proc(
p_wait int DEFAULT 0
, p_analyze boolean DEFAULT NULL
)Parameters
Parameter | Type | Required | Default | Description |
|
| No |
| The number of seconds to wait between maintaining each partition set. |
|
| No |
| Same as |
check_default()
Checks for data in all default partitions managed by pg_partman and returns the default tables that contain data, along with their row counts. This is an important means of monitoring whether partitioning is working correctly. You can use partition_data_time() and partition_data_id() to move data from these parent/default tables to the correct partitions.
Syntax
check_default(
p_exact_count boolean DEFAULT true
)Parameters
Parameter | Type | Required | Default | Description |
|
| No |
| Whether to return the exact row count. Set to |
show_partitions()
Lists all partitions for a given partition set.
Tables are returned in the logical order of the partition interval, not in the native sort order of their names.
Syntax
show_partitions(
p_parent_table text
, p_order text DEFAULT 'ASC'
, p_include_default boolean DEFAULT false
)
RETURNS TABLE (
partition_schemaname text
, partition_tablename text
)Parameters
Parameter | Type | Required | Default | Description |
|
| Yes | None | The parent table of the partition set. |
|
| No |
| The sort order of the results.
|
|
| No |
| Whether to include the default partition in the results. |
show_partition_name()
Returns the name of the child partition to which a given value should belong, regardless of whether the partition actually exists.
Syntax
show_partition_name(
p_parent_table text
, p_value text
, OUT partition_schema text
, OUT partition_table text
, OUT suffix_timestamp timestamptz
, OUT suffix_id bigint
, OUT table_exists boolean
)
RETURNS recordParameters
Parameter | Type | Required | Default | Description |
|
| Yes | None | The parent table of the partition set. |
|
| Yes | None | A time or numeric/ID value (passed as text) used to determine the partition it belongs to. Note If using epoch time partitioning, give the timestamp value, not the integer epoch value (you can use |
|
| - | - | The partition schema name. |
|
| - | - | The partition name. |
|
| - | - | The partition name suffix. |
|
| - | - | Whether the partition actually exists. |
show_partition_info()
Returns the boundary value information for a partition based on its name.
Syntax
show_partition_info(
p_child_table text
, p_partition_interval text DEFAULT NULL
, p_parent_table text DEFAULT NULL
, OUT child_start_time timestamptz
, OUT child_end_time timestamptz
, OUT child_start_id bigint
, OUT child_end_id bigint
, OUT suffix text
)
RETURNS recordParameters
Parameter | Type | Required | Default | Description |
|
| Yes | None | The name of the partition (must include the schema name). |
|
| No |
| Specifies the partition interval to calculate the boundaries. If not provided, it is looked up from the |
|
| No |
| Provide the parent table name to optimize internal queries. |
|
| - | - | If the partition set is time-based, the function returns values for these output parameters. Otherwise, it returns NULL. Note The start value ( |
|
| - | - | If the partition set is integer-based, the function returns values for these output parameters. Otherwise, it returns NULL. Note The start value ( |
|
| - | - | Outputs the text part appended to the partition name that identifies its content (excluding |
apply_constraints()/drop_constraints()/reapply_constraints_proc()
Manages the extra CHECK constraints defined in the constraint_cols column of part_config.
apply_constraints(): Applies constraints to the partitions of a given partition set. All constraint names are prefixed withpartmanconstr_.NoteYou do not need to call this function manually to maintain custom constraints. Adding constraints to old partitions is managed automatically when new partitions are created.
If a
pg_partmanconstraint already exists on a partition, the function skips the existing constraint to avoid duplicate creation.If all given columns have NULL values, no constraint is created.
If a partition parameter is provided, the constraint is applied only to that partition.
If no partition parameter is provided, the constraint is applied to the last partition that is older than the
optimize_constraintvalue. For example, ifoptimize_constraintis 30, the constraint will be applied to the 31st partition before the current one (provided that partition pre-creation is up to date).To apply constraints to all old partitions, use the
reapply_constraints_procprocedure. This method has options to simplify constraint application with minimal performance impact.p_job_idis for internal use, allowing logs to be merged into the original job that called this function.
apply_constraints( p_parent_table text , p_child_table text DEFAULT NULL , p_analyze boolean DEFAULT FALSE , p_job_id bigint DEFAULT NULL ) RETURNS voiddrop_constraints(): Drops the constraints created bypg_partmanfor the columns configured inpart_config. This makes it easy to remove constraints when you need to edit old data and the constraints prevent it.NoteOnly drops constraints on the given partition and configured columns that start with
partmanconstr_*.To drop constraints on all partitions, use the
reapply_constraints_procprocedure. This method has options to simplify constraint dropping with minimal performance impact.The
p_debugparameter will display the constraint drop statements used.
drop_constraints( p_parent_table text , p_child_table text , p_debug boolean DEFAULT false ) RETURNS voidreapply_constraints_proc(): Applies or drops constraints on all partitions in batches, committing in batches to avoid lock contention.reapply_constraints_proc( p_parent_table text , p_drop_constraints boolean DEFAULT false , p_apply_constraints boolean DEFAULT false , p_wait int DEFAULT 0 , p_dryrun boolean DEFAULT false )
Parameter | Type | Required | Default | Description |
|
| Yes | None | The parent table of the created partition set. |
|
| No |
| Drops all constraints managed by |
|
| No |
| Applies constraints on the configured columns to all partitions older than the |
|
| No |
| The number of seconds to wait after dropping or adding constraints on one table before continuing to the next. |
|
| No |
| Runs this procedure without actually running the drop/apply constraint commands. Only outputs which tables the commands would be run on as |
Other maintenance functions
dump_partitioned_table_definition(): Generates acreate_parent()statement to rebuild the partition set configuration and anUPDATEstatement to set the additional parameters stored inpart_config.NoteCurrently only supports single-level partition sets.
p_ignore_templateis used because the generated SQL requires the template table to be created before it can be run. If you have not made any modifications to the template table, it is safe to passtruehere, and the generated SQL will tellpg_partmanto generate a new template table. However, to be safe, we recommend usingpg_dumpto export the template tables and restore them before using the generated SQL to preserve any template override settings.
dump_partitioned_table_definition( p_parent_table text, p_ignore_template_table boolean DEFAULT false ) RETURNS textpartition_gap_fill(): Checks for and fills any gaps that may exist in the partition sequence. Starting from the current minimum partition, it fills any gaps encountered based on the partition interval, up to the current maximum partition. Returns the number of partitions created. If no partitions are created, it returns 0.partition_gap_fill( p_parent_table text ) RETURNS integerreapply_privileges(): Reapplies the ownership and permissions of the parent table to all partitions.NotePermissions held by the parent table are granted to all partitions. Permissions not held by the parent table are revoked (
CASCADE).The permissions checked are:
SELECT,INSERT,UPDATE,DELETE,TRUNCATE,REFERENCES, andTRIGGER.This can be a very time-consuming operation for large partition sets, so it is designed as a standalone function. The function only applies permissions that are different from the parent table, but it still requires system catalog lookups and comparisons for each child partition and all its individual permissions.
reapply_privileges( p_parent_table text ) RETURNS voidstop_sub_partition(): Stops automatic maintenance for a sub-partition set while retaining maintenance for its parent partition. By default, if you undo a partition that is also a partition itself, it will not prevent other partitions of that parent partition set from continuing to be sub-partitioned unless you also undo its parent partition set. To address this situation, where you may not want to delete the parent table but do not want to continue creating subpartitions, you can use this function.NoteThis function only removes the parent table entry from the
part_config_subtable.stop_sub_partition( p_parent_table text ) RETURNS boolean
Destroy partitions
These functions are used to undo a partitioning structure or to delete old partitions according to a retention policy.
undo_partition()
Moves data from all partitions in a partition set to a single target table and removes the partitioning structure. This is a data moving operation. Use it with caution.
When this function is run, the
undo_in_progresscolumn in the configuration table is set totrue. This will stop all partition creation and retention management.By default, partitions are not dropped (
DROP), but are detached (DETACH). This will make the original partitions empty, standalone tables.If no batch parameters are manually set, each run of the function will move all data from one partition to the target table.
Once all partitions have been un-inherited/dropped, the configuration data is automatically removed from
pg_partman.For sub-partitioned tables, you need to start the un-partitioning operation from the lowest-level parent table and then work your way up layer by layer.
Syntax
undo_partition(
p_parent_table text
, p_target_table text
, p_loop_count int DEFAULT 1
, p_batch_interval text DEFAULT NULL
, p_keep_table boolean DEFAULT true
, p_lock_wait numeric DEFAULT 0
, p_ignored_columns text[] DEFAULT NULL
, p_drop_cascade boolean DEFAULT false
, OUT partitions_undone int
, OUT rows_undone bigint)
RETURNS record
Parameters
Parameter | Type | Required | Default | Description |
|
| Yes | None | The parent table of the partition set. Must include the schema name and must match the parent table name already configured in |
|
| Yes | None | A table with a schema name that will receive the data from the old partition tables. |
| int | No |
| The number of batches to process in a single call. |
|
| No |
| The size of the data interval to move in each batch. Can be set smaller than the partition interval to break up very large partitions into smaller commit batches. If not specified or if the provided interval is larger than the partition interval, the configured partition interval is used. Note that the value of this parameter must be passed as text. |
|
| No |
| Whether to keep or drop the empty partition after data migration.
Note At least two batches are required to actually remove a table from the partition set. |
|
| No |
| The number of seconds to wait for a table or row to be unlocked before timing out. A value of 0 means wait indefinitely. |
|
| No |
| This option allows filtering out specific columns when moving data from a partition to the target table. This is usually only needed when using columns of type |
|
| No |
| Whether to cascade drop subpartitions. Only takes effect when Note A cascade drop will cause all subpartition tables to be dropped when the parent table is dropped. |
undo_partition_proc()
The procedure version of undo_partition(). We recommend using this procedure when undoing partitions that contain a large amount of data, to commit in batches and avoid long transactions.
Syntax
undo_partition_proc(
p_parent_table text
, p_target_table text DEFAULT NULL
, p_loop_count int DEFAULT NULL
, p_interval text DEFAULT NULL
, p_keep_table boolean DEFAULT true
, p_lock_wait int DEFAULT 0
, p_lock_wait_tries int DEFAULT 10
, p_wait int DEFAULT 1
, p_ignored_columns text[] DEFAULT NULL
, p_drop_cascade boolean DEFAULT false
, p_quiet boolean DEFAULT false
)Parameters
Its parameters are similar to undo_partition(), with some additional parameters to control batch processing behavior.
Parameter | Type | Required | Default | Description |
|
| No |
| Passed as the |
|
| No |
| Sets the number of times the procedure will try to wait for the time set by |
|
| No |
| Causes the procedure to pause for the specified number of seconds between each commit (batch) to reduce write load. |
|
| No |
| Sub-procedures cannot return values, so by default they issue a |
drop_partition_time()/drop_partition_id()
Manually triggers the deletion or detachment of old tables based on a retention policy. By default, old tables are only un-inherited and not actually dropped. We recommend using the run_maintenance() function with a configured retention policy to automatically drop old tables, rather than calling this function directly.
Syntax
drop_partition_time(
p_parent_table text
, p_retention interval DEFAULT NULL
, p_keep_table boolean DEFAULT NULL
, p_keep_index boolean DEFAULT NULL
, p_retention_schema text DEFAULT NULL
, p_reference_timestamp timestamptz DEFAULT CURRENT_TIMESTAMP
)
RETURNS int
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 intParameters
Parameter | Type | Required | Default | Description |
|
| Yes | None | The parent table of the partition set. |
|
| No |
| Manually specifies the retention policy. If not provided, the configuration from the |
|
| No |
| Whether to keep or drop the table when un-inheriting.
|
|
| No |
| Whether to keep or drop indexes when un-inheriting a partition.
|
|
| No |
| Moves expired tables to this schema for archiving instead of dropping them. |
|
| No |
| (Time-based partitioning only) Specifies a different reference timestamp to determine which partitions should be affected. |
Configuration tables
All behavior of pg_partman is driven by two core configuration tables. When you create a partition set using the create_parent() or create_sub_parent() functions, the corresponding configuration is automatically inserted into these tables. You can also directly modify records in these tables to dynamically adjust the behavior of a partition set.
part_config
This table is the main configuration center for pg_partman, storing the partitioning policies and maintenance settings for all top-level parent tables.
Configuration item (column name) | Data type | Default value | Description |
|
| None | The name of the parent table of the partition set. |
|
| None | The name of the control column used as the basis for partitioning. It must be a time or integer type column. |
|
| None | The partition interval. For time-based partitioning, it is an |
|
|
| The partition type. Currently supports |
|
|
| The number of partitions to pre-create. This means that, in addition to the current partition, this many future partitions are always kept available. |
|
|
| Controls whether this partition set is automatically managed by the global |
|
| None | The template table used for new partitions. Newly created partitions will inherit indexes, constraints, and other attributes that cannot be inherited from the parent table from this template. |
|
|
| The data retention policy. For time-based partitioning, it is an |
|
|
| When old partitions expire, move them to this specified schema for archiving instead of deleting them. This option takes precedence over |
|
|
| Controls how expired partitions are handled.
|
|
|
| Controls whether to keep the indexes on the table when it is detached.
|
|
|
| Controls whether to keep the detached table in its original logical replication publication.
|
|
|
| Used when the partition key column is an integer representing time (epoch).
|
|
|
| An array of column names. |
|
|
| Defines how old a partition must be before applying the constraints defined in |
|
|
| Controls whether the constraints defined by
|
|
|
| Whether to continue creating new time partitions even when no new data is written.
|
|
| None | The date-time formatting string used to generate the suffix for partition names (for example, |
|
|
| Whether to inherit permissions and ownership from the parent table to all partitions. Enable this only if you need direct access to partitions.
|
|
|
| Whether maintenance tasks ignore data in the default partition when deciding whether to create new partitions. Temporarily set this to |
|
|
| Defines the execution order when
|
|
|
| The name of the logical replication subscription to refresh when maintenance runs. If your partition set subscribes to a publication that adds/removes tables and you need the partition set to be aware of these changes, you must use this option to specify the subscription name. Otherwise, unless refreshed by other means, the subscription will never be aware of new tables added by the publisher. For more information, see ALTER SUBSCRIPTION. |
|
|
| Marks whether the sub-partition set has been fully created. When there are many sub-partition sets, this allows |
|
|
| When |
|
|
| Records the timestamp of the last successful maintenance run for this partition set. This can be used as a monitoring metric to ensure partition maintenance is running correctly. |
part_config_sub
This table stores the configuration for subpartitions. Its structure is basically the same as part_config, but with one key difference: it uses the sub_parent column to identify its immediate parent table.
sub_parent(text): The name of the parent table of the sub-partition set. This table itself is a partition of the top-level parent table.Other columns: All other columns in the table (such as
partition_interval,retention,premake, etc.) have the same meaning as inpart_config, but their scope is the current sub-partition set.
You do not need to review the definitions of all columns here again. Just understand that when pg_partman maintains a sub-partition set, it looks up the configuration row in the part_config_sub table using sub_parent as the key and applies the policy defined in that row.