timescaledb/sql/chunk_constraint.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

69 lines
2.9 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.
-- create constraint on newly created chunk based on hypertable constraint
CREATE OR REPLACE FUNCTION _timescaledb_internal.chunk_constraint_add_table_constraint(
chunk_constraint_row _timescaledb_catalog.chunk_constraint
)
RETURNS VOID LANGUAGE PLPGSQL AS
$BODY$
DECLARE
chunk_row _timescaledb_catalog.chunk;
hypertable_row _timescaledb_catalog.hypertable;
constraint_oid OID;
constraint_type CHAR;
check_sql TEXT;
def TEXT;
indx_tablespace NAME;
tablespace_def TEXT;
BEGIN
SELECT * INTO STRICT chunk_row FROM _timescaledb_catalog.chunk c WHERE c.id = chunk_constraint_row.chunk_id;
SELECT * INTO STRICT hypertable_row FROM _timescaledb_catalog.hypertable h WHERE h.id = chunk_row.hypertable_id;
IF chunk_constraint_row.dimension_slice_id IS NOT NULL THEN
RAISE 'cannot create dimension constraint %', chunk_constraint_row;
ELSIF chunk_constraint_row.hypertable_constraint_name IS NOT NULL THEN
SELECT oid, contype INTO STRICT constraint_oid, constraint_type FROM pg_constraint
WHERE conname=chunk_constraint_row.hypertable_constraint_name AND
conrelid = format('%I.%I', hypertable_row.schema_name, hypertable_row.table_name)::regclass::oid;
IF constraint_type IN ('p','u') THEN
-- since primary keys and unique constraints are backed by an index
-- they might have an index tablespace assigned
-- the tablspace is not part of the constraint definition so
-- we have to append it explicitly to preserve it
SELECT T.spcname INTO indx_tablespace
FROM pg_constraint C, pg_class I, pg_tablespace T
WHERE C.oid = constraint_oid AND C.contype IN ('p', 'u') AND I.oid = C.conindid AND I.reltablespace = T.oid;
def := pg_get_constraintdef(constraint_oid);
IF indx_tablespace IS NOT NULL THEN
def := format('%s USING INDEX TABLESPACE %I', def, indx_tablespace);
END IF;
ELSIF constraint_type = 't' THEN
-- constraint triggers are copied separately with normal triggers
def := NULL;
ELSE
def := pg_get_constraintdef(constraint_oid);
END IF;
ELSE
RAISE 'unknown constraint type';
END IF;
IF def IS NOT NULL THEN
-- to allow for custom types with operators outside of pg_catalog
-- we set search_path to @extschema@
SET LOCAL search_path TO @extschema@, pg_temp;
EXECUTE pg_catalog.format(
$$ ALTER TABLE %I.%I ADD CONSTRAINT %I %s $$,
chunk_row.schema_name, chunk_row.table_name, chunk_constraint_row.constraint_name, def
);
END IF;
END
$BODY$ SET search_path TO pg_catalog, pg_temp;