timescaledb/sql/util_internal_table_ddl.sql
Erik Nordström a51d21efbe Fix issue creating dimensional constraints
During chunk creation, the chunk's dimensional CHECK constraints are
created via an "upcall" to PL/pgSQL code. However, creating
dimensional constraints in PL/pgSQL code sometimes fails, especially
during high-concurrency inserts, because PL/pgSQL code scans metadata
using a snapshot that might not see the same metadata as the C
code. As a result, chunk creation sometimes fail during constraint
creation.

To fix this issue, implement dimensional CHECK-constraint creation in
C code. Other constraints (FK, PK, etc.) are still created via an
upcall, but should probably also be rewritten in C. However, since
these constraints don't depend on recently updated metadata, this is
left to a future change.

Fixes #5456
2023-03-24 10:55:08 +01:00

73 lines
2.4 KiB
PL/PgSQL

-- 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 file contains functions associated with creating new
-- hypertables.
-- Outputs the create_hypertable command to recreate the given hypertable.
--
-- This is currently used internally for our single hypertable backup tool
-- so that it knows how to restore the hypertable without user intervention.
--
-- It only works for hypertables with up to 2 dimensions.
CREATE OR REPLACE FUNCTION _timescaledb_internal.get_create_command(
table_name NAME
)
RETURNS TEXT LANGUAGE PLPGSQL VOLATILE AS
$BODY$
DECLARE
h_id INTEGER;
schema_name NAME;
time_column NAME;
time_interval BIGINT;
space_column NAME;
space_partitions INTEGER;
dimension_cnt INTEGER;
dimension_row record;
ret TEXT;
BEGIN
SELECT h.id, h.schema_name
FROM _timescaledb_catalog.hypertable AS h
WHERE h.table_name = get_create_command.table_name
INTO h_id, schema_name;
IF h_id IS NULL THEN
RAISE EXCEPTION 'hypertable "%" not found', table_name
USING ERRCODE = 'TS101';
END IF;
SELECT COUNT(*)
FROM _timescaledb_catalog.dimension d
WHERE d.hypertable_id = h_id
INTO STRICT dimension_cnt;
IF dimension_cnt > 2 THEN
RAISE EXCEPTION 'get_create_command only supports hypertables with up to 2 dimensions'
USING ERRCODE = 'TS101';
END IF;
FOR dimension_row IN
SELECT *
FROM _timescaledb_catalog.dimension d
WHERE d.hypertable_id = h_id
LOOP
IF dimension_row.interval_length IS NOT NULL THEN
time_column := dimension_row.column_name;
time_interval := dimension_row.interval_length;
ELSIF dimension_row.num_slices IS NOT NULL THEN
space_column := dimension_row.column_name;
space_partitions := dimension_row.num_slices;
END IF;
END LOOP;
ret := format($$SELECT create_hypertable('%I.%I', '%s'$$, schema_name, table_name, time_column);
IF space_column IS NOT NULL THEN
ret := ret || format($$, '%I', %s$$, space_column, space_partitions);
END IF;
ret := ret || format($$, chunk_time_interval => %s, create_default_indexes=>FALSE);$$, time_interval);
RETURN ret;
END
$BODY$ SET search_path TO pg_catalog, pg_temp;