GithubHelp home page GithubHelp logo

postgrespro / pg_pathman Goto Github PK

View Code? Open in Web Editor NEW
578.0 62.0 67.0 4.84 MB

Partitioning tool for PostgreSQL

License: Other

Makefile 0.43% C 74.01% PLpgSQL 18.87% Shell 0.61% Python 6.07%
postgresql range hash fdw partition-table partitioning pathman query-optimization runtimeappend customscan

pg_pathman's Introduction

Build Status PGXN version codecov GitHub license

NOTE: this project is not under development anymore

pg_pathman supports Postgres versions [11..15], but most probably it won't be ported to later releases. Native partitioning is pretty mature now and has almost everything implemented in pg_pathman'; we encourage users switching to it. We are still maintaining the project (fixing bugs in supported versions), but no new development is going to happen here.

pg_pathman

The pg_pathman module provides optimized partitioning mechanism and functions to manage partitions.

The extension is compatible with:

  • PostgreSQL 12, 13;
  • PostgreSQL with core-patch: 11, 14, 15;
  • Postgres Pro Standard 11, 12, 13, 14, 15;
  • Postgres Pro Enterprise;

Take a look at our Wiki out there.

Overview

Partitioning means splitting one large table into smaller pieces. Each row in such table is moved to a single partition according to the partitioning key. PostgreSQL <= 10 supports partitioning via table inheritance: each partition must be created as a child table with CHECK CONSTRAINT:

CREATE TABLE test (id SERIAL PRIMARY KEY, title TEXT);
CREATE TABLE test_1 (CHECK ( id >= 100 AND id < 200 )) INHERITS (test);
CREATE TABLE test_2 (CHECK ( id >= 200 AND id < 300 )) INHERITS (test);

PostgreSQL 10 provides native partitioning:

CREATE TABLE test(id int4, value text) PARTITION BY RANGE(id);
CREATE TABLE test_1 PARTITION OF test FOR VALUES FROM (1) TO (10);
CREATE TABLE test_2 PARTITION OF test FOR VALUES FROM (10) TO (20);

It's not so different from the classic approach; there are implicit check constraints, and most of its limitations are still relevant.

Despite the flexibility, this approach forces the planner to perform an exhaustive search and to check constraints on each partition to determine whether it should be present in the plan or not. Large amount of partitions may result in significant planning overhead.

The pg_pathman module features partition managing functions and optimized planning mechanism which utilizes knowledge of the partitions' structure. It stores partitioning configuration in the pathman_config table; each row contains a single entry for a partitioned table (relation name, partitioning column and its type). During the initialization stage the pg_pathman module caches some information about child partitions in the shared memory, which is used later for plan construction. Before a SELECT query is executed, pg_pathman traverses the condition tree in search of expressions like:

VARIABLE OP CONST

where VARIABLE is a partitioning key, OP is a comparison operator (supported operators are =, <, <=, >, >=), CONST is a scalar value. For example:

WHERE id = 150

Based on the partitioning type and condition's operator, pg_pathman searches for the corresponding partitions and builds the plan. Currently pg_pathman supports two partitioning schemes:

  • RANGE - maps rows to partitions using partitioning key ranges assigned to each partition. Optimization is achieved by using the binary search algorithm;
  • HASH - maps rows to partitions using a generic hash function.

More interesting features are yet to come. Stay tuned!

Feature highlights

  • HASH and RANGE partitioning schemes;
  • Partitioning by expression and composite key;
  • Both automatic and manual partition management;
  • Support for integer, floating point, date and other types, including domains;
  • Effective query planning for partitioned tables (JOINs, subselects etc);
  • RuntimeAppend & RuntimeMergeAppend custom plan nodes to pick partitions at runtime;
  • PartitionFilter: an efficient drop-in replacement for INSERT triggers;
  • PartitionRouter and PartitionOverseer for cross-partition UPDATE queries (instead of triggers);
  • Automatic partition creation for new INSERTed data (only for RANGE partitioning);
  • Improved COPY FROM statement that is able to insert rows directly into partitions;
  • User-defined callbacks for partition creation event handling;
  • Non-blocking concurrent table partitioning;
  • FDW support (foreign partitions);
  • Various GUC toggles and configurable settings.
  • Partial support of declarative partitioning (from PostgreSQL 10).

Installation guide

To install pg_pathman, execute this in the module's directory:

make install USE_PGXS=1

Important: Don't forget to set the PG_CONFIG variable (make PG_CONFIG=...) in case you want to test pg_pathman on a non-default or custom build of PostgreSQL. Read more here.

Modify the shared_preload_libraries parameter in postgresql.conf as following:

shared_preload_libraries = 'pg_pathman'

Important: pg_pathman may cause conflicts with some other extensions that use the same hook functions. For example, pg_pathman uses ProcessUtility_hook to handle COPY queries for partitioned tables, which means it may interfere with pg_stat_statements from time to time. In this case, try listing libraries in certain order: shared_preload_libraries = 'pg_stat_statements, pg_pathman'.

It is essential to restart the PostgreSQL instance. After that, execute the following query in psql:

CREATE SCHEMA pathman;
GRANT USAGE ON SCHEMA pathman TO PUBLIC;
CREATE EXTENSION pg_pathman WITH SCHEMA pathman;

Done! Now it's time to setup your partitioning schemes.

Security notice: pg_pathman is believed to be secure against search-path-based attacks mentioned in Postgres documentation. However, if your calls of pathman's functions doesn't exactly match the signature, they might be vulnerable to malicious overloading. If in doubt, install pathman to clean schema where nobody except superusers have CREATE object permission to avoid problems.

Windows-specific: pg_pathman imports several symbols (e.g. None_Receiver, InvalidObjectAddress) from PostgreSQL, which is fine by itself, but requires that those symbols are marked as PGDLLIMPORT. Unfortunately, some of them are not exported from vanilla PostgreSQL, which means that you have to either use Postgres Pro Standard/Enterprise (which includes all necessary patches), or patch and build your own distribution of PostgreSQL.

How to update

In order to update pg_pathman:

  1. Install the latest stable release of pg_pathman.
  2. Restart your PostgreSQL cluster.
  3. Execute the following queries:
/* only required for major releases, e.g. 1.4 -> 1.5 */
ALTER EXTENSION pg_pathman UPDATE;
SET pg_pathman.enable = t;

Available functions

Module's version

pathman_version()

Although it's possible to get major and minor version numbers using \dx pg_pathman, it doesn't show the actual patch number. This function returns a complete version number of the loaded pg_pathman module in MAJOR.MINOR.PATCH format.

Partition creation

create_hash_partitions(parent_relid     REGCLASS,
                       expression       TEXT,
                       partitions_count INTEGER,
                       partition_data   BOOLEAN DEFAULT TRUE,
                       partition_names  TEXT[] DEFAULT NULL,
                       tablespaces      TEXT[] DEFAULT NULL)

Performs HASH partitioning for relation by partitioning expression expr. The partitions_count parameter specifies the number of partitions to create; it cannot be changed afterwards. If partition_data is true then all the data will be automatically copied from the parent table to partitions. Note that data migration may took a while to finish and the table will be locked until transaction commits. See partition_table_concurrently() for a lock-free way to migrate data. Partition creation callback is invoked for each partition if set beforehand (see set_init_callback()).

create_range_partitions(parent_relid    REGCLASS,
                        expression      TEXT,
                        start_value     ANYELEMENT,
                        p_interval      ANYELEMENT,
                        p_count         INTEGER DEFAULT NULL
                        partition_data  BOOLEAN DEFAULT TRUE)

create_range_partitions(parent_relid    REGCLASS,
                        expression      TEXT,
                        start_value     ANYELEMENT,
                        p_interval      INTERVAL,
                        p_count         INTEGER DEFAULT NULL,
                        partition_data  BOOLEAN DEFAULT TRUE)

create_range_partitions(parent_relid    REGCLASS,
                        expression      TEXT,
                        bounds          ANYARRAY,
                        partition_names TEXT[] DEFAULT NULL,
                        tablespaces     TEXT[] DEFAULT NULL,
                        partition_data  BOOLEAN DEFAULT TRUE)

Performs RANGE partitioning for relation by partitioning expression expr, start_value argument specifies initial value, p_interval sets the default range for auto created partitions or partitions created with append_range_partition() or prepend_range_partition() (if NULL then auto partition creation feature won't work), p_count is the number of premade partitions (if not set then pg_pathman tries to determine it based on expression's values). The bounds array can be built using generate_range_bounds(). Partition creation callback is invoked for each partition if set beforehand.

generate_range_bounds(p_start     ANYELEMENT,
                      p_interval  INTERVAL,
                      p_count     INTEGER)

generate_range_bounds(p_start     ANYELEMENT,
                      p_interval  ANYELEMENT,
                      p_count     INTEGER)

Builds bounds array for create_range_partitions().

Data migration

partition_table_concurrently(relation   REGCLASS,
                             batch_size INTEGER DEFAULT 1000,
                             sleep_time FLOAT8 DEFAULT 1.0)

Starts a background worker to move data from parent table to partitions. The worker utilizes short transactions to copy small batches of data (up to 10K rows per transaction) and thus doesn't significantly interfere with user's activity. If the worker is unable to lock rows of a batch, it sleeps for sleep_time seconds before the next attempt and tries again up to 60 times, and quits if it's still unable to lock the batch.

stop_concurrent_part_task(relation REGCLASS)

Stops a background worker performing a concurrent partitioning task. Note: worker will exit after it finishes relocating a current batch.

Triggers

Triggers are no longer required nor for INSERTs, neither for cross-partition UPDATEs. However, user-supplied triggers are supported:

  • Each inserted row results in execution of BEFORE/AFTER INSERT trigger functions of a corresponding partition.
  • Each updated row results in execution of BEFORE/AFTER UPDATE trigger functions of a corresponding partition.
  • Each moved row (cross-partition update) results in execution of BEFORE UPDATE + BEFORE/AFTER DELETE + BEFORE/AFTER INSERT trigger functions of corresponding partitions.

Post-creation partition management

replace_hash_partition(old_partition REGCLASS,
                       new_partition REGCLASS,
                       lock_parent   BOOLEAN DEFAULT TRUE)

Replaces specified partition of HASH-partitioned table with another table. The lock_parent parameter will prevent any INSERT/UPDATE/ALTER TABLE queries to parent table.

split_range_partition(partition_relid REGCLASS,
                      split_value     ANYELEMENT,
                      partition_name  TEXT DEFAULT NULL,
                      tablespace      TEXT DEFAULT NULL)

Split RANGE partition in two by split_value. Partition creation callback is invoked for a new partition if available.

merge_range_partitions(variadic partitions REGCLASS[])

Merge several adjacent RANGE partitions. Partitions are automatically ordered by increasing bounds; all the data will be accumulated in the first partition.

append_range_partition(parent_relid   REGCLASS,
                       partition_name TEXT DEFAULT NULL,
                       tablespace     TEXT DEFAULT NULL)

Append new RANGE partition with pathman_config.range_interval as interval.

prepend_range_partition(parent_relid   REGCLASS,
                        partition_name TEXT DEFAULT NULL,
                        tablespace     TEXT DEFAULT NULL)

Prepend new RANGE partition with pathman_config.range_interval as interval.

add_range_partition(parent_relid   REGCLASS,
                    start_value    ANYELEMENT,
                    end_value      ANYELEMENT,
                    partition_name TEXT DEFAULT NULL,
                    tablespace     TEXT DEFAULT NULL)

Create new RANGE partition for relation with specified range bounds. If start_value or end_value are NULL then corresponding range bound will be infinite.

drop_range_partition(partition TEXT, delete_data BOOLEAN DEFAULT TRUE)

Drop RANGE partition and all of its data if delete_data is true.

attach_range_partition(parent_relid    REGCLASS,
                       partition_relid REGCLASS,
                       start_value     ANYELEMENT,
                       end_value       ANYELEMENT)

Attach partition to the existing RANGE-partitioned relation. The attached table must have exactly the same structure as the parent table, including the dropped columns. Partition creation callback is invoked if set (see pathman_config_params).

detach_range_partition(partition_relid REGCLASS)

Detach partition from the existing RANGE-partitioned relation.

disable_pathman_for(parent_relid REGCLASS)

Permanently disable pg_pathman partitioning mechanism for the specified parent table and remove the insert trigger if it exists. All partitions and data remain unchanged.

drop_partitions(parent_relid REGCLASS,
                delete_data  BOOLEAN DEFAULT FALSE)

Drop partitions of the parent table (both foreign and local relations). If delete_data is false, the data is copied to the parent table first. Default is false.

To remove partitioned table along with all partitions fully, use conventional DROP TABLE relation CASCADE. However, care should be taken in somewhat rare case when you are running logical replication and DROP was executed by replication apply worker, e.g. via trigger on replicated table. pg_pathman uses pathman_ddl_trigger event trigger to remove the record about dropped table from pathman_config, and this trigger by default won't fire on replica, leading to inconsistent state when pg_pathman thinks that the table still exists, but in fact it doesn't. If this is the case, configure this trigger to fire on replica too:

ALTER EVENT TRIGGER pathman_ddl_trigger ENABLE ALWAYS;

Physical replication doesn't have this problem since DDL as well as pathman_config table is replicated too; master and slave PostgreSQL instances are basically identical, and it is only harmful to keep this trigger in ALWAYS mode.

Additional parameters

set_interval(relation REGCLASS, value ANYELEMENT)

Update RANGE partitioned table interval. Note that interval must not be negative and it must not be trivial, i.e. its value should be greater than zero for numeric types, at least 1 microsecond for TIMESTAMP and at least 1 day for DATE.

set_enable_parent(relation REGCLASS, value BOOLEAN)

