Fix non-default tablespaces with constraints

If a hypertable uses a non-default tablespace for its primary or
unique constraints with additional DEFERRABLE or INITIALLY DEFERRED
characteristics then any chunk creation will fail with syntax error. We
now set the tablespace via a separate command for such constraints for
the chunks.

Fixes #6338
This commit is contained in:
Nikhil Sontakke 2023-11-22 15:38:12 +05:30 committed by Nikhil
parent 8745063bd4
commit 51d92a3638
5 changed files with 85 additions and 6 deletions

2
.unreleased/fix_6339 Normal file
View File

@ -0,0 +1,2 @@
Fixes: #6339 Fix tablespace with constraints
Thanks: @lyp-bobi for reporting the issue

View File

@ -40,10 +40,6 @@ BEGIN
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;
@ -63,6 +59,18 @@ BEGIN
$$ ALTER TABLE %I.%I ADD CONSTRAINT %I %s $$,
chunk_row.schema_name, chunk_row.table_name, chunk_constraint_row.constraint_name, def
);
-- if constraint (primary or unique) needs a tablespace then add it
-- via a separate ALTER INDEX SET TABLESPACE command. We cannot append it
-- to the "def" string above since it leads to a SYNTAX error when
-- "DEFERRABLE" or "INITIALLY DEFERRED" are used in the constraint
IF indx_tablespace IS NOT NULL THEN
EXECUTE pg_catalog.format(
$$ ALTER INDEX %I.%I SET TABLESPACE %I $$,
chunk_row.schema_name, chunk_constraint_row.constraint_name, indx_tablespace
);
END IF;
END IF;
END
$BODY$ SET search_path TO pg_catalog, pg_temp;

View File

@ -333,3 +333,72 @@ BEGIN
END LOOP;
END;
$$ LANGUAGE PLPGSQL;
DROP FUNCTION _timescaledb_functions.chunk_constraint_add_table_constraint(
chunk_constraint_row _timescaledb_catalog.chunk_constraint
);
CREATE FUNCTION _timescaledb_functions.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;

View File

@ -812,7 +812,7 @@ CREATE TABLE tbl (
fk_id int,
id int,
time timestamp,
CONSTRAINT pk PRIMARY KEY (time, id) USING INDEX TABLESPACE tablespace1);
CONSTRAINT pk PRIMARY KEY (time, id) USING INDEX TABLESPACE tablespace1 DEFERRABLE INITIALLY DEFERRED);
SELECT create_hypertable('tbl', 'time');
WARNING: column type "timestamp without time zone" used for "time" does not follow best practices
create_hypertable

View File

@ -612,7 +612,7 @@ CREATE TABLE tbl (
fk_id int,
id int,
time timestamp,
CONSTRAINT pk PRIMARY KEY (time, id) USING INDEX TABLESPACE tablespace1);
CONSTRAINT pk PRIMARY KEY (time, id) USING INDEX TABLESPACE tablespace1 DEFERRABLE INITIALLY DEFERRED);
SELECT create_hypertable('tbl', 'time');