timescaledb/test/sql/create_chunks.sql
Joshua Lockerman 949b88ef2e Initial support for PostgreSQL 12
This change includes a major refactoring to support PostgreSQL
12. Note that many tests aren't passing at this point. Changes
include, but are not limited to:

- Handle changes related to table access methods
- New way to expand hypertables since expansion has changed in
  PostgreSQL 12 (more on this below).
- Handle changes related to table expansion for UPDATE/DELETE
- Fixes for various TimescaleDB optimizations that were affected by
  planner changes in PostgreSQL (gapfill, first/last, etc.)

Before PostgreSQL 12, planning was organized something like as
follows:

 1. construct add `RelOptInfo` for base and appendrels
 2. add restrict info, joins, etc.
 3. perform the actual planning with `make_one_rel`

For our optimizations we would expand hypertables in the middle of
step 1; since nothing in the query planner before `make_one_rel` cared
about the inheritance children, we didn’t have to be too precises
about where we were doing it.

However, with PG12, and the optimizations around declarative
partitioning, PostgreSQL now does care about when the children are
expanded, since it wants as much information as possible to perform
partition-pruning. Now planning is organized like:

 1. construct add RelOptInfo for base rels only
 2. add restrict info, joins, etc.
 3. expand appendrels, removing irrelevant declarative partitions
 4. perform the actual planning with make_one_rel

Step 3 always expands appendrels, so when we also expand them during
step 1, the hypertable gets expanded twice, and things in the planner
break.

The changes to support PostgreSQL 12 attempts to solve this problem by
keeping the hypertable root marked as a non-inheritance table until
`make_one_rel` is called, and only then revealing to PostgreSQL that
it does in fact have inheritance children. While this strategy entails
the least code change on our end, the fact that the first hook we can
use to re-enable inheritance is `set_rel_pathlist_hook` it does entail
a number of annoyances:

 1. this hook is called after the sizes of tables are calculated, so we
    must recalculate the sizes of all hypertables, as they will not
    have taken the chunk sizes into account
 2. the table upon which the hook is called will have its paths planned
    under the assumption it has no inheritance children, so if it's a
    hypertable we have to replan it's paths

Unfortunately, the code for doing these is static, so we need to copy
them into our own codebase, instead of just using PostgreSQL's.

In PostgreSQL 12, UPDATE/DELETE on inheritance relations have also
changed and are now planned in two stages:

- In stage 1, the statement is planned as if it was a `SELECT` and all
  leaf tables are discovered.
- In stage 2, the original query is planned against each leaf table,
  discovered in stage 1, directly, not part of an Append.

Unfortunately, this means we cannot look in the appendrelinfo during
UPDATE/DELETE planning, in particular to determine if a table is a
chunk, as the appendrelinfo is not at the point we wish to do so
initialized. This has consequences for how we identify operations on
chunks (sometimes for blocking and something for enabling
functionality).
2020-04-14 23:12:15 +02:00

124 lines
5.2 KiB
SQL