Include/exclude parent table into/from query plan. In original PostgreSQL planner parent table is always included into query plan even if it's empty which can lead to additional overhead. You can use disable_parent() if you are never going to use parent table as a storage. Default value depends on the partition_data parameter that was specified during initial partitioning in create_range_partitions() function. If the partition_data parameter was true then all data have already been migrated to partitions and parent table disabled. Otherwise it is enabled.

set_auto(relation REGCLASS, value BOOLEAN)

Enable/disable auto partition propagation (only for RANGE partitioning). It is enabled by default.

set_init_callback(relation REGCLASS, callback REGPROC DEFAULT 0)

Set partition creation callback to be invoked for each attached or created partition (both HASH and RANGE). If callback is marked with SECURITY INVOKER, it's executed with the privileges of the user that produced a statement which has led to creation of a new partition (e.g. INSERT INTO partitioned_table VALUES (-5)). The callback must have the following signature: part_init_callback(args JSONB) RETURNS VOID. Parameter arg consists of several fields whose presence depends on partitioning type:

/* RANGE-partitioned table abc (child abc_4) */
{
    "parent":           "abc",
    "parent_schema":    "public",
    "parttype":         "2",
    "partition":        "abc_4",
    "partition_schema": "public",
    "range_max":        "401",
    "range_min":        "301"
}

/* HASH-partitioned table abc (child abc_0) */
{
    "parent":           "abc",
    "parent_schema":    "public",
    "parttype":         "1",
    "partition":        "abc_0",
    "partition_schema": "public"
}
set_set_spawn_using_bgw(relation REGCLASS, value BOOLEAN)

When INSERTing new data beyond the partitioning range, use SpawnPartitionsWorker to create new partitions in a separate transaction.

Views and tables

pathman_config --- main config storage

CREATE TABLE IF NOT EXISTS pathman_config (
    partrel         REGCLASS NOT NULL PRIMARY KEY,
    expr            TEXT NOT NULL,
    parttype        INTEGER NOT NULL,
    range_interval  TEXT,
    cooked_expr     TEXT);

This table stores a list of partitioned tables.

pathman_config_params --- optional parameters

CREATE TABLE IF NOT EXISTS pathman_config_params (
    partrel         REGCLASS NOT NULL PRIMARY KEY,
    enable_parent   BOOLEAN NOT NULL DEFAULT TRUE,
    auto            BOOLEAN NOT NULL DEFAULT TRUE,
    init_callback   TEXT DEFAULT NULL,
    spawn_using_bgw BOOLEAN NOT NULL DEFAULT FALSE);

This table stores optional parameters which override standard behavior.

pathman_concurrent_part_tasks --- currently running partitioning workers

-- helper SRF function
CREATE OR REPLACE FUNCTION show_concurrent_part_tasks()
RETURNS TABLE (
    userid     REGROLE,
    pid        INT,
    dbid       OID,
    relid      REGCLASS,
    processed  INT,
    status     TEXT)
AS 'pg_pathman', 'show_concurrent_part_tasks_internal'
LANGUAGE C STRICT;

CREATE OR REPLACE VIEW pathman_concurrent_part_tasks
AS SELECT * FROM show_concurrent_part_tasks();

This view lists all currently running concurrent partitioning tasks.

pathman_partition_list --- list of all existing partitions

-- helper SRF function
CREATE OR REPLACE FUNCTION show_partition_list()
RETURNS TABLE (
    parent     REGCLASS,
    partition  REGCLASS,
    parttype   INT4,
    expr       TEXT,
    range_min  TEXT,
    range_max  TEXT)
AS 'pg_pathman', 'show_partition_list_internal'
LANGUAGE C STRICT;

CREATE OR REPLACE VIEW pathman_partition_list
AS SELECT * FROM show_partition_list();

This view lists all existing partitions, as well as their parents and range boundaries (NULL for HASH partitions).

pathman_cache_stats --- per-backend memory consumption

-- helper SRF function
CREATE OR REPLACE FUNCTION @[email protected]_cache_stats()
RETURNS TABLE (
	context     TEXT,
	size        INT8,
	used        INT8,
	entries     INT8)
AS 'pg_pathman', 'show_cache_stats_internal'
LANGUAGE C STRICT;

CREATE OR REPLACE VIEW @[email protected]_cache_stats
AS SELECT * FROM @[email protected]_cache_stats();

Shows memory consumption of various caches.

Declarative partitioning

From PostgreSQL 10 ATTACH PARTITION, DETACH PARTITION and CREATE TABLE .. PARTITION OF commands could be used with tables partitioned by pg_pathman:

CREATE TABLE child1 (LIKE partitioned_table);

--- attach new partition
ALTER TABLE partitioned_table ATTACH PARTITION child1
	FOR VALUES FROM ('2015-05-01') TO ('2015-06-01');

--- detach the partition
ALTER TABLE partitioned_table DETACH PARTITION child1;

-- create a partition
CREATE TABLE child2 PARTITION OF partitioned_table
	FOR VALUES IN ('2015-05-01', '2015-06-01');

Custom plan nodes

pg_pathman provides a couple of custom plan nodes which aim to reduce execution time, namely:

  • RuntimeAppend (overrides Append plan node)
  • RuntimeMergeAppend (overrides MergeAppend plan node)
  • PartitionFilter (drop-in replacement for INSERT triggers)
  • PartitionOverseer (implements cross-partition UPDATEs)
  • PartitionRouter (implements cross-partition UPDATEs)

PartitionFilter acts as a proxy node for INSERT's child scan, which means it can redirect output tuples to the corresponding partition:

EXPLAIN (COSTS OFF)
INSERT INTO partitioned_table
SELECT generate_series(1, 10), random();
               QUERY PLAN
-----------------------------------------
 Insert on partitioned_table
   ->  Custom Scan (PartitionFilter)
         ->  Subquery Scan on "*SELECT*"
               ->  Result
(4 rows)

PartitionOverseer and PartitionRouter are another proxy nodes used in conjunction with PartitionFilter to enable cross-partition UPDATEs (i.e. when update of partitioning key requires that we move row to another partition). Since this node has a great deal of side effects (ordinary UPDATE becomes slower; cross-partition UPDATE is transformed into DELETE + INSERT), it is disabled by default. To enable it, refer to the list of GUCs below.

EXPLAIN (COSTS OFF)
UPDATE partitioned_table
SET value = value + 1 WHERE value = 2;
                       QUERY PLAN
---------------------------------------------------------
 Custom Scan (PartitionOverseer)
   ->  Update on partitioned_table_2
         ->  Custom Scan (PartitionFilter)
               ->  Custom Scan (PartitionRouter)
                     ->  Seq Scan on partitioned_table_2
                           Filter: (value = 2)
(6 rows)

RuntimeAppend and RuntimeMergeAppend have much in common: they come in handy in a case when WHERE condition takes form of:

VARIABLE OP PARAM

This kind of expressions can no longer be optimized at planning time since the parameter's value is not known until the execution stage takes place. The problem can be solved by embedding the WHERE condition analysis routine into the original Append's code, thus making it pick only required scans out of a whole bunch of planned partition scans. This effectively boils down to creation of a custom node capable of performing such a check.


There are at least several cases that demonstrate usefulness of these nodes:

/* create table we're going to partition */
CREATE TABLE partitioned_table(id INT NOT NULL, payload REAL);

/* insert some data */
INSERT INTO partitioned_table
SELECT generate_series(1, 1000), random();

/* perform partitioning */
SELECT create_hash_partitions('partitioned_table', 'id', 100);

/* create ordinary table */
CREATE TABLE some_table AS SELECT generate_series(1, 100) AS VAL;
  • id = (select ... limit 1)
EXPLAIN (COSTS OFF, ANALYZE) SELECT * FROM partitioned_table
WHERE id = (SELECT * FROM some_table LIMIT 1);
                                             QUERY PLAN
----------------------------------------------------------------------------------------------------
 Custom Scan (RuntimeAppend) (actual time=0.030..0.033 rows=1 loops=1)
   InitPlan 1 (returns $0)
     ->  Limit (actual time=0.011..0.011 rows=1 loops=1)
           ->  Seq Scan on some_table (actual time=0.010..0.010 rows=1 loops=1)
   ->  Seq Scan on partitioned_table_70 partitioned_table (actual time=0.004..0.006 rows=1 loops=1)
         Filter: (id = $0)
         Rows Removed by Filter: 9
 Planning time: 1.131 ms
 Execution time: 0.075 ms
(9 rows)

/* disable RuntimeAppend node */
SET pg_pathman.enable_runtimeappend = f;

EXPLAIN (COSTS OFF, ANALYZE) SELECT * FROM partitioned_table
WHERE id = (SELECT * FROM some_table LIMIT 1);
                                    QUERY PLAN
----------------------------------------------------------------------------------
 Append (actual time=0.196..0.274 rows=1 loops=1)
   InitPlan 1 (returns $0)
     ->  Limit (actual time=0.005..0.005 rows=1 loops=1)
           ->  Seq Scan on some_table (actual time=0.003..0.003 rows=1 loops=1)
   ->  Seq Scan on partitioned_table_0 (actual time=0.014..0.014 rows=0 loops=1)
         Filter: (id = $0)
         Rows Removed by Filter: 6
   ->  Seq Scan on partitioned_table_1 (actual time=0.003..0.003 rows=0 loops=1)
         Filter: (id = $0)
         Rows Removed by Filter: 5
         ... /* more plans follow */
 Planning time: 1.140 ms
 Execution time: 0.855 ms
(306 rows)
  • id = ANY (select ...)
EXPLAIN (COSTS OFF, ANALYZE) SELECT * FROM partitioned_table
WHERE id = any (SELECT * FROM some_table limit 4);
                                                QUERY PLAN
-----------------------------------------------------------------------------------------------------------
 Nested Loop (actual time=0.025..0.060 rows=4 loops=1)
   ->  Limit (actual time=0.009..0.011 rows=4 loops=1)
         ->  Seq Scan on some_table (actual time=0.008..0.010 rows=4 loops=1)
   ->  Custom Scan (RuntimeAppend) (actual time=0.002..0.004 rows=1 loops=4)
         ->  Seq Scan on partitioned_table_70 partitioned_table (actual time=0.001..0.001 rows=10 loops=1)
         ->  Seq Scan on partitioned_table_26 partitioned_table (actual time=0.002..0.003 rows=9 loops=1)
         ->  Seq Scan on partitioned_table_27 partitioned_table (actual time=0.001..0.002 rows=20 loops=1)
         ->  Seq Scan on partitioned_table_63 partitioned_table (actual time=0.001..0.002 rows=9 loops=1)
 Planning time: 0.771 ms
 Execution time: 0.101 ms
(10 rows)

/* disable RuntimeAppend node */
SET pg_pathman.enable_runtimeappend = f;

EXPLAIN (COSTS OFF, ANALYZE) SELECT * FROM partitioned_table
WHERE id = any (SELECT * FROM some_table limit 4);
                                       QUERY PLAN
-----------------------------------------------------------------------------------------
 Nested Loop Semi Join (actual time=0.531..1.526 rows=4 loops=1)
   Join Filter: (partitioned_table.id = some_table.val)
   Rows Removed by Join Filter: 3990
   ->  Append (actual time=0.190..0.470 rows=1000 loops=1)
         ->  Seq Scan on partitioned_table (actual time=0.187..0.187 rows=0 loops=1)
         ->  Seq Scan on partitioned_table_0 (actual time=0.002..0.004 rows=6 loops=1)
         ->  Seq Scan on partitioned_table_1 (actual time=0.001..0.001 rows=5 loops=1)
         ->  Seq Scan on partitioned_table_2 (actual time=0.002..0.004 rows=14 loops=1)
... /* 96 scans follow */
   ->  Materialize (actual time=0.000..0.000 rows=4 loops=1000)
         ->  Limit (actual time=0.005..0.006 rows=4 loops=1)
               ->  Seq Scan on some_table (actual time=0.003..0.004 rows=4 loops=1)
 Planning time: 2.169 ms
 Execution time: 2.059 ms
(110 rows)
  • NestLoop involving a partitioned table, which is omitted since it's occasionally shown above.

In case you're interested, you can read more about custom nodes at Alexander Korotkov's blog.

Examples

Common tips

  • You can easily add partition column containing the names of the underlying partitions using the system attribute called tableoid:
SELECT tableoid::regclass AS partition, * FROM partitioned_table;
  • Though indices on a parent table aren't particularly useful (since it's supposed to be empty), they act as prototypes for indices on partitions. For each index on the parent table, pg_pathman will create a similar index on every partition.

  • All running concurrent partitioning tasks can be listed using the pathman_concurrent_part_tasks view:

SELECT * FROM pathman_concurrent_part_tasks;
 userid | pid  | dbid  | relid | processed | status
--------+------+-------+-------+-----------+---------
 dmitry | 7367 | 16384 | test  |    472000 | working
(1 row)
  • pathman_partition_list in conjunction with drop_range_partition() can be used to drop RANGE partitions in a more flexible way compared to good old DROP TABLE:
SELECT drop_range_partition(partition, false) /* move data to parent */
FROM pathman_partition_list
WHERE parent = 'part_test'::regclass AND range_min::int < 500;
NOTICE:  1 rows copied from part_test_11
NOTICE:  100 rows copied from part_test_1
NOTICE:  100 rows copied from part_test_2
 drop_range_partition
----------------------
 dummy_test_11
 dummy_test_1
 dummy_test_2
(3 rows)
  • You can turn foreign tables into partitions using the attach_range_partition() function. Rows that were meant to be inserted into parent will be redirected to foreign partitions (as usual, PartitionFilter will be involved), though by default it is prohibited to insert rows into partitions provided not by postgres_fdw. Only superuser is allowed to set pg_pathman.insert_into_fdw GUC variable.

HASH partitioning

Consider an example of HASH partitioning. First create a table with some integer column:

CREATE TABLE items (
    id       SERIAL PRIMARY KEY,
    name     TEXT,
    code     BIGINT);

