When data tables in PolarDB for PostgreSQL (Compatible with Oracle) grow, manually managing partitioned tables becomes complex and time-consuming. pg_partman is a PostgreSQL extension that addresses this issue by automating partition management. The extension automatically creates and maintains table partitions based on time or serial IDs and enforces data retention policies. This simplifies the lifecycle management of partitioned tables, which improves database maintainability and query performance.
Applicability
Supported versions of PolarDB for PostgreSQL (Compatible with Oracle): Oracle syntax compatibility 2.0 with minor engine version 2.0.14.19.40.0 or later.
You can view the minor engine version in the console or by running the SHOW polardb_version; statement. If your version does not meet the requirements, you must 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 numeric/ID values. It is based entirely on the built-in declarative partitioning feature of PostgreSQL (since pg_partman version 5.0.1) and no longer uses the legacy trigger-based method.
The working mechanism is 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 can be written seamlessly.
Data retention policy: Automatically detaches (DETACH) or drops (DROP) expired data partitions. This manages the data lifecycle and storage costs.
Default partition: Creates a default partition for each partition set to catch any data that does not belong to an existing child partition. This prevents data loss. You can use associated functions to migrate this data to the correct partitions.
Subpartitioning support: Supports multi-level partitioning. For example, you can subdivide annual partitions into daily partitions to meet complex data organization needs.
Quick start
This section provides a complete example of how to partition a log table by day to help you get started with pg_partman. In this example, you will create a log table named log_table, create a new partition daily based on the created_at field, and configure a policy to automatically delete data that is older than seven days.
Step 1: Install the extension and create a 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 will be a partitioned table,
-- using 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, which means a new partition is created daily.
SELECT create_parent(
p_parent_table := 'public.log_table',
p_control := 'created_at',
p_interval := '1 day'
);After successful execution, 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 created_at value.
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 a retention policy and verify cleanup
Set a data retention policy so that pg_partman automatically cleans up expired data. Configure log_table to retain data for the last two days 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', -- Set the retention period to 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 have been deleted.
SELECT partition_schemaname, partition_tablename FROM show_partitions('public.log_table', 'DESC');At this point, you have successfully configured a log table that is partitioned daily and cleaned up automatically.
Precautions
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 properties that cannot be directly inherited from the parent table, such as specific indexes or theUNLOGGEDstatus. Changes to the template table affect only newly created partitions. Existing partitions must be updated manually.Time zones: When you partition based on time, ensure that the database system, clients, and all environments that call maintenance tasks use a consistent time zone. We recommend that you use UTC to avoid partition creation failures or skips due to issues such as daylight saving time (DST).
Subpartitioning:
The main function of subpartitioning is data organization and lifecycle management. Its effect on performance improvement is limited. If you want to optimize performance, we recommend that you adjust the partition interval instead of relying on subpartitioning. Too many subpartitions increase management overhead and may exhaust
max_locks_per_transactionresources.Subpartitioning does not support logical replication (
PUBLICATION/SUBSCRIPTION). If you plan to use logical replication on a partitioned table, avoid using the subpartitioning feature.Subpartition creation (
create_sub_parent) is a destructive operation. It deletes and rebuilds existing partitions to add the next level of partitioning. Before execution, 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 accommodate suffixes. However, if parent table names are too long and similar, it may cause partition name conflicts across different partition sets. We recommend that you keep parent table names concise.UNIQUE constraints: You cannot create global unique constraints or primary keys 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: Data that falls into the default partition must be handled with care.
pg_partmancreates a default partition to receive data that does not belong to 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.Undoing partitioning: Executing
undo_partition()pauses all partition maintenance. Until the undo operation is complete,run_maintenance()will not create new partitions or enforce 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 situation, we recommend that you use therun_maintenance_proc()procedure. It commits the transaction after it maintains each partition set, which reduces lock contention from long-running transactions.
Best practices
Scenario 1: Daily partitioned log table with automatic 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 daily, retain data for 30 days, and automatically archive expired data to another schema instead of deleting it directly.
Procedure
Create a 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 for daily partitioning. 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 partitions that are older than 30 days to the
archiveschema for subsequent 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 entirely older than 30 days, such asaudit_logs_p2023_10_01, will be detached from the primary 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 a primary key, such as for user or product tables, and want to separate hot and cold data through partitioning.
Business scenario
Partition the orders table by order_id, with each partition containing 10 million IDs.
Decision advice
The choice of p_interval is critical. You can estimate it based on the growth rate of your 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 a 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 for partitioning 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 a 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 order records. -- Assume the current maximum order_id is 100,000,000. -- This policy will delete 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 a large existing table to a partitioned table
This scenario is for smoothly migrating a large, existing monolithic table, such as a terabyte-scale table, to a partitioned structure managed by pg_partman.
Business scenario
Migrate a 1 TB sensor_data table to a monthly partitioned table and minimize the impact on online services.
Core tool
The partition_data_proc() procedure. It can migrate data in small batches and independent transactions, which avoids long-running table locks and transaction backlogs.
Procedure
Preparations: Create a 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');Execute data migration: 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 setting it to be less than or equal to the partition interval. Here it is set to '1 day', meaning data for each day is processed in one transaction. -- p_wait: The waiting time (in seconds) between each batch, 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 will loop until all data in the
sensor_data_sourcetable has been migrated. During the migration,pg_partmanwill automatically create 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 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 when the first partition to create is determined.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 previous partitions may be created than expected, and not all future partitions may be created. The first run ofrun_maintenance()will create the missing future partitions. This is due to the need to support custom time intervals. For intervals of 24 hours or more, the setup will proceed as expected.
For intervals of 100 years or more, the extension will use the actual starting point of the century or millennium to determine partition names and constraint rules. For example, the 21st century and the 3rd millennium started on January 1, 2001 (not 2000). This also means there is no year 0.
Numeric/ID-based: Accepts an integer that represents 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 Monday is the start day, we recommend that you explicitly specify 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', so thatpg_partmanmanages it as time-based partitioning.
Sub-partitioning
Subpartitioning lets you create finer-grained partitions on existing partitions to form a multi-level partition structure, such as partitioning by year and then subpartitioning 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, subpartitioning is usually of limited effectiveness. We recommend that you prioritize the adjustment of the top-level partition interval.
Subpartition creation (create_sub_parent) is a destructive operation. It deletes and rebuilds existing partitions to add the next level of partitioning. Before execution, we recommend that you back up the relevant data.
Data retention policy
pg_partman automatically enforces data retention policies through run_maintenance() to manage the lifecycle of old partitions. The core configuration parameters are as follows:
Parameter | Description | Example |
| The retention period.
|
|
|
|
|
| Moves the detached old partition to a specified archive schema instead of keeping it in the original schema or dropping it. This setting takes precedence over |
|
| Whether to keep the indexes on an old partition when it is detached. The default is |
|
For subpartition sets, when a partition of a parent table is dropped, if that partition is itself a partitioned table, the drop operation will cascade (CASCADE) to all lower-level partitions in the entire inheritance tree and delete 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 explicitly exclude certain partitions, the query optimizer will skip scanning those partitions.
pg_partman provides the ability to add CHECK constraints on non-partition key columns to enhance the effect of 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 older than 30 partition intervals.constraint_valid: Adding a constraint to a table may conflict with existing data in the table and may also causepg_partman's maintenance operations to take a long time. You can set these constraints to be not valid when they are created. Although this allows the constraint creation to be almost instantaneous, constraint exclusion cannot be used until it is 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, and 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 in sub-partitioning. It can execute normally on the first level of partitions, but whether it is effective in deeper sub-partitioned table sets depends on the combination of partition intervals used and the setting ofoptimize_constraint. For example, when a weekly partition is further subdivided into daily partitions, and the daily partition'soptimize_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 declarative partitioning has incomplete support for inheriting certain object attributes, such as primary keys or unique indexes on non-partition keys and autovacuum settings, 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.
Attribute | Inherited from parent | Inherited from template |
Primary key on non-partition columns | - | and later |
Unique index on non-partition columns | - | and later |
Tablespace for unique index on non-partition columns | - | and later |
Relation-specific options (autovacuum, etc.) | - | and later |
UNLOGGED table status* | - | and later |
Non-unique indexes | and later | - |
Permissions/Ownership | 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 indexes 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 make changes, 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 occurs only at partition creation time and is not automatically applied after changes. You can reapply them 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 supports automatic generation of new sequence values only when data is inserted through the parent table. It is not supported when you insert data directly into partitions.
The template table feature is a temporary solution designed to accelerate the adoption of declarative partitioning. As PostgreSQL core features improve,
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. You must plan for related adjustments when you upgrade to a major version.If you want to use the
REPLICA IDENTITYattribute with theUSING INDEXclause to support logical replication, note that this feature is supported only if the required index is created on the actual parent table, not on the template table. Because you cannot control theREPLICA IDENTITYsettings on the parent and template tables, it is impossible to determine which is the correct identity. To maintain consistency with other identity inheritance methods (FULL and NONE), we recommend that you select only the parent table as the source.PostgreSQL handles the
UNLOGGEDattribute inconsistently on the parent table of a partition set. Therefore,pg_partmanuses a template table to manage theUNLOGGEDstatus. When anALTERcommand is executed, the attribute on the parent table does not actually change, so new partitions continue to use the attribute from before the change. This means that you cannot change a partition set fromUNLOGGEDtoLOGGEDand have all future partitions inherit this change. Because this attribute is managed through the template table, changing the attribute on the template table causes newly created partitions to inherit the change. However, you must still change existing partitions manually. For more information, see Unable to alter partitioned table to set logged.
O&M and management
Automatic maintenance
The core maintenance tasks of pg_partman, creating new partitions and enforcing retention policies, are performed by the run_maintenance() function or the run_maintenance_proc() procedure. In PolarDB for PostgreSQL (Compatible with Oracle), we recommend that you use the built-in Background Worker to automate this process. It automatically calls run_maintenance_proc() at the interval you set.
The Background Worker is disabled by default. To enable it, go to Quota Center, find the quota ID polardb_pg_pg_cron, and in the Actions column, click Apply to request the quota.
Manual maintenance
In some cases, you may need to trigger maintenance manually, such as when you are debugging or when you need fine-grained control over a specific table.
CALL run_maintenance_proc();We recommend that you use this procedure. It performs 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 want to perform maintenance only 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 normally 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
-- for more than 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 during its run.
All options passed to this function must be configured correctly. You must apply all defaults, indexes, constraints, permissions, and ownership to the parent table so that they propagate to the child tables. For instructions on how to handle unique indexes and other table properties, see Template table and attribute inheritance.
By default, a default partition and a template table are created, unless otherwise configured.
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. Must be an existing, partitioned table, 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 regular 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. For more information, 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 it determined automatically by the system. 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 properties, 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 the partition control column is required to be
|
|
| No |
| When the partition key is of type |
|
| No |
| When the partition key is of type |
create_sub_parent()
This function creates a subpartition for an existing partition set. This is a destructive operation because it requires deleting and rebuilding existing partitions to transform them into new parent tables.
If you plan to rely on table names for organization, we recommend that you use shorter table names for subpartition 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 subpartitioning, 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 different values tocreate_sub_parent(), that is, the child tables 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 transform all partitions under this parent table into new subpartition parent tables. |
|
| Yes |
| A safety confirmation flag. Must be set to |
The remaining parameters, such as p_control or p_interval, 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 then want to partition each year partition by day, you can use this function.
create_partition_time()/create_partition_id()
Manually create specific child partitions for time-based or numeric/ID-based partition sets. 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 will affect the partition's name. Therefore, ensure that the given timestamp value is consistent with other partitions, otherwise you may encounter gaps with value overlaps. |
|
| 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 will affect the partition's name. Therefore, ensure that the given integer value is consistent with other partitions, otherwise you may encounter gaps with value overlaps. |
|
| No |
| Used in subpartitioning scenarios to specify the starting value of a subpartition. |
Migrate data
These functions are used to migrate historical data or data that has mistakenly entered a default partition into the correct partitions.
partition_data_time()/partition_data_id()
Migrates data from a parent table, default partition, or a specified source table into the corresponding partitions. If the target partition does not exist, the function automatically creates it. It can also fix data that has been inserted into the default table.
To automatically partition a large amount of data, we recommend that you use the
partition_data_procprocedure to commit data in smaller batches. This will effectively reduce problems caused by long-running transactions and data volume.For subpartition sets, you must partition the data layer by layer, starting from the highest level. This means you must first run this function, then execute
create_sub_parent()to create additional partition levels. Subsequently, you must 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 the 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 for handling 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 that you use this procedure when you migrate large amounts of data to avoid long-running 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 |
| The columns to ignore during data migration. |
|
| No |
| Procedures cannot return values, so by default they emit a |
Maintain partitions
These functions are used for routine partition management, monitoring, and information queries.
run_maintenance()
The core maintenance function of pg_partman, which should be called periodically using cron or a Background Worker. It is responsible for automatically creating new partitions and enforcing 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-running transactions when you manage 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 the presence of 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 an 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()
Given a value, returns the name of the child partition to which it 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()
Given a partition name, returns information about its boundary values.
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 are NULL, 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, which allows 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 clean up constraints when you need to edit old data and the constraints do not allow 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 in bulk on all partitions, 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 applying a constraint drop or add to a table before continuing to the next table. |
|
| No |
| Runs this procedure without actually executing 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, this function supports only single-level partition sets.
The SQL generated by
p_ignore_templaterequires the template table to be created before running. 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, for safety, we recommend that you usepg_dumpto export the template tables and restore them before you use 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 smallest partition, it fills any encountered gaps based on the partition interval, up to the current largest partition. It 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 owned by the parent table will be granted to all partitions. Permissions not owned by the parent table will be revoked (
CASCADE).The permissions checked are:
SELECT,INSERT,UPDATE,DELETE,TRUNCATE,REFERENCES, andTRIGGER.For large partition sets, this can be a very time-consuming operation, so it is designed as a standalone function. The function applies only 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 subpartition set while it preserves 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 subpartitioned 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 removes only 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 partition structure or delete old partitions according to a retention policy.
undo_partition()
Moves data from all partitions of a partition set to a single target table and removes the partition structure. This is a data movement operation, so 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 set manually, each run of the function will move all data from one partition to the target table.
Once all partitions are detached or dropped, the configuration data is automatically removed from
pg_partman.For subpartitioned tables, you need to start the undo 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 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 effective 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 that you use this procedure when you undo partitions that contain a large amount of data, to commit in batches and avoid long-running 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 attempt 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 emit 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 detached, not actually deleted. We recommend that you use the run_maintenance() function with a configured retention policy to automatically delete 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 it.
|
|
| 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 the 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 and stores 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. It means that, in addition to the current partition, this many future partitions are always maintained. |
|
|
| Controls whether this partition set is automatically managed by the global |
|
| None | The template table 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 an old partition expires, move it to this specified schema for archiving instead of deleting it. 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 the permissions and ownership of 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 subpartition set has been fully created. When there are many subpartition 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 normally. |
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 subpartition 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, andpremake, have the same meaning as inpart_config, but their scope is the current subpartition set.
You do not need to review the definitions of all columns. Essentially, when pg_partman maintains a subpartition 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.