-- This file and its contents are licensed under the Apache License 2.0.
-- Please see the included NOTICE for copyright information and
-- LICENSE-APACHE for a copy of the license.
--
-- This test will create chunks in two dimenisions, time (x) and
-- space (y), where the time dimension is aligned. The figure below
-- shows the expected result. The chunk number in the figure
-- indicates the creation order.
--
-- +
-- +
-- + +-----+ +-----+
-- + | 2 | | 3 |
-- + | +---+-+ |
-- + +-----+ 5 |6+-----+
-- + | 1 +---+-+-----+ +---------+
-- + | | |4| 7 | | 8 |
-- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
-- 0 5 10 15 20
--
-- Partitioning:
--
-- Chunk # | time | space
-- 1 | 3 | 2
-- 4 | 1 | 3
-- 5 | 5 | 3
--
CREATE TABLE chunk_test(time integer, temp float8, tag integer, color integer);
SELECT create_hypertable('chunk_test', 'time', 'tag', 2, chunk_time_interval => 3);
INSERT INTO chunk_test VALUES (4, 24.3, 1, 1);
SELECT * FROM _timescaledb_catalog.dimension_slice;
INSERT INTO chunk_test VALUES (4, 24.3, 2, 1);
INSERT INTO chunk_test VALUES (10, 24.3, 2, 1);
SELECT c.table_name AS chunk_name, d.id AS dimension_id, ds.id AS slice_id, range_start, range_end FROM _timescaledb_catalog.chunk c
LEFT JOIN _timescaledb_catalog.chunk_constraint cc ON (c.id = cc.chunk_id)
LEFT JOIN _timescaledb_catalog.dimension_slice ds ON (ds.id = cc.dimension_slice_id)
LEFT JOIN _timescaledb_catalog.dimension d ON (d.id = ds.dimension_id)
LEFT JOIN _timescaledb_catalog.hypertable h ON (d.hypertable_id = h.id)
WHERE h.schema_name = 'public' AND h.table_name = 'chunk_test'
ORDER BY c.id, d.id;
\c :TEST_DBNAME :ROLE_SUPERUSER
SELECT set_number_partitions('chunk_test', 3);
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER
SELECT set_chunk_time_interval('chunk_test', 1::bigint);
INSERT INTO chunk_test VALUES (8, 24.3, 11233, 1);
SELECT set_chunk_time_interval('chunk_test', 5::bigint);
SELECT * FROM _timescaledb_catalog.dimension;
INSERT INTO chunk_test VALUES (7, 24.3, 79669, 1);
INSERT INTO chunk_test VALUES (8, 24.3, 79669, 1);
INSERT INTO chunk_test VALUES (10, 24.3, 11233, 1);
INSERT INTO chunk_test VALUES (16, 24.3, 11233, 1);
SELECT c.table_name AS chunk_name, d.id AS dimension_id, ds.id AS slice_id, range_start, range_end FROM _timescaledb_catalog.chunk c
LEFT JOIN _timescaledb_catalog.chunk_constraint cc ON (c.id = cc.chunk_id)
LEFT JOIN _timescaledb_catalog.dimension_slice ds ON (ds.id = cc.dimension_slice_id)
LEFT JOIN _timescaledb_catalog.dimension d ON (d.id = ds.dimension_id)
LEFT JOIN _timescaledb_catalog.hypertable h ON (d.hypertable_id = h.id)
WHERE h.schema_name = 'public' AND h.table_name = 'chunk_test'
ORDER BY c.id, d.id;
--test the edges of an open partition -- INT_64_MAX and INT_64_MIN.
CREATE TABLE chunk_test_ends(time bigint, temp float8, tag integer, color integer);
SELECT create_hypertable('chunk_test_ends', 'time', chunk_time_interval => 5);
INSERT INTO chunk_test_ends VALUES ((-9223372036854775808)::bigint, 23.1, 11233, 1);
INSERT INTO chunk_test_ends VALUES (9223372036854775807::bigint, 24.1, 11233, 1);
--try to hit cache
INSERT INTO chunk_test_ends VALUES (9223372036854775807::bigint, 24.2, 11233, 1);
INSERT INTO chunk_test_ends VALUES (9223372036854775807::bigint, 24.3, 11233, 1), (9223372036854775807::bigint, 24.4, 11233, 1);
INSERT INTO chunk_test_ends VALUES ((-9223372036854775808)::bigint, 23.2, 11233, 1);
INSERT INTO chunk_test_ends VALUES ((-9223372036854775808)::bigint, 23.3, 11233, 1), ((-9223372036854775808)::bigint, 23.4, 11233, 1);
SELECT * FROM chunk_test_ends ORDER BY time asc, tag;
--further tests of set_chunk_time_interval
CREATE TABLE chunk_test2(time TIMESTAMPTZ, temp float8, tag integer, color integer);
SELECT create_hypertable('chunk_test2', 'time', 'tag', 2, chunk_time_interval => 3);
SELECT interval_length
FROM _timescaledb_catalog.dimension d
LEFT JOIN _timescaledb_catalog.hypertable h ON (d.hypertable_id = h.id)
WHERE h.schema_name = 'public' AND h.table_name = 'chunk_test2'
ORDER BY d.id;
-- should work since time column is non-INT
SELECT set_chunk_time_interval('chunk_test2', INTERVAL '1 minute');
SELECT interval_length
FROM _timescaledb_catalog.dimension d
LEFT JOIN _timescaledb_catalog.hypertable h ON (d.hypertable_id = h.id)
WHERE h.schema_name = 'public' AND h.table_name = 'chunk_test2'
ORDER BY d.id;
-- should still work for non-INT time columns
SELECT set_chunk_time_interval('chunk_test2', 1000000);
SELECT interval_length
FROM _timescaledb_catalog.dimension d
LEFT JOIN _timescaledb_catalog.hypertable h ON (d.hypertable_id = h.id)
WHERE h.schema_name = 'public' AND h.table_name = 'chunk_test2'
ORDER BY d.id;
\set ON_ERROR_STOP 0
select set_chunk_time_interval(NULL,NULL::interval);
-- should fail since time column is an int
SELECT set_chunk_time_interval('chunk_test', INTERVAL '1 minute');
-- should fail since its not a valid way to represent time
SELECT set_chunk_time_interval('chunk_test', 'foo'::TEXT);
SELECT set_chunk_time_interval('chunk_test', NULL::BIGINT);
SELECT set_chunk_time_interval('chunk_test2', NULL::BIGINT);
SELECT set_chunk_time_interval('chunk_test2', NULL::INTERVAL);
\set ON_ERROR_STOP 1