INSERT INTO items (id, name, code)
SELECT g, md5(g::text), random() * 100000
FROM generate_series(1, 100000) as g;

Now run the create_hash_partitions() function with appropriate arguments:

SELECT create_hash_partitions('items', 'id', 100);

This will create new partitions and move the data from parent to partitions.

Here's an example of the query performing filtering by partitioning key:

SELECT * FROM items WHERE id = 1234;
  id  |               name               | code
------+----------------------------------+------
 1234 | 81dc9bdb52d04dc20036dbd8313ed055 | 1855
(1 row)

EXPLAIN SELECT * FROM items WHERE id = 1234;
                                     QUERY PLAN
------------------------------------------------------------------------------------
 Append  (cost=0.28..8.29 rows=0 width=0)
   ->  Index Scan using items_34_pkey on items_34  (cost=0.28..8.29 rows=0 width=0)
         Index Cond: (id = 1234)

Notice that the Append node contains only one child scan which corresponds to the WHERE clause.

Important: pay attention to the fact that pg_pathman excludes the parent table from the query plan.

To access parent table use ONLY modifier:

EXPLAIN SELECT * FROM ONLY items;
                      QUERY PLAN
------------------------------------------------------
 Seq Scan on items  (cost=0.00..0.00 rows=1 width=45)

RANGE partitioning

Consider an example of RANGE partitioning. Let's create a table containing some dummy logs:

CREATE TABLE journal (
    id      SERIAL,
    dt      TIMESTAMP NOT NULL,
    level   INTEGER,
    msg     TEXT);

-- similar index will also be created for each partition
CREATE INDEX ON journal(dt);

-- generate some data
INSERT INTO journal (dt, level, msg)
SELECT g, random() * 6, md5(g::text)
FROM generate_series('2015-01-01'::date, '2015-12-31'::date, '1 minute') as g;

Run the create_range_partitions() function to create partitions so that each partition would contain the data for one day:

SELECT create_range_partitions('journal', 'dt', '2015-01-01'::date, '1 day'::interval);

It will create 365 partitions and move the data from parent to partitions.

New partitions are appended automaticaly by insert trigger, but it can be done manually with the following functions:

-- add new partition with specified range
SELECT add_range_partition('journal', '2016-01-01'::date, '2016-01-07'::date);

-- append new partition with default range
SELECT append_range_partition('journal');

The first one creates a partition with specified range. The second one creates a partition with default interval and appends it to the partition list. It is also possible to attach an existing table as partition. For example, we may want to attach an archive table (or even foreign table from another server) for some outdated data:

CREATE FOREIGN TABLE journal_archive (
    id      INTEGER NOT NULL,
    dt      TIMESTAMP NOT NULL,
    level   INTEGER,
    msg     TEXT)
SERVER archive_server;

SELECT attach_range_partition('journal', 'journal_archive', '2014-01-01'::date, '2015-01-01'::date);

Important: the definition of the attached table must match the one of the existing partitioned table, including the dropped columns.

To merge to adjacent partitions, use the merge_range_partitions() function:

SELECT merge_range_partitions('journal_archive', 'journal_1');

To split partition by value, use the split_range_partition() function:

SELECT split_range_partition('journal_366', '2016-01-03'::date);

To detach partition, use the detach_range_partition() function:

SELECT detach_range_partition('journal_archive');

Here's an example of the query performing filtering by partitioning key:

SELECT * FROM journal WHERE dt >= '2015-06-01' AND dt < '2015-06-03';
   id   |         dt          | level |               msg
--------+---------------------+-------+----------------------------------
 217441 | 2015-06-01 00:00:00 |     2 | 15053892d993ce19f580a128f87e3dbf
 217442 | 2015-06-01 00:01:00 |     1 | 3a7c46f18a952d62ce5418ac2056010c
 217443 | 2015-06-01 00:02:00 |     0 | 92c8de8f82faf0b139a3d99f2792311d
 ...
(2880 rows)

EXPLAIN SELECT * FROM journal WHERE dt >= '2015-06-01' AND dt < '2015-06-03';
                            QUERY PLAN
------------------------------------------------------------------
 Append  (cost=0.00..58.80 rows=0 width=0)
   ->  Seq Scan on journal_152  (cost=0.00..29.40 rows=0 width=0)
   ->  Seq Scan on journal_153  (cost=0.00..29.40 rows=0 width=0)
(3 rows)

Disabling pg_pathman

There are several user-accessible GUC variables designed to toggle the whole module or specific custom nodes on and off:

  • pg_pathman.enable --- disable (or enable) pg_pathman completely
  • pg_pathman.enable_runtimeappend --- toggle RuntimeAppend custom node on\off
  • pg_pathman.enable_runtimemergeappend --- toggle RuntimeMergeAppend custom node on\off
  • pg_pathman.enable_partitionfilter --- toggle PartitionFilter custom node on\off (for INSERTs)
  • pg_pathman.enable_partitionrouter --- toggle PartitionRouter custom node on\off (for cross-partition UPDATEs)
  • pg_pathman.enable_auto_partition --- toggle automatic partition creation on\off (per session)
  • pg_pathman.enable_bounds_cache --- toggle bounds cache on\off (faster updates of partitioning scheme)
  • pg_pathman.insert_into_fdw --- allow INSERTs into various FDWs (disabled | postgres | any_fdw)
  • pg_pathman.override_copy --- toggle COPY statement hooking on\off

To permanently disable pg_pathman for some previously partitioned table, use the disable_pathman_for() function:

SELECT disable_pathman_for('range_rel');

All sections and data will remain unchanged and will be handled by the standard PostgreSQL inheritance mechanism.

Feedback

Do not hesitate to post your issues, questions and new ideas at the issues page.

Authors

Ildar Musin Alexander Korotkov Dmitry Ivanov Maksim Milyutin Ildus Kurbangaliev

pg_pathman's People

Contributors

akorotkov avatar antamel avatar arssher avatar elisnow avatar feodor avatar funbringer avatar funny-falcon avatar ildus avatar kovdb75 avatar kulaginm avatar maksm90 avatar pashkinelfe avatar rjuju avatar saaros avatar vbwagner avatar victorspirin avatar zilder avatar ziva777 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pg_pathman's Issues

disable_parent() sometimes does not exclude parent table from plan

postgres=# create table test(id int primary key, info text, crt_time timestamp);
CREATE TABLE
postgres=# select create_range_partitions('test'::regclass, 'id', 1, 20000000, 100, false);
 create_range_partitions 
-------------------------
                     100
(1 row)

postgres=# select disable_parent('test'::regclass);
 disable_parent 
----------------

(1 row)

postgres=# explain select * from test where id=1;
                                   QUERY PLAN                                    
---------------------------------------------------------------------------------
 Append  (cost=0.00..2.17 rows=2 width=44)
   ->  Seq Scan on test  (cost=0.00..0.00 rows=1 width=44)
         Filter: (id = 1)
   ->  Index Scan using test_1_pkey on test_1  (cost=0.15..2.17 rows=1 width=44)
         Index Cond: (id = 1)
(5 rows)

when i reconnect

postgres=# \q
-> psql
psql (9.6rc1)
Type "help" for help.

postgres=# explain select * from test where id=1;
                                   QUERY PLAN                                    
---------------------------------------------------------------------------------
 Append  (cost=0.15..2.17 rows=1 width=44)
   ->  Index Scan using test_1_pkey on test_1  (cost=0.15..2.17 rows=1 width=44)
         Index Cond: (id = 1)
(3 rows)

Partition creation isn't transactional

The partition cache in shared memory is apparently updated immediately when partitions are updated, but that's not safe as other transactions won't see the new tables until they are committed. And there's nothing that would refresh the cache in case a transaction is rolled back, so tables are essentially broken until you restart postgres in case a partition modifying transaction is rolled back:

os=# begin;
BEGIN
os=# SELECT create_range_partitions('kala', 'id', 0, 50000, 3);

[......] 

create_range_partitions 
-------------------------
3
os=# rollback;
ROLLBACK
os=# select * from kala;
ERROR:  could not open relation with OID 16556

Partition naming using the set_init_callback()

Hi,
Sorry for stupid question it's not clear for me how can I rename a partition in the handler part_init_callback() during the partition creation, so that when I call create_range_partitions() it would make names like table_2016_11_01, table_2016_11_02 etc. Could you show some example please?

cheers

Partitioning of the tables having case-sensitive names

Проблема с партицией таблицы с названием Info.

При выполнении запроса:
SELECT create_range_partitions('Info', 'date', '1451606400000'::bigint, '2419200000'::bigint)
получаю 7 ERROR: relation "info" does not exist.

При выполнении запроса:
SELECT create_range_partitions("Info", 'date', '1451606400000'::bigint, '2419200000'::bigint);
получаю 7 ERROR: column "Info" does not exist.

Constraint "pathman_table_1_14_check" does not exist

I tried to create a range partition on a table (11G in size) by first calling create_range_partitions with partition_data set to false. It created 10 partitions and added pathman_table_number_11_check on them
Then I tried running partition_table_concurrently and got this error:

PG::InternalError: ERROR: constraint "pathman_table_1_14_check" for partition "table_1" does not exist
HINT: pg_pathman will be disabled to allow you to resolve this issue

Constraint id is being calculated incorrectly in this case.

incorrect result

Postgres Pro, официальный репозиторий, версия 9.6, debian 8.6 x64

DISCARD PLANS;
with tmp as
(
	select tid
	from transport.transaction_specdata
	where state_code != transport.get_state_code_by_name('processing')
)
select *
from main.transaction t
	join tmp on t.id = tmp.tid;

выдает пустую выборку. Если заменить вызов функции transport.get_state_code_by_name() на константу, то запрос отрабатывает корректно. Также запрос отрабатывает корректно со второго раза без DISCARD PLANS. main.transaction разбита на разделы, но поле pdate (см.ниже), по которому производится партицирование, не используется (если использовать, то проблема остается - просто сократили пример).

CREATE OR REPLACE FUNCTION transport.get_state_code_by_name(_state_name text)
  RETURNS smallint AS ...
  LANGUAGE plpgsql STABLE;

Более того, в практическом применении функция не вызывается так, как указано выше. Вот реальная функция, которая вообще не работает (del_trn всегда пустой) с любыми манипуляциями снаружи:

CREATE OR REPLACE FUNCTION transport.garbage_collector(_max_store_time interval)
  RETURNS void AS
$BODY$
declare
	_cur_tid bigint;
	_processing_code smallint := transport.get_state_code_by_name('processing');
BEGIN
	for _cur_tid in 
		with tmp as
		(
			select tid, regtime::date as pdate, test_mode, state_code
			from transport.transaction_specdata
			where regtime < (now() - _max_store_time) and
				(test_mode > 0 or state_code != _processing_code)
		),
		del_trn as
		(
			-- delete test-mode transactions
			delete from main.transaction t
			using tmp
			where t.id = tmp.tid and t.pdate = tmp.pdate and tmp.test_mode > 0
		)
		delete from transport.transaction_specdata s
		using tmp
		where s.tid = tmp.tid and tmp.state_code != _processing_code
		returning s.tid
	loop
		raise notice '[Garbage collector][tid=%] The transaction was deleted', _cur_tid;
	end loop;
END;
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;

Проект накануне запуска. Кажется, придется делать всё по-старинке :(

Нет возможности задавать шаблон названий дочерних таблиц

Например, для разбивалки по диапазонам - я хочу некоторые операции делать над фактическими таблицами. А где взять их имена ? Вдруг в следующей версии схема именования поменяется ?

Ну, и пачка идей:

Нет возможности указывать шаблон названия тейблспейса для каждой создаваемой таблицы. Либо нужен триггер в котором можно переместить таблицу (это возможно из триггера?).

Ещё, из идей - чтобы при выборках из виртуальной таблицы был дополнительный виртуальный столбец - имя фактической таблицы.

Кстати, не по теме, а там нет оптимизации, что при удалении записей в диапазоном разбиении - если выясняется что все записи из некоторой таблицы будут удалены, то проще сделать drop table, чем удалять из таблицы всё.

и ещё, в документации нужно указать полный список того, что копируется с главной таблицы, а что нет во время создания реальных. Фантазирую, конечно, но вдруг какой-то признак типа unlogged, fillfactor или autovacuum не копируется (не проверял).

Не документировано как формируется название индексов при копировании с мастер-таблицы.
Если в мастер-таблице было запомнено что кластеризовать по этому индексу, это перенесётся в дочерние таблицы (должна смениться ссылка на новый индекс)?

ещё, ранее я находил https://github.com/keithf4/pg_partman и там ведётся работа. у кого лучше ? есть ещё https://github.com/moat/range_partitioning

the table is lost

PostgreSQL 9.5.4 on x86_64-pc-linux-gnu, compiled by gcc (Debian 4.9.2-10) 4.9.2, 64-bit
(postgrespro)

select pathman.create_range_partitions('main.participant', 'pdate', '2016-01-01'::date, '1 year'::interval);
->
NOTICE: sequence "participant_seq" does not exist, skipping
WARNING: Range constraint for relation 41726 MUST have exact format: VARIABLE >= CONST AND VARIABLE < CONST. Skipping...
WARNING: Partitions 41692 and 41726 overlap. Disabling pathman for relation 29053...
NOTICE: Copying data to partitions...
ERROR: ERROR: Cannot find partition P0001

select * from main.participant
->
ERROR: could not open relation with OID 2147483649

  • several rows in
    create table main.participant
    (
    tid bigint not null,
    cid integer not null,
    pdate date not null,
    ...
    primary key (tid, cid)
    )

Parent table size after creating partitions

I created a range partition on an existing table of size around 4G using create_range_partitions with partition_data set to false.
After that I ran partition_table_concurrently. After the data migration has been completed (which I verified by running SELECT * FROM pathman_concurrent_part_tasks;), the parent table size still remains 4G.
Does this mean that parent table still contains all data? Similar question about indexes on parent table, our indexes on parent table are still huge in size. Should I manually clean the parent table and indexes?

synchronous partitioning of referenced tables

The very typical case is several big tables with foreign keys (e.g. transactions <- posts). We could (have to) add the same field to split on, but foreign key constraints and referenced tables are out of scope of pg_pathman. If inherited tables can't share pk of their parent, pg_pathman should help to solve the fk problem (OR there is a way to apply a single pk on all child tables in case of being referenced by equally partitioned table?).

prepared statement can't choose partition?

create table test_pg_part_pathman(id int primary key, info text, crt_time timestamp);

ostgres=# select                                             
create_range_partitions('test_pg_part_pathman'::regclass,             -- 主表OID
                        'id',                                        -- 分区列名
                        1,                                           -- 开始值
                        1000000,                                       -- 间隔
                        20,                                -- 分多少个区
                        true) ;                           -- 迁移数据

postgres=# select set_enable_parent('test_pg_part_pathman'::regclass, false);

postgres=# \d+ test_pg_part_pathman
                            Table "public.test_pg_part_pathman"
  Column  |            Type             | Modifiers | Storage  | Stats target | Description 
----------+-----------------------------+-----------+----------+--------------+-------------
 id       | integer                     | not null  | plain    |              | 
 info     | text                        |           | extended |              | 
 crt_time | timestamp without time zone |           | plain    |              | 
Indexes:
    "test_pg_part_pathman_pkey" PRIMARY KEY, btree (id)
Child tables: test_pg_part_pathman_1,
              test_pg_part_pathman_10,
              test_pg_part_pathman_11,
              test_pg_part_pathman_12,
              test_pg_part_pathman_13,
              test_pg_part_pathman_14,
              test_pg_part_pathman_15,
              test_pg_part_pathman_16,
              test_pg_part_pathman_17,
              test_pg_part_pathman_18,
              test_pg_part_pathman_19,
              test_pg_part_pathman_2,
              test_pg_part_pathman_20,
              test_pg_part_pathman_3,
              test_pg_part_pathman_4,
              test_pg_part_pathman_5,
              test_pg_part_pathman_6,
              test_pg_part_pathman_7,
              test_pg_part_pathman_8,
              test_pg_part_pathman_9

postgres=# \d+ test_pg_part_pathman_1
                           Table "public.test_pg_part_pathman_1"
  Column  |            Type             | Modifiers | Storage  | Stats target | Description 
----------+-----------------------------+-----------+----------+--------------+-------------
 id       | integer                     | not null  | plain    |              | 
 info     | text                        |           | extended |              | 
 crt_time | timestamp without time zone |           | plain    |              | 
Indexes:
    "test_pg_part_pathman_1_pkey" PRIMARY KEY, btree (id)
Check constraints:
    "pathman_test_pg_part_pathman_1_1_check" CHECK (id >= 1 AND id < 1000001)
Inherits: test_pg_part_pathman

postgres=# \d+ test_pg_part_pathman_10
                           Table "public.test_pg_part_pathman_10"
  Column  |            Type             | Modifiers | Storage  | Stats target | Description 
----------+-----------------------------+-----------+----------+--------------+-------------
 id       | integer                     | not null  | plain    |              | 
 info     | text                        |           | extended |              | 
 crt_time | timestamp without time zone |           | plain    |              | 
Indexes:
    "test_pg_part_pathman_10_pkey" PRIMARY KEY, btree (id)
Check constraints:
    "pathman_test_pg_part_pathman_10_1_check" CHECK (id >= 9000001 AND id < 10000001)
Inherits: test_pg_part_pathman

postgres=#  insert into test_pg_part_pathman select generate_series(1,20000000);
INSERT 0 20000000
Time: 61634.401 ms
postgres=# select count(*) from test_pg_part_orig;
  count   
----------
 20000000
(1 row)
Time: 1879.867 ms

postgres=# prepare p1(int) as select * from test_pg_part_pathman where id=$1;
PREPARE
postgres=# explain execute p1(1);
                                                   QUERY PLAN                                                    
-----------------------------------------------------------------------------------------------------------------
 Append  (cost=0.42..2.44 rows=1 width=44)
   ->  Index Scan using test_pg_part_pathman_1_pkey on test_pg_part_pathman_1  (cost=0.42..2.44 rows=1 width=44)
         Index Cond: (id = 1)
(3 rows)
...
sixth:
postgres=# explain execute p1(2);
                                                               QUERY PLAN                                                               
----------------------------------------------------------------------------------------------------------------------------------------
 Custom Scan (RuntimeAppend)  (cost=0.42..2.44 rows=1 width=44)
   ->  Index Scan using test_pg_part_pathman_1_pkey on test_pg_part_pathman_1 test_pg_part_pathman  (cost=0.42..2.44 rows=1 width=44)
         Index Cond: (id = $1)
   ->  Index Scan using test_pg_part_pathman_2_pkey on test_pg_part_pathman_2 test_pg_part_pathman  (cost=0.42..2.44 rows=1 width=44)
         Index Cond: (id = $1)
   ->  Index Scan using test_pg_part_pathman_3_pkey on test_pg_part_pathman_3 test_pg_part_pathman  (cost=0.42..2.44 rows=1 width=44)
         Index Cond: (id = $1)
   ->  Index Scan using test_pg_part_pathman_4_pkey on test_pg_part_pathman_4 test_pg_part_pathman  (cost=0.42..2.44 rows=1 width=44)
         Index Cond: (id = $1)
   ->  Index Scan using test_pg_part_pathman_5_pkey on test_pg_part_pathman_5 test_pg_part_pathman  (cost=0.42..2.44 rows=1 width=44)
         Index Cond: (id = $1)
   ->  Index Scan using test_pg_part_pathman_6_pkey on test_pg_part_pathman_6 test_pg_part_pathman  (cost=0.42..2.44 rows=1 width=44)
         Index Cond: (id = $1)
   ->  Index Scan using test_pg_part_pathman_7_pkey on test_pg_part_pathman_7 test_pg_part_pathman  (cost=0.42..2.44 rows=1 width=44)
         Index Cond: (id = $1)
   ->  Index Scan using test_pg_part_pathman_8_pkey on test_pg_part_pathman_8 test_pg_part_pathman  (cost=0.42..2.44 rows=1 width=44)
         Index Cond: (id = $1)
   ->  Index Scan using test_pg_part_pathman_9_pkey on test_pg_part_pathman_9 test_pg_part_pathman  (cost=0.42..2.44 rows=1 width=44)
         Index Cond: (id = $1)
   ->  Index Scan using test_pg_part_pathman_10_pkey on test_pg_part_pathman_10 test_pg_part_pathman  (cost=0.42..2.44 rows=1 width=44)
         Index Cond: (id = $1)
   ->  Index Scan using test_pg_part_pathman_11_pkey on test_pg_part_pathman_11 test_pg_part_pathman  (cost=0.42..2.44 rows=1 width=44)
         Index Cond: (id = $1)
   ->  Index Scan using test_pg_part_pathman_12_pkey on test_pg_part_pathman_12 test_pg_part_pathman  (cost=0.42..2.44 rows=1 width=44)
         Index Cond: (id = $1)
   ->  Index Scan using test_pg_part_pathman_13_pkey on test_pg_part_pathman_13 test_pg_part_pathman  (cost=0.42..2.44 rows=1 width=44)
         Index Cond: (id = $1)
   ->  Index Scan using test_pg_part_pathman_14_pkey on test_pg_part_pathman_14 test_pg_part_pathman  (cost=0.42..2.44 rows=1 width=44)
         Index Cond: (id = $1)
   ->  Index Scan using test_pg_part_pathman_15_pkey on test_pg_part_pathman_15 test_pg_part_pathman  (cost=0.42..2.44 rows=1 width=44)
         Index Cond: (id = $1)
   ->  Index Scan using test_pg_part_pathman_16_pkey on test_pg_part_pathman_16 test_pg_part_pathman  (cost=0.42..2.44 rows=1 width=44)
         Index Cond: (id = $1)
   ->  Index Scan using test_pg_part_pathman_17_pkey on test_pg_part_pathman_17 test_pg_part_pathman  (cost=0.42..2.44 rows=1 width=44)
         Index Cond: (id = $1)
   ->  Index Scan using test_pg_part_pathman_18_pkey on test_pg_part_pathman_18 test_pg_part_pathman  (cost=0.42..2.44 rows=1 width=44)
         Index Cond: (id = $1)
   ->  Index Scan using test_pg_part_pathman_19_pkey on test_pg_part_pathman_19 test_pg_part_pathman  (cost=0.42..2.44 rows=1 width=44)
         Index Cond: (id = $1)
   ->  Index Scan using test_pg_part_pathman_20_pkey on test_pg_part_pathman_20 test_pg_part_pathman  (cost=0.42..2.44 rows=1 width=44)
         Index Cond: (id = $1)
(41 rows)

Build error on PostgreSQL 9.6rc1 (on OS X 10.11 El Capitan and Debian Jessie)

Trying to build tag 1.0.1 of pg_pathman on latest OS X (same error with CC=gcc):

$ make PG_CONFIG=/usr/local/Cellar/postgresql/9.6rc1/bin/pg_config install USE_PGXS=1

clang -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -Wno-unused-command-line-argument -O2  -I. -I./ -I/usr/local/Cellar/postgresql/9.6rc1/include/server -I/usr/local/Cellar/postgresql/9.6rc1/include/internal -I/usr/local/opt/openssl/include -I/usr/local/opt/readline/include -I/usr/include/libxml2   -c -o src/utils.o src/utils.c
src/utils.c:268:10: error: use of undeclared identifier 'PVC_REJECT_AGGREGATES'
                                                   PVC_REJECT_AGGREGATES,
                                                   ^
src/utils.c:269:10: error: use of undeclared identifier 'PVC_REJECT_PLACEHOLDERS'
                                                   PVC_REJECT_PLACEHOLDERS);
                                                   ^
2 errors generated.
make: *** [src/utils.o] Error 1

Output of /usr/local/Cellar/postgresql/9.6rc1/bin/pg_config:

BINDIR = /usr/local/Cellar/postgresql/9.6rc1/bin
DOCDIR = /usr/local/Cellar/postgresql/9.6rc1/share/doc/postgresql
HTMLDIR = /usr/local/Cellar/postgresql/9.6rc1/share/doc/postgresql
INCLUDEDIR = /usr/local/Cellar/postgresql/9.6rc1/include
PKGINCLUDEDIR = /usr/local/Cellar/postgresql/9.6rc1/include
INCLUDEDIR-SERVER = /usr/local/Cellar/postgresql/9.6rc1/include/server
LIBDIR = /usr/local/lib
PKGLIBDIR = /usr/local/lib/postgresql
LOCALEDIR = /usr/local/Cellar/postgresql/9.6rc1/share/locale
MANDIR = /usr/local/Cellar/postgresql/9.6rc1/share/man
SHAREDIR = /usr/local/share/postgresql
SYSCONFDIR = /usr/local/etc/postgresql
PGXS = /usr/local/lib/postgresql/pgxs/src/makefiles/pgxs.mk
CONFIGURE = '--disable-debug' '--prefix=/usr/local/Cellar/postgresql/9.6rc1' '--datadir=/usr/local/share/postgresql' '--libdir=/usr/local/lib' '--sysconfdir=/usr/local/etc' '--docdir=/usr/local/Cellar/postgresql/9.6rc1/share/doc/postgresql' '--enable-thread-safety' '--with-bonjour' '--with-gssapi' '--with-ldap' '--with-openssl' '--with-pam' '--with-libxml' '--with-libxslt' '--with-perl' '--with-tcl' '--with-tclconfig=/usr/lib' '--with-uuid=e2fs' 'CC=clang' 'LDFLAGS=-L/usr/local/opt/openssl/lib -L/usr/local/opt/readline/lib' 'CPPFLAGS=-I/usr/local/opt/openssl/include -I/usr/local/opt/readline/include'
CC = clang
CPPFLAGS = -DFRONTEND -I/usr/local/opt/openssl/include -I/usr/local/opt/readline/include -I/usr/include/libxml2
CFLAGS = -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -Wno-unused-command-line-argument -O2
CFLAGS_SL =
LDFLAGS = -L../../src/common -L/usr/local/opt/openssl/lib -L/usr/local/opt/readline/lib -Wl,-dead_strip_dylibs
LDFLAGS_EX =
LDFLAGS_SL =
LIBS = -lpgcommon -lpgport -lxslt -lxml2 -lpam -lssl -lcrypto -lgssapi_krb5 -lz -lreadline -lm
VERSION = PostgreSQL 9.6rc1

Clang version:

clang -v
Apple LLVM version 7.3.0 (clang-703.0.31)
Target: x86_64-apple-darwin15.6.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin

Also tried to install it into Docker postgres:9.6 image (based on Debian Jessie) with next Dockerfile (placed in pg_pathman root):

FROM postgres:9.6
RUN apt-get update -qq && apt-get install -y build-essential postgresql-server-dev-$PG_MAJOR
ADD . /pg_pathman
WORKDIR  /pg_pathman
RUN make install USE_PGXS=1

result is the same:

Step 5 : RUN make install USE_PGXS=1
 ---> Running in e9599ddfff8a
gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -g -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -I/usr/include/mit-krb5 -fPIC -pie -fno-omit-frame-pointer -fpic -I. -I./ -I/usr/include/postgresql/9.6/server -I/usr/include/postgresql/internal -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -I/usr/include/libxml2  -I/usr/include/tcl8.6  -c -o src/utils.o src/utils.c
src/utils.c: In function ‘check_rinfo_for_partitioned_attr’:
src/utils.c:268:10: error: ‘PVC_REJECT_AGGREGATES’ undeclared (first use in this function)
          PVC_REJECT_AGGREGATES,
          ^
src/utils.c:268:10: note: each undeclared identifier is reported only once for each function it appears in
src/utils.c:269:10: error: ‘PVC_REJECT_PLACEHOLDERS’ undeclared (first use in this function)
          PVC_REJECT_PLACEHOLDERS);
          ^
src/utils.c:267:9: error: too many arguments to function ‘pull_var_clause’
  vars = pull_var_clause((Node *) clauses,
         ^
In file included from src/utils.c:22:0:
/usr/include/postgresql/9.6/server/optimizer/var.h:37:14: note: declared here
 extern List *pull_var_clause(Node *node, int flags);
              ^
<builtin>: recipe for target 'src/utils.o' failed

If I'm trying to build it against 9.5 in Docker, I'm getting another error:

Step 5 : RUN make install USE_PGXS=1
 ---> Running in e45dd2f79558
gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -g -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -I/usr/include/mit-krb5 -fPIC -pie -fno-omit-frame-pointer -fpic -I. -I./ -I/usr/include/postgresql/9.5/server -I/usr/include/postgresql/internal -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -I/usr/include/libxml2  -I/usr/include/tcl8.6  -c -o src/utils.o src/utils.c
gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -g -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -I/usr/include/mit-krb5 -fPIC -pie -fno-omit-frame-pointer -fpic -I. -I./ -I/usr/include/postgresql/9.5/server -I/usr/include/postgresql/internal -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -I/usr/include/libxml2  -I/usr/include/tcl8.6  -c -o src/partition_filter.o src/partition_filter.c
gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -g -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -I/usr/include/mit-krb5 -fPIC -pie -fno-omit-frame-pointer -fpic -I. -I./ -I/usr/include/postgresql/9.5/server -I/usr/include/postgresql/internal -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -I/usr/include/libxml2  -I/usr/include/tcl8.6  -c -o src/runtimeappend.o src/runtimeappend.c
gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -g -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -I/usr/include/mit-krb5 -fPIC -pie -fno-omit-frame-pointer -fpic -I. -I./ -I/usr/include/postgresql/9.5/server -I/usr/include/postgresql/internal -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -I/usr/include/libxml2  -I/usr/include/tcl8.6  -c -o src/runtime_merge_append.o src/runtime_merge_append.c
gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -g -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -I/usr/include/mit-krb5 -fPIC -pie -fno-omit-frame-pointer -fpic -I. -I./ -I/usr/include/postgresql/9.5/server -I/usr/include/postgresql/internal -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -I/usr/include/libxml2  -I/usr/include/tcl8.6  -c -o src/pg_pathman.o src/pg_pathman.c
gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -g -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -I/usr/include/mit-krb5 -fPIC -pie -fno-omit-frame-pointer -fpic -I. -I./ -I/usr/include/postgresql/9.5/server -I/usr/include/postgresql/internal -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -I/usr/include/libxml2  -I/usr/include/tcl8.6  -c -o src/dsm_array.o src/dsm_array.c
gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -g -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -I/usr/include/mit-krb5 -fPIC -pie -fno-omit-frame-pointer -fpic -I. -I./ -I/usr/include/postgresql/9.5/server -I/usr/include/postgresql/internal -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -I/usr/include/libxml2  -I/usr/include/tcl8.6  -c -o src/rangeset.o src/rangeset.c
gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -g -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -I/usr/include/mit-krb5 -fPIC -pie -fno-omit-frame-pointer -fpic -I. -I./ -I/usr/include/postgresql/9.5/server -I/usr/include/postgresql/internal -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -I/usr/include/libxml2  -I/usr/include/tcl8.6  -c -o src/pl_funcs.o src/pl_funcs.c
gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -g -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -I/usr/include/mit-krb5 -fPIC -pie -fno-omit-frame-pointer -fpic -I. -I./ -I/usr/include/postgresql/9.5/server -I/usr/include/postgresql/internal -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -I/usr/include/libxml2  -I/usr/include/tcl8.6  -c -o src/pathman_workers.o src/pathman_workers.c
gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -g -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -I/usr/include/mit-krb5 -fPIC -pie -fno-omit-frame-pointer -fpic -I. -I./ -I/usr/include/postgresql/9.5/server -I/usr/include/postgresql/internal -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -I/usr/include/libxml2  -I/usr/include/tcl8.6  -c -o src/hooks.o src/hooks.c
gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -g -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -I/usr/include/mit-krb5 -fPIC -pie -fno-omit-frame-pointer -fpic -I. -I./ -I/usr/include/postgresql/9.5/server -I/usr/include/postgresql/internal -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -I/usr/include/libxml2  -I/usr/include/tcl8.6  -c -o src/nodes_common.o src/nodes_common.c
gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -g -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -I/usr/include/mit-krb5 -fPIC -pie -fno-omit-frame-pointer -fpic -I. -I./ -I/usr/include/postgresql/9.5/server -I/usr/include/postgresql/internal -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -I/usr/include/libxml2  -I/usr/include/tcl8.6  -c -o src/xact_handling.o src/xact_handling.c
gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -g -g -O2 -fstack-protector-strong -Wformat -Werror=format-security -I/usr/include/mit-krb5 -fPIC -pie -fno-omit-frame-pointer -fpic -shared -o pg_pathman.so src/init.o src/relation_info.o src/utils.o src/partition_filter.o src/runtimeappend.o src/runtime_merge_append.o src/pg_pathman.o src/dsm_array.o src/rangeset.o src/pl_funcs.o src/pathman_workers.o src/hooks.o src/nodes_common.o src/xact_handling.o  -L/usr/lib/x86_64-linux-gnu -Wl,-z,relro -Wl,-z,now -Wl,--as-needed -L/usr/lib/mit-krb5 -L/usr/lib/x86_64-linux-gnu/mit-krb5  -Wl,--as-needed
src/init.o: file not recognized: File format not recognized
collect2: error: ld returned 1 exit status
make: *** [pg_pathman.so] Error 1
/usr/lib/postgresql/9.5/lib/pgxs/src/makefiles/../../src/Makefile.shlib:311: recipe for target 'pg_pathman.so' failed
The command '/bin/sh -c make install USE_PGXS=1' returned a non-zero code: 2

Question: is it compatible with Postgres 9.6? Am I need to install some requirement not mentioned in README?

Query plan when data is being copied to children

If I run create_range_partitions and then partition_table_concurrently at a later time, then how is the query plan generated for all the queries that happen in between?
I ran explain on a query and the query plan showed that ti would read data from the child, even though the data migration was not complete.
Ideally all the queries should be executed on parent till the data migration is complete. After migration, query should execute only on children.
Documentation of partition_table_concurrently is not very clear on this.

Owner for partitions is not equal to parent table

Hi!

When range partition is created it is owned by default user which is not equal to parent table owner (see below).
Is it possible to set the same owner for partitions?

drop role if exists test_owner;
drop table if exists testing_part_owner cascade; 

create role test_owner login nosuperuser;

create table if not exists testing_part_owner(
  range_field      timestamp not null,
  data             integer
)
with (oids = false);

create index if not exists testing_part_owner_i on testing_part_owner(data);

alter table testing_part_owner owner to test_owner;
grant all privileges on testing_part_owner to test_owner;

select
create_range_partitions(
  'testing_part_owner',
  'range_field',
  date_trunc('day', localtimestamp)::timestamp,
  '1 day'::interval,
  1
);

select b.rolname as object_owner, a.relkind as object_type, a.relname as object_name
from pg_class a inner join pg_roles b on a.relowner = b.oid
where a.relname like 'testing\_part\_owner%' escape '\';

Result:

 object_owner | object_type |          object_name          
--------------+-------------+-------------------------------
 test_owner   | r           | testing_part_owner
 postgres     | r           | testing_part_owner_1
 postgres     | i           | testing_part_owner_1_data_idx
 test_owner   | i           | testing_part_owner_i
 postgres     | S           | testing_part_owner_seq
(5 rows)


range partitions interval, locks

Last version of postgrespro, pg_pathman from it's contrib package.
Problem 1: unable to create several partitions within single transaction.
Problem 2: pg_pathman creates next partition with incorrect end_value despite interval parameter
(look at the result partitions of last query).

Init query:

CREATE SCHEMA main;
CREATE SCHEMA pathman;

CREATE EXTENSION pg_pathman SCHEMA pathman;

SET search_path = main, pg_catalog;

CREATE FUNCTION tr_registry_after_all_stmt() RETURNS trigger
    LANGUAGE plpgsql
    AS $$
begin
    raise notice 'tr_registry_after_all_stmt';
    return null;
end
$$;

CREATE FUNCTION tr_registry_before_all_row() RETURNS trigger
    LANGUAGE plpgsql
    AS $$
begin
    raise notice 'tr_registry_before_all_row';
    return case when TG_OP = 'DELETE' then old else new end;
end
$$;

CREATE TABLE main.transaction
(
  id bigserial primary key,
  pdate date NOT NULL DEFAULT (now())::date
);

CREATE TABLE main.participant
(
  tid bigint NOT NULL,
  cid integer NOT NULL,
  pdate date NOT NULL DEFAULT (now())::date,
  PRIMARY KEY (tid, cid)
);

CREATE TABLE main.registry
(
  pdate date NOT NULL,
  tid bigint NOT NULL,
  post_id integer NOT NULL,
  PRIMARY KEY (tid, post_id)
);

CREATE TRIGGER tr_registry_after_all_stmt
  AFTER INSERT OR UPDATE OR DELETE
  ON main.registry
  FOR EACH STATEMENT
  EXECUTE PROCEDURE main.tr_registry_after_all_stmt();

CREATE TRIGGER tr_registry_before_all_row
  BEFORE INSERT OR UPDATE OR DELETE
  ON main.registry
  FOR EACH ROW
  EXECUTE PROCEDURE main.tr_registry_before_all_row();


CREATE FUNCTION after_split_participant(params jsonb) RETURNS void
    LANGUAGE plpgsql
    AS $_$
declare
    _c text := params->>'partition';
begin

    execute 'alter table main.' || _c || ' 
                add foreign key(tid) references main.transaction_' || substring(_c from '_(\d+)$') || '(id) 
                on delete cascade';

end
$_$;

select pathman.set_init_callback('main.participant', 'main.after_split_participant');

CREATE FUNCTION after_split_registry(params jsonb) RETURNS void
    LANGUAGE plpgsql
    AS $_$
declare
    _c text := params->>'partition';
begin

    execute 'alter table main.' || _c || ' 
                add foreign key(tid) references main.transaction_' || substring(_c from '_(\d+)$') || '(id) 
                on delete cascade deferrable initially immediate';

    execute 'create trigger tr_' || _c || '_before_all_row
                before insert or update or delete
                on main.' || _c || '
                for each row
                execute procedure main.tr_registry_before_all_row()';

    execute 'create trigger tr_' || _c || '_after_all_stmt
               after insert or update or delete
               on main.' || _c || '
               for each statement
               execute procedure main.tr_registry_after_all_stmt()';

end
$_$;

select pathman.set_init_callback('main.registry', 'main.after_split_registry');

select pathman.create_range_partitions('main.transaction', 'pdate', '2016-01-01'::date, '1 year'::interval, 1);
select pathman.create_range_partitions('main.registry', 'pdate', '2016-01-01'::date, '1 year'::interval, 1);
select pathman.create_range_partitions('main.participant', 'pdate', '2016-01-01'::date, '1 year'::interval, 1);

test query:

do
$$
declare 
    _tid bigint;
    _pdate date := now() + '1year'::interval;
begin

    insert into main.transaction(pdate) values (_pdate) 
    returning id into _tid;

    insert into main.participant (tid, cid, pdate)
    values (_tid, 1, _pdate);

    insert into main.registry (pdate, tid, post_id)
    values (_pdate, _tid, 1);

end
$$;

Time spent on partition range insert

Hello,

I'm comparing insertion time between non-partitionned tables and partitionned tables.
In my tests, the insert ratio is 10 times slower with partitionned tables. I tried to identify the cause of this time spent.

Time spend on the trigger is greater than 98%.

So I modified the function used by the insert trigger on my test table tbl_pathman, to identify on which part of the function the most of time is spent.

CREATE OR REPLACE FUNCTION tbl_pathman_insert_trigger_func() RETURNS trigger AS $$
DECLARE                                                                                     
        v_part_relid OID;
        v_tmstp_begin TIMESTAMP;
        v_tmstp_end TIMESTAMP;                                                                   
BEGIN                                                                                       
        SELECT clock_timestamp() INTO v_tmstp_begin;
        IF TG_OP = 'INSERT' THEN
                SELECT clock_timestamp() INTO v_tmstp_end;
                INSERT INTO log_pathman_trig SELECT 1, v_tmstp_begin, v_tmstp_end, 1000.0 * extract(epoch FROM (v_tmstp_end - v_tmstp_begin));
                select v_tmstp_end INTO v_tmstp_begin;

                IF NEW.id IS NULL THEN                                                      
                        RAISE EXCEPTION 'ERROR: NULL value in partitioning key';            
                END IF;                                                                     

                SELECT clock_timestamp() INTO v_tmstp_end;
                INSERT INTO log_pathman_trig SELECT 2, v_tmstp_begin, v_tmstp_end, 1000.0 * extract(epoch FROM (v_tmstp_end - v_tmstp_begin));
                select v_tmstp_end INTO v_tmstp_begin;

                v_part_relid := public.find_or_create_range_partition(TG_RELID, NEW.id);    

                SELECT clock_timestamp() INTO v_tmstp_end;
                INSERT INTO log_pathman_trig SELECT 3, v_tmstp_begin, v_tmstp_end, 1000.0 * extract(epoch FROM (v_tmstp_end - v_tmstp_begin));
                --SELECT 3, v_tmstp_begin, v_tmstp_end, 1000.0 * extract(epoch FROM (v_tmstp_end - v_tmstp_begin));
                select v_tmstp_end INTO v_tmstp_begin;

                IF NOT v_part_relid IS NULL THEN                                            
                        EXECUTE format('INSERT INTO %s SELECT $1.*', v_part_relid::regclass)
                        USING NEW;                                                          
                ELSE                                                                        
                        RAISE EXCEPTION 'ERROR: Cannot find partition';                     
                END IF;                              
                SELECT clock_timestamp() INTO v_tmstp_end;
                INSERT INTO log_pathman_trig SELECT 4, v_tmstp_begin, v_tmstp_end, 1000.0 * extract(epoch FROM (v_tmstp_end - v_tmstp_begin));

        END IF;                                                                             
        RETURN NULL;                                                                        
END;                                                                                         
$$ LANGUAGE plpgsql;

Adding the log table:

CREATE TABLE log_pathman_trig 
(trig_id INTEGER, tmstp_begin timestamp, tmstp_end timestamp, ms_elapsed numeric(18,3));

Here are the results

trig_id Statement Percent
1 TG_OP=INSERT 4.41
2 Check ID is NULL 18.84
3 find_or_create_range_partition 18.96
4 EXECUTE format 57.78

So the statement EXECUTE format is the longest statement of the trigger.

Do you know if there is a way to improve this?
Can I help you on this point?

Regards,
Thomas

segfault in pg_pathman.so with Postgres 9.5.2 on ubuntu, debian, OSX

From my workstation running Xubuntu 16.04 with 9.5.2:

0x00007ffbd1a821e4 in pathman_rel_pathlist_hook () from /usr/lib/postgresql/9.5/lib/pg_pathman.so

similar issue (with lack of core dumps though) in same situation on Debian 8 and OSX 10.11.4

from postgres log:

2016-07-13 18:13:20 EEST [18360-2] LOG:  server process (PID 18701) was terminated by signal 11: Segmentation fault
2016-07-13 18:13:20 EEST [18360-3] DETAIL:  Failed process was running: 
        SELECT facebook.status_upsert(
            '1_1',
            'link',
            1,
            0,
            4,
            NULL,
            '',
            'name value',
            '2016-02-01T11:02:00+00:00'::timestamptz,
            '2016-02-01T11:01:00+00:00'::timestamptz
        )
2016-07-13 18:13:20 EEST [18360-4] LOG:  terminating any other active server processes

table facebook.status is range-partitioned on id

culprit function:

CREATE OR REPLACE FUNCTION facebook.status_upsert (
    _eid TEXT,
    _etype TEXT,
    _status_type INT4,
    _streaming_status INT4,
    _story_id INT4,
    _stream_id INT4,
    _description TEXT,
    _caption TEXT,
    _as_at_ TIMESTAMPTZ,
    _published_at_ TIMESTAMPTZ
) RETURNS INT AS $$
DECLARE
  _id INT;
  _unchanged BOOL;
BEGIN

-- should only update the row it something has changed. but cannot just put that condition in the criteria of the update
-- statement since we must return the id of the row regardless if it is changed or not
SELECT
  id,
  as_at = _as_at_
  AND etype = _etype
  AND status_type = _status_type
  AND streaming_status = _streaming_status
  AND story_id = _story_id
  AND description = _description
  AND caption = _caption
  AND published_at =  _published_at_
  INTO _id, _unchanged
FROM
  facebook.status fs
WHERE
  eid = _eid
;

IF FOUND AND _unchanged THEN
    RETURN _id;
END IF;

LOOP
    -- updates on partitioned tables do return the id of the record updated unlike inserts
    UPDATE facebook.status SET
      etype = _etype,
      status_type = _status_type,
      streaming_status = _streaming_status,
      story_id = _story_id,
      description = _description,
      caption = _caption,
      as_at = _as_at_,
      published_at = _published_at_,
      _as_at = EXTRACT(EPOCH FROM _as_at_),
      _published_at = EXTRACT(EPOCH FROM _published_at_)
    WHERE
      id = _id;

    IF FOUND THEN
        RETURN _id;
    END IF;

    BEGIN

        -- believe that it is highly unlikely that will get a unique_violation for this table
        -- therefore its more efficient to increment the sequence inside the loop right before the insert
        SELECT NEXTVAL('facebook.status_id_seq') INTO _id;

        -- take this out when acquisition can reliable give me the stream_id
        SELECT COALESCE(_stream_id, (SELECT stream_id FROM facebook.page WHERE eid = split_part(_eid, '_', 1))) INTO _stream_id;

        -- inserts on partitioned do not return the id of the record inserted so use explicitly allocated sequence id
        INSERT INTO facebook.status (
          id,
          eid,
          etype,
          status_type,
          streaming_status,
          story_id,
          stream_id,
          description,
          caption,
          as_at,
          published_at,
          _as_at,
          _published_at
        ) VALUES (
          _id,
          _eid,
          _etype,
          _status_type,
          _streaming_status,
          _story_id,
          _stream_id,
          _description,
          _caption,
          _as_at_,
          _published_at_,
          EXTRACT(EPOCH FROM _as_at_),
          EXTRACT(EPOCH FROM _published_at_)
        );
        RETURN _id;

    EXCEPTION WHEN unique_violation THEN
        -- loop again
    END;
END LOOP;
END
$$ language plpgsql;

ubuntu's .crash

undefined symbol: get_rel_persistence

Hi,

can anybody help me. i just installed pg_pathman and when i include shared_preload_libraries = 'pg_pathman' in postgresql.conf i get error when running postgres:
FATAL: could not load library .....pg_pathman.so : undefined symbol : get_rel_persistence

does anybody know what could be the problem?

Not working create_range_partitions with bigint or numeric data type ID

If you change the data type of the ID column to bigint or numeric pg_pathman gives an error and is disabled:

CREATE TABLE items (
    id       BIGINT PRIMARY KEY,
    name     TEXT,
    code     BIGINT);

INSERT INTO items (id, name, code)
SELECT g, md5(g::text), random() * 100000
FROM generate_series(1, 100000) as g;

SELECT create_range_partitions('items', 'id', 1, 1000);

WARNING: Constant type in some check constraint does not match the partitioned column's type
ERROR: Wrong constraint format for RANGE partition "items_1"
HINT: pg_pathman will be disabled to allow you to resolve this issue
CONTEXT: SQL statement "WITH part_data AS (DELETE FROM ONLY items RETURNING *)
INSERT INTO items SELECT * FROM part_data"
PL/pgSQL function partition_data(regclass) line 11 at EXECUTE
SQL statement "SELECT public.partition_data(parent_relid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer,boolean) line 81 at PERFORM

SELECT version()
PostgreSQL 9.5.4 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-17), 64-bit

Предсказуемое название дочерних таблиц

#5:

Я бы хотел предсказуемое имя реальных таблиц, чтобы потом на эти таблицы можно было ссылаться, грубо говоря, из крон-скриптов. Тоже самое к любым другим авто-нумеруемым элементам типа индексов и др. (если есть). Либо, не использовать шаблоны, но задокументировать и зафиксировать способ образования новых имён.

Здесь также имеется в виду название и других авто-создаваемых элементов типа индексов и др.

Can't reinstall pg_pathman: relation "i_pathman_config_params" already exists

I've tried to upgrade pg_pathman from 1.0.1 to 1.1, but there were some errors (not saved them, sorry).

I've removed pg_pathman 1.0.1 with DROP EXTENSION pg_pathman; and then make uninstall USE_PGXS=1 and the removed pg_pathman from shared_preload_libraries and restarted PostgreSQL.

Then I'm trying to install pg_pathman 1.1 with git checkout 1.1; make USE_PGXS=1; make install USE_PGXS=1 and then add pg_pathman back to shared_preload_libraries and restarted PostgreSQL.

And then:

# CREATE EXTENSION pg_pathman;
ОШИБКА:  отношение "i_pathman_config_params" уже существует
# DROP EXTENSION pg_pathman;
ОШИБКА:  расширение "pg_pathman" не существует
# CREATE EXTENSION pg_pathman;
ОШИБКА:  отношение "i_pathman_config_params" уже существует

Tried to remove that index, but with no luck:

# DROP TABLE pathman_config_params;
ОШИБКА:  таблица "pathman_config_params" не существует
# DROP INDEX i_pathman_config_params;
ОШИБКА:  индекс "i_pathman_config_params" не существует

How to fix that without dropping database?

сделать DROP TABLE при удалении всех строк реальной таблицы

#5:

Если это возможно, то сделать нано-оптимизацию для следующего кейса

Если сделали delete from megatable where column_for_range_split between xxx and yyy (или аналогичный) И диапазон полностью покрывает реальную таблицу, то делать drop real_table .

Если же условия удаления более сложные (даже приводящие к полному удалению записей из реальной таблицы) то указанную мной оптимизацию не включать.

Реальный юзекейс - удаление старых записей из статистики. удалению будет именно такое "Удалить все записи старее такой-то даты". Ну и разделение, естественно, под столбцу с таймстампом.

custom scheme unsupported

PostgreSQL 9.5.4 on x86_64-pc-linux-gnu, compiled by gcc (Debian 4.9.2-10) 4.9.2, 64-bit
(postgrespro)

CREATE EXTENSION pg_pathman SCHEMA pathman;

select pathman.disable_partitioning('main.participant')
->
ERROR: relation "pathman_config" does not exist
СТРОКА 1: SELECT parttype FROM pathman_config WHERE relname = relation

create_range_partitions ERROR: integer out of range

I have a error

snip=# select create_range_partitions('snip', 'build_id', 1, 5);
ERROR:  integer out of range
CONTEXT:  PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer,boolean) line 27 at EXECUTE

I fink problem in this place v_rows_count declared as INTEGER, but count(*) can return bigint value.

pg_pathman creates thousands partitions

Hello! Today for the second time we faced strange pg_pathman behavior.

Base table:

CREATE TABLE prices
(
linkId integer NOT NULL,
date integer NOT NULL,
price double precision NOT NULL
);

Range partition:

SELECT create_range_partitions('prices', 'date', '1451606400'::int, '2419200'::int);

Before "problem" last partition was "prices_7". last record date was 1466663582. Then we insert 3 records with date=1466664722. And... pathman put them into partition "prices_2007". So we have "prices_7", 2000 empty partitions and 3 records in "prices_2007".

We dropped all empty partitions, but serveral minutes later pathman writes new records to "prices_2766" starting with date=1466665444. 722 seconds and 759 new partitions =)

Right now we have "prices_3373" and i think it will keep growing.

What can we do to help fix this problem? We have similar problem with other table in other db and fixed it by recreate whole table from scratch. I'm going to do same thing here, but if you need some additional info - it can wait for several hours.

UPD: Almost forgot versions! Postgres 9.5.3 (https://hub.docker.com/_/postgres/), pg_pathman from master (2775b5a)

Not working INSERT ... ON CONFLICT on partition table

postgres=# explain insert into test(id,info,crt_time) values(1, md5(random()::text), now()) on conflict (id) do update set info=excluded.info,crt_time=excluded.crt_time;
                               QUERY PLAN                               
------------------------------------------------------------------------
 Insert on test  (cost=0.00..0.03 rows=1 width=44)
   Conflict Resolution: UPDATE
   Conflict Arbiter Indexes: test_pkey
   ->  Custom Scan (PartitionFilter)  (cost=0.00..0.03 rows=1 width=44)
         ->  Result  (cost=0.00..0.03 rows=1 width=44)
(5 rows)

postgres=# insert into test(id,info,crt_time) values(1, md5(random()::text), now()) on conflict (id) do update set info=excluded.info,crt_time=excluded.crt_time;
ERROR:  XX000: unexpected failure to find arbiter index
LOCATION:  ExecCheckIndexConstraints, execIndexing.c:594

добавить двух-уровневое партиционирование

простой пример: таблица с логом операций, в которой нужен поиск по идентификаторам (пользователей). для обслуживания таблицы нужно периодически удалять устаревшие данные - для этого нужна нарезка по времени (дням/неделям/месяцам). для быстрого поиска - нарезка по идентификаторам. можно, конечно, пользоваться только нарезкой по времени и индексами, но не во всех случаях это (индексирование) применимо.

Migrating existing partitions to pg_pathman

Assuming that I already use table inheritance to partition my table what I need to do to migrate to pg_pathman? My experiments show that I only need to execute

select create_partitions_from_range('notices', 'id', 0::bigint, -1::bigint, 22468047667200000);

to start using pg_pathman. Is that correct?

Please note that I had to use start_value=0 and end_value=-1 to prevent pg_statsman from creating any partitions... It is totally fine, but probably should be documented.

disable_partitioning() - DROP TRIGGER instead DROP FUNCTION

select disable_partitioning('test_items');

ЗАМЕЧАНИЕ:  функция test_schema.test_items_insert_trigger_func() не существует, пропускается
CONTEXT:  SQL-оператор: "DROP FUNCTION IF EXISTS test_schema.test_items_insert_trigger_func() CASCADE"
функция PL/pgSQL disable_partitioning(text), строка 6, оператор EXECUTE
Total query runtime: 142 msec
1 row retrieved.

Может-быть здесь ожидался DROP TRIGGER, а не DROP FUNCTION?

Примечание: расширение установлено в pg9.5 из branch'а pgpro9_5.

pg_dump not working with partitioned table: improper qualified name (too many dotted names)

Executing command (DB server is on the same machine):

pg_dump -F custom database > new_shiny.dump

Getting error:

pg_dump: [archiver (db)] query failed: ERROR:  improper qualified name (too many dotted names): id.tracker_id.data.evented_at.created_at.ambulance_status_id.in_zone.is_stop
LINE 1: COPY public.tracker_points (id, tracker_id, data, evented_at...
         ^
pg_dump: [archiver (db)] query was: COPY public.tracker_points (id, tracker_id, data, evented_at, created_at, ambulance_status_id, in_zone, is_stop) TO stdout;

And pg_dump is immediately finished with non-zero exit code.

Where id.tracker_id.data.evented_at.created_at.ambulance_status_id.in_zone.is_stop is just a list of columns of tracker_points table joined with dot.

PostgreSQL 9.5.4 with pg_pathman 1.1, 1.1.1 and master_improved_planning branch.

Database is the same as in #41

when use insert on conflict syntax, i cann't supply the constraint name or column for it.

postgres=# create table pathman_test(id int primary key, info text, crt_time timestamptz);
CREATE TABLE
postgres=# select create_range_partitions('pathman_test'::regclass, 'id', 1, 20000000, 100, false);
NOTICE:  sequence "pathman_test_seq" does not exist, skipping
 create_range_partitions 
-------------------------
                     100
(1 row)

postgres=# select disable_parent('pathman_test'::regclass);
 disable_parent 
----------------

(1 row)

postgres=# insert into pathman_test values (1,'test',now()) on conflict (id) do update set info=excluded.info;
ERROR:  unexpected failure to find arbiter index
postgres=# \set VERBOSITY verbose
postgres=# insert into pathman_test values (1,'test',now()) on conflict (id) do update set info=excluded.info;
ERROR:  XX000: unexpected failure to find arbiter index
LOCATION:  ExecCheckIndexConstraints, execIndexing.c:594
postgres=# insert into pathman_test values (1,'test',now()) on conflict on constraint pathman_test_1_pkey do update set info=excluded.info;
ERROR:  42704: constraint "pathman_test_1_pkey" for table "pathman_test" does not exist
LOCATION:  get_relation_constraint_oid, pg_constraint.c:810
postgres=# \d pathman_test_1
          Table "public.pathman_test_1"
  Column  |           Type           | Modifiers 
----------+--------------------------+-----------
 id       | integer                  | not null
 info     | text                     | 
 crt_time | timestamp with time zone | 
Indexes:
    "pathman_test_1_pkey" PRIMARY KEY, btree (id)
Check constraints:
    "pathman_pathman_test_1_1_check" CHECK (id >= 1 AND id < 20000001)
Inherits: pathman_test

postgres=# insert into pathman_test values (1,'test',now()) on conflict on constraint pathman_test_pkey do update set info=excluded.info;
ERROR:  XX000: unexpected failure to find arbiter index
LOCATION:  ExecCheckIndexConstraints, execIndexing.c:594

Compilation issue on PostgreSQL 9.5.1 on Fedora23 x64

Hello,

I encounter a compilation issue on pg_pathman, here is the result of the command make install USE_PGXS=1:

gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -DLINUX_OOM_SCORE_ADJ=0 -fpic -I. -I./ -I/usr/include/pgsql/server -I/usr/include/pgsql/internal -D_GNU_SOURCE -I/usr/include/libxml2   -c -o init.o init.c
init.c: In function ‘create_relations_hashtable’:
init.c:259:88: error: ‘HASH_BLOBS’ undeclared (first use in this function)
  relations = ShmemInitHash("Partitioning relation info", 1024, 1024, &ctl, HASH_ELEM | HASH_BLOBS);

                                                                                        ^
init.c:259:88: note: each undeclared identifier is reported only once for each function it appears in
init.c: In function ‘create_range_restrictions_hashtable’:
init.c:561:43: error: ‘HASH_BLOBS’ undeclared (first use in this function)
             1024, 1024, &ctl, HASH_ELEM | HASH_BLOBS);
                                           ^
<builtin>: recipe for target 'init.o' failed
make: *** [init.o] Error 1

Is there a problem on my laptop?

Thanks in advance for your help,
Have a nice week-end,
Regards,
Thomas

Errors while adding new range partitions

Now it's useless for new incoming data. For example I want to split my data by day(86400 sec), partition should be created every 86400 seconds but now it's just throws exception.

Constraint type not consistent when partitioning by int8

postgres=# drop table test;
postgres=# create table test(id int8 primary key, info text, crt_time timestamp);
CREATE TABLE
postgres=# 
postgres=# select create_range_partitions('test'::regclass, 'id', 1::int8, 20000000::int8, 500::int8, false);
 create_range_partitions 
-------------------------
                     500
(1 row)
postgres=# select create_range_update_trigger('test'::regclass);
 create_range_update_trigger 
-----------------------------
 postgres.test_upd_trig_func
(1 row)
postgres=# select disable_parent('test'::regclass);
 disable_parent 
----------------

(1 row)
postgres=# explain select * from test where id=1;
WARNING:  Constant type in some check constraint does not match the partitioned column's type
ERROR:  Wrong constraint format for RANGE partition "test_1"
HINT:  pg_pathman will be disabled to allow you to resolve this issue

add some utility functions for hash partitioning

HI,
there is some wrapped function for range partition , like add/attach/detach/remove partition .
but there is no wrapped funcs for hash partition, if some body like to use pg_pathman for sharding(based on fdw), i think these funcs will help them.
thanks, this an advise.

one partition for all old data

Hi,
Is there any way to automatically insert some data into one partition (table_old_data) if the value less than something?
Like this in postgres:
CREATE OR REPLACE FUNCTION table_insert_trigger()
.......
BEGIN
IF (NEW.time_key >= TIMESTAMP '2016-10-01 00:00:00' and NEW.time_key < TIMESTAMP '2016-10-02 00:00:00') THEN
INSERT into table_pt_01 values (NEW.) ;
.......................................................
ELSE
INSERT into table_old values (NEW.
) ;
END IF;
RETURN NULL;
END;

thanks

Range partitioning on non-equiwidth table.

Seems there is no support of the non-equiwidth partitioning, i.e. we know we have say 100M items per partition and we know start, end of a bigint value but there's no interval as such. This happens quite often in scientific domains (i.e. keys based on healpix indexing in astronomy, scarcely sampled time domains, molecules encoding etc), everywhere where uneven sampling is common.

add_range_partition could look like a saver, but it also relies on interval type.

Could you please add something like:
create_partitions_from_range( relation REGCLASS, attribute TEXT, start_range ANYARRAY, end_value ANYARRAY, partition_data BOOLEAN DEFAULT true)

where start_, end_range are arrays of boundaries of partitions?
or even arrays based on existing range types, int8range[] etc?

This would be a very welcomed addition.
Do you have any plans to include it?

Not optimal query plans when querying partitioned table with enable_parent set to true

Query:

SELECT "tracker_points".* FROM "tracker_points" WHERE "tracker_points"."ambulance_status_id" = 1147 AND "tracker_points"."evented_at" BETWEEN '2015-11-30 08:40:37.04006' and '2016-02-15 08:40:37.04006' ORDER BY "tracker_points"."evented_at" ASC;

Query plan (EXPLAIN):

 Sort  (cost=822974.40..822978.02 rows=1450 width=414)
   Sort Key: tracker_points.evented_at
   ->  Append  (cost=0.57..822898.26 rows=1450 width=414)
         ->  Index Scan using tracker_points_by_status_index on tracker_points  (cost=0.57..3944.45 rows=1051 width=414)
               Index Cond: ((ambulance_status_id = 1147) AND (evented_at >= '2015-11-30 08:40:37.04006'::timestamp without time zone) AND (evented_at <= '2016-02-15 08:40:37.04006'::timestamp without time zone))
         ->  Seq Scan on tracker_points_2015_11  (cost=0.00..20.65 rows=1 width=414)
               Filter: ((evented_at >= '2015-11-30 08:40:37.04006'::timestamp without time zone) AND (ambulance_status_id = 1147))
         ->  Seq Scan on tracker_points_2015_12  (cost=0.00..325808.35 rows=158 width=414)
               Filter: (ambulance_status_id = 1147)
         ->  Seq Scan on tracker_points_2016_01  (cost=0.00..493104.16 rows=239 width=414)
               Filter: (ambulance_status_id = 1147)
         ->  Seq Scan on tracker_points_2016_02  (cost=0.00..20.65 rows=1 width=414)
               Filter: ((evented_at <= '2016-02-15 08:40:37.04006'::timestamp without time zone) AND (ambulance_status_id = 1147))

Query plan for query only by single partition:

# EXPLAIN SELECT "tracker_points_2016_01".* FROM "tracker_points_2016_01" WHERE "tracker_points_2016_01"."ambulance_status_id" = 1147 AND "tracker_points_2016_01"."evented_at" BETWEEN '2015-11-30 08:40:37.04006' and '2016-02-15 08:40:37.04006' ORDER BY "tracker_points_2016_01"."evented_at" ASC;
                                                                                               QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Index Scan using tracker_points_2016_01_ambulance_status_id_evented_at_idx on tracker_points_2016_01  (cost=0.56..1685.90 rows=839 width=414)
   Index Cond: ((ambulance_status_id = 1147) AND (evented_at >= '2015-11-30 08:40:37.04006'::timestamp without time zone) AND (evented_at <= '2016-02-15 08:40:37.04006'::timestamp without time zone))

But if set enable_parent to false, then indexes are being used (expected result):

# SELECT set_enable_parent('tracker_points', false);
                                                                       QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------------
 Append  (cost=0.43..4137.51 rows=2005 width=414)
   ->  Index Scan using tracker_points_2015_11_ambulance_status_id_evented_at_idx on tracker_points_2015_11  (cost=0.43..66.93 rows=30 width=414)
         Index Cond: ((ambulance_status_id = 1147) AND (evented_at >= '2015-11-30 08:40:37.04006'::timestamp without time zone))
   ->  Index Scan using tracker_points_2015_12_ambulance_status_id_evented_at_idx on tracker_points_2015_12  (cost=0.56..2237.79 rows=1101 width=414)
         Index Cond: (ambulance_status_id = 1147)
   ->  Index Scan using tracker_points_2016_01_ambulance_status_id_evented_at_idx on tracker_points_2016_01  (cost=0.56..1824.61 rows=873 width=414)
         Index Cond: (ambulance_status_id = 1147)
   ->  Index Scan using tracker_points_2016_02_ambulance_status_id_evented_at_idx on tracker_points_2016_02  (cost=0.15..8.17 rows=1 width=414)
         Index Cond: ((ambulance_status_id = 1147) AND (evented_at <= '2016-02-15 08:40:37.04006'::timestamp without time zone))

Expected result

Postgres will use tracker_points_2016_01_ambulance_status_id_evented_at_idx index when collecting data from partition (and similarly for other partitions) when enable_parent is set to true.

Actual result

Sequential scan is performed, as a result all quickly performed queries before partitioning became extremely slow and whole program's performance was effectively ruined :-)
And while data is being moved concurrently we can't disable parent table scanning.

Technical details

  1. PostgreSQL 9.5.4 (from PGDG) on Ubuntu 14.04 with pg_pathman 1.0 and 1.1.
  2. PostgreSQL 9.6.0 (built from source with Homebrew) on OS X with pg_pathman 1.1.

pg_pathman was installed like this:

git clone https://github.com/postgrespro/pg_pathman.git /var/tmp/pg_pathman
cd /var/tmp/pg_pathman
git checkout 1.1
make USE_PGXS=1
make install USE_PGXS=1
echo "shared_preload_libraries = pg_pathman" | sudo tee -a /etc/postgresql/9.5/main/postgresql.conf
sudo service postgresql restart
psql db -c 'CREATE EXTENSION pg_pathman'

Table was partitioned with script like this:

SELECT create_range_partitions('tracker_points', 'evented_at', '2015-11-01'::timestamp, '1 month'::interval, 0, false);
SELECT add_range_partition('tracker_points', '2015-11-01'::timestamp, '2015-11-01'::timestamp + '1 month'::interval, 'tracker_points_2015_11');
SELECT append_range_partition('tracker_points', 'tracker_points_2015_12');
SELECT append_range_partition('tracker_points', 'tracker_points_2016_01');
SELECT append_range_partition('tracker_points', 'tracker_points_2016_02');
SELECT partition_table_concurrently('tracker_points');

Output of psql's \d+ (fragment):

public | tracker_points                     | таблица            | smp      | 55 GB      |
public | tracker_points_2015_11             | таблица            | smp      | 8192 bytes |
public | tracker_points_2015_12             | таблица            | smp      | 2078 MB    |
public | tracker_points_2016_01             | таблица            | smp      | 3146 MB    |
public | tracker_points_2016_02             | таблица            | smp      | 8192 bytes |

Output of psql's \d+ tracker_points:

                                              Table "public.tracker_points"
       Column        |            Type             |              Modifiers              | Storage  | Stats target | Description
---------------------+-----------------------------+-------------------------------------+----------+--------------+-------------
 id                  | uuid                        | not null default uuid_generate_v4() | plain    |              |
 tracker_id          | uuid                        | not null                            | plain    |              |
 data                | jsonb                       | not null default '{}'::jsonb        | extended |              |
 evented_at          | timestamp without time zone | not null                            | plain    |              |
 created_at          | timestamp without time zone |                                     | plain    |              |
 ambulance_status_id | integer                     |                                     | plain    |              |
 in_zone             | boolean                     | not null default true               | plain    |              |
 is_stop             | boolean                     | default false                       | plain    |              |
Indexes:
    "tracker_points_pkey" PRIMARY KEY, btree (id)
    "index_tracker_points_on_evented_at" brin (evented_at)
    "index_tracker_points_on_tracker_id" btree (tracker_id)
    "main_tracker_points_search_index" btree (tracker_id, evented_at DESC)
    "tracker_points_by_status_index" btree (ambulance_status_id, evented_at)
Child tables: tracker_points_2015_06,
    tracker_points_2015_11,
    tracker_points_2015_12,
    tracker_points_2016_01,
    tracker_points_2016_02,

Output of psql's \d+ tracker_points_2016_01:

                                              Table "public.tracker_points_2016_01"
       Column        |            Type             |              Modifiers              | Storage  | Stats target | Description
---------------------+-----------------------------+-------------------------------------+----------+--------------+-------------
 id                  | uuid                        | not null default uuid_generate_v4() | plain    |              |
 tracker_id          | uuid                        | not null                            | plain    |              |
 data                | jsonb                       | not null default '{}'::jsonb        | extended |              |
 evented_at          | timestamp without time zone | not null                            | plain    |              |
 created_at          | timestamp without time zone |                                     | plain    |              |
 ambulance_status_id | integer                     |                                     | plain    |              |
 in_zone             | boolean                     | not null default true               | plain    |              |
 is_stop             | boolean                     | default false                       | plain    |              |
Indexes:
    "tracker_points_2016_01_pkey" PRIMARY KEY, btree (id)
    "tracker_points_2016_01_ambulance_status_id_evented_at_idx" btree (ambulance_status_id, evented_at)
    "tracker_points_2016_01_evented_at_idx" brin (evented_at)
    "tracker_points_2016_01_tracker_id_evented_at_idx" btree (tracker_id, evented_at DESC)
    "tracker_points_2016_01_tracker_id_idx" btree (tracker_id)
Check constraints:
    "pathman_tracker_points_2016_01_4_check" CHECK (evented_at >= '2016-01-01 00:00:00'::timestamp without time zone AND evented_at < '2016-02-01 00:00:00'::timestamp without time zone)
Inherits: tracker_points

Also I've tried to do VACUUM ANALYZE on table tracker_points_2016_01 with no effect.

Question

  1. Can it be fixed on pg_pathman's side?
  2. Any workarounds?

Thank you for pg_pathman!

Unable to spawn new partitions on insertion

When table owner set to non-default role, creating a new partition terminates with error. Please see test script below:

drop role if exists oimc_owner;
drop table if exists raw_flows cascade; 

create role oimc_owner nologin nosuperuser;

--
--
create table if not exists raw_flows(
  rawf_begin_connection_time      timestamp not null,
  rawf_client_address             inet not null
)
with (oids = false);
--
alter table raw_flows owner to oimc_owner;
--
select
create_range_partitions(
  'raw_flows',
  'rawf_begin_connection_time',
  date_trunc('day', localtimestamp)::timestamp,
  '1 day'::interval,
  1
);

insert into raw_flows values (now()::timestamp, '127.0.0.1');
insert into raw_flows values (now()::timestamp + interval '1 day', '127.0.0.1');

This script causes error:

ERROR:  Attempt to spawn new partitions of relation "raw_flows" failed
HINT:  See server log for more details.

If the alter table raw_flows owner to oimc_owner statement is removed, all works perfectly.

Добавить в документацию описание моментов по производительности

#5:

@akorotkov публиковал сравнение производительности pg_pathman и pg_partman: Pg_pathman UPDATE and DELETE Support and Benchmark

Необходимо написать важные вещи в документации, что данный модуль замедляет незначительно SELECT, Зато значительно ускоряет такие-то операции, так как оптимизируется вот этот процесс вот в этом месте. (Я не очень силён в этом, но видимо здесь имеется в виду перестройка индексов, возможно что-то ещё).

Как ни странно довольно сложно найти адекватное описание почему вообще такой подход лучше чем большая таблица. А может нужно просто создавать кучу мелких индексов для одной большой таблицы (create index ... where ...)?

Ещё нашёл преимущество в том что реальные таблицы можно распихивать по tablespace и удалять чохом всю таблицу при удалении диапазона данных - это действительно оптимизация.

Другими словами -- надо написать плюсы и минусы использования данного модуля.

create_range_partitions() fail

Postgresql version: 9.5

Table definitions (1K rows):

comment=# \d+ comment;
                                                               Table "public.comment"
     Column     |            Type             |                          Modifiers                           | Storage  | Stats target | Description
----------------+-----------------------------+--------------------------------------------------------------+----------+--------------+-------------
 comment_id     | integer                     | not null default nextval('comment_comment_id_seq'::regclass) | plain    |              |
 created_at     | timestamp without time zone | not null                                                     | plain    |              |
 updated_at     | timestamp without time zone | not null                                                     | plain    |              |
 entity_type_id | smallint                    | not null                                                     | plain    |              |
 entity_id      | integer                     | not null                                                     | plain    |              |
 status_id      | smallint                    | not null                                                     | plain    |              |
 comment_index  | integer                     | not null                                                     | plain    |              |
 anonymously    | boolean                     | not null                                                     | plain    |              |
 user_id        | integer                     | not null                                                     | plain    |              |
 user_name      | character varying(256)      | not null                                                     | extended |              |
 user_mail      | character varying(256)      | not null                                                     | extended |              |
 user_cookie    | character varying(32)       | not null                                                     | extended |              |
 user_ip        | inet                        | not null                                                     | main     |              |
 user_agent_id  | integer                     | not null                                                     | plain    |              |
 moderator_id   | integer                     | not null                                                     | plain    |              |
 extra          | jsonb                       | not null                                                     | extended |              |
 body           | text                        | not null                                                     | extended |              |
Indexes:
    "comment_pkey" PRIMARY KEY, btree (comment_id)
    "comment_entity_id_entity_type_id_comment_index_idx" btree (entity_id, entity_type_id, comment_index)
Check constraints:
    "comment_comment_id_check" CHECK (0 < comment_id)
    "comment_comment_index_check" CHECK (0 <= comment_index)
    "comment_entity_id_check" CHECK (0 < entity_id)
    "comment_entity_type_id_check" CHECK (0 < entity_type_id)
    "comment_moderator_id_check" CHECK (0 <= moderator_id)
    "comment_status_id_check" CHECK (0 < status_id)
    "comment_user_agent_id_check" CHECK (0 <= user_agent_id)
    "comment_user_id_check" CHECK (0 <= user_id)

Result:

comment=# select create_range_partitions('comment', 'entity_id', 1, 10000);
NOTICE:  sequence "comment_seq" does not exist, skipping
CONTEXT:  SQL statement "DROP SEQUENCE IF EXISTS public.comment_seq"
PL/pgSQL function create_or_replace_sequence(text,text) line 5 at EXECUTE
SQL statement "SELECT public.create_or_replace_sequence(v_plain_schema, v_plain_relname)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 49 at PERFORM
WARNING:  Wrong CHECK constraint for relation 'comment_2'. It MUST have exact format: VARIABLE >= CONST AND VARIABLE < CONST. Skipping...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Wrong CHECK constraint for relation 'comment_2'. It MUST have exact format: VARIABLE >= CONST AND VARIABLE < CONST. Skipping...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Wrong CHECK constraint for relation 'comment_2'. It MUST have exact format: VARIABLE >= CONST AND VARIABLE < CONST. Skipping...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Wrong CHECK constraint for relation 'comment_2'. It MUST have exact format: VARIABLE >= CONST AND VARIABLE < CONST. Skipping...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Wrong CHECK constraint for relation 'comment_2'. It MUST have exact format: VARIABLE >= CONST AND VARIABLE < CONST. Skipping...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Wrong CHECK constraint for relation 'comment_2'. It MUST have exact format: VARIABLE >= CONST AND VARIABLE < CONST. Skipping...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Wrong CHECK constraint for relation 'comment_2'. It MUST have exact format: VARIABLE >= CONST AND VARIABLE < CONST. Skipping...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Wrong CHECK constraint for relation 'comment_2'. It MUST have exact format: VARIABLE >= CONST AND VARIABLE < CONST. Skipping...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Wrong CHECK constraint for relation 'comment_3'. It MUST have exact format: VARIABLE >= CONST AND VARIABLE < CONST. Skipping...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Wrong CHECK constraint for relation 'comment_1'. It MUST have exact format: VARIABLE >= CONST AND VARIABLE < CONST. Skipping...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Wrong CHECK constraint for relation 'comment_1'. It MUST have exact format: VARIABLE >= CONST AND VARIABLE < CONST. Skipping...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Wrong CHECK constraint for relation 'comment_1'. It MUST have exact format: VARIABLE >= CONST AND VARIABLE < CONST. Skipping...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Wrong CHECK constraint for relation 'comment_1'. It MUST have exact format: VARIABLE >= CONST AND VARIABLE < CONST. Skipping...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Wrong CHECK constraint for relation 'comment_1'. It MUST have exact format: VARIABLE >= CONST AND VARIABLE < CONST. Skipping...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Wrong CHECK constraint for relation 'comment_1'. It MUST have exact format: VARIABLE >= CONST AND VARIABLE < CONST. Skipping...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Wrong CHECK constraint for relation 'comment_1'. It MUST have exact format: VARIABLE >= CONST AND VARIABLE < CONST. Skipping...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Wrong CHECK constraint for relation 'comment_1'. It MUST have exact format: VARIABLE >= CONST AND VARIABLE < CONST. Skipping...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Wrong CHECK constraint for relation 'comment_3'. It MUST have exact format: VARIABLE >= CONST AND VARIABLE < CONST. Skipping...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Wrong CHECK constraint for relation 'comment_3'. It MUST have exact format: VARIABLE >= CONST AND VARIABLE < CONST. Skipping...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Wrong CHECK constraint for relation 'comment_3'. It MUST have exact format: VARIABLE >= CONST AND VARIABLE < CONST. Skipping...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Wrong CHECK constraint for relation 'comment_3'. It MUST have exact format: VARIABLE >= CONST AND VARIABLE < CONST. Skipping...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Wrong CHECK constraint for relation 'comment_3'. It MUST have exact format: VARIABLE >= CONST AND VARIABLE < CONST. Skipping...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Wrong CHECK constraint for relation 'comment_3'. It MUST have exact format: VARIABLE >= CONST AND VARIABLE < CONST. Skipping...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Wrong CHECK constraint for relation 'comment_3'. It MUST have exact format: VARIABLE >= CONST AND VARIABLE < CONST. Skipping...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Wrong CHECK constraint for relation 'comment_4'. It MUST have exact format: VARIABLE >= CONST AND VARIABLE < CONST. Skipping...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Wrong CHECK constraint for relation 'comment_4'. It MUST have exact format: VARIABLE >= CONST AND VARIABLE < CONST. Skipping...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Wrong CHECK constraint for relation 'comment_4'. It MUST have exact format: VARIABLE >= CONST AND VARIABLE < CONST. Skipping...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Wrong CHECK constraint for relation 'comment_4'. It MUST have exact format: VARIABLE >= CONST AND VARIABLE < CONST. Skipping...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Wrong CHECK constraint for relation 'comment_4'. It MUST have exact format: VARIABLE >= CONST AND VARIABLE < CONST. Skipping...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Wrong CHECK constraint for relation 'comment_4'. It MUST have exact format: VARIABLE >= CONST AND VARIABLE < CONST. Skipping...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Wrong CHECK constraint for relation 'comment_4'. It MUST have exact format: VARIABLE >= CONST AND VARIABLE < CONST. Skipping...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Wrong CHECK constraint for relation 'comment_4'. It MUST have exact format: VARIABLE >= CONST AND VARIABLE < CONST. Skipping...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Wrong CHECK constraint for relation 'comment_5'. It MUST have exact format: VARIABLE >= CONST AND VARIABLE < CONST. Skipping...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Wrong CHECK constraint for relation 'comment_5'. It MUST have exact format: VARIABLE >= CONST AND VARIABLE < CONST. Skipping...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Wrong CHECK constraint for relation 'comment_5'. It MUST have exact format: VARIABLE >= CONST AND VARIABLE < CONST. Skipping...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Wrong CHECK constraint for relation 'comment_5'. It MUST have exact format: VARIABLE >= CONST AND VARIABLE < CONST. Skipping...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Wrong CHECK constraint for relation 'comment_5'. It MUST have exact format: VARIABLE >= CONST AND VARIABLE < CONST. Skipping...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Wrong CHECK constraint for relation 'comment_5'. It MUST have exact format: VARIABLE >= CONST AND VARIABLE < CONST. Skipping...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Wrong CHECK constraint for relation 'comment_5'. It MUST have exact format: VARIABLE >= CONST AND VARIABLE < CONST. Skipping...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Wrong CHECK constraint for relation 'comment_5'. It MUST have exact format: VARIABLE >= CONST AND VARIABLE < CONST. Skipping...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Partitions 0 and 0 overlap. Disabling pathman for relation 22848...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Partitions 0 and 0 overlap. Disabling pathman for relation 22848...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Partitions 0 and 0 overlap. Disabling pathman for relation 22848...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Partitions 0 and 0 overlap. Disabling pathman for relation 22848...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Partitions 0 and 0 overlap. Disabling pathman for relation 22848...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Partitions 0 and 0 overlap. Disabling pathman for relation 22848...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Partitions 0 and 0 overlap. Disabling pathman for relation 22848...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
WARNING:  Partitions 0 and 0 overlap. Disabling pathman for relation 22848...
CONTEXT:  SQL statement "SELECT public.on_create_partitions(p_relation::regclass::oid)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 68 at PERFORM
NOTICE:  Copying data to partitions...
CONTEXT:  SQL statement "SELECT public.partition_data(p_relation)"
PL/pgSQL function create_range_partitions(regclass,text,anyelement,anyelement,integer) line 71 at PERFORM
ERROR:  ERROR: Cannot find partition P0001

Consistent hash partitioning

Hi,

I'm curious about this extension so much. Currently it supports hash partitioning out of the box which is great. But for real world cases it's super-handy to have partition key --> virtual bucket --> physical storage kind of things to reduce re-sharding routine.
I know about different approaches to work it around including smart clients or some manual table functions and mappings, but it seems to me the place where it could be done in same manner as existing partitioning ways with less client-side or manual routing table kind of things efforts, i.e "consistent hash partitioning" out of the box.

What's your take on this?

Thanks

Hooking into partition management process

In some cases it is required to perform custom actions when a partition related action is performed, such as populate triggers on partition creation or remove certain triggers (or as in my case, include it into logical replication set) when partition is detached.

It seems though event triggers can solve this issue if used correctly, but I feel like a built in solution would fit here.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.