mirror of
https://github.com/timescale/timescaledb.git
synced 2025-05-15 10:11:29 +08:00
Add support for hypertable constraints
This PR add support for primary-key, foreign-key, unique, and exclusion constraints. Previously supported are CHECK and NOT NULL constraints. Now, foreign key constraints where a hypertable references a plain table is support (while vice versa, with a plain table references a hypertable, is still not).
This commit is contained in:
parent
3b2afb6e9a
commit
4dcbe6114d
1
Makefile
1
Makefile
@ -49,7 +49,6 @@ SRCS = \
|
||||
src/dimension_slice.c \
|
||||
src/dimension_vector.c \
|
||||
src/hypercube.c \
|
||||
src/ddl_utils.c \
|
||||
src/chunk_constraint.c \
|
||||
src/partitioning.c \
|
||||
src/planner_utils.c \
|
||||
|
@ -63,6 +63,11 @@ docker rm -f timescaledb-clean
|
||||
docker run -d --name timescaledb-clean -v ${BASE_DIR}:/src -v ${CLEAN_VOLUME}:/var/lib/postgresql/data ${UPDATE_TO_IMAGE}:${UPDATE_TO_TAG}
|
||||
wait_for_pg timescaledb-clean
|
||||
|
||||
#same restart of sequences needed on update container in case the update script modified sequences
|
||||
docker rm -f timescaledb-updated
|
||||
docker run -d --name timescaledb-updated -v ${BASE_DIR}:/src -v ${UPDATE_VOLUME}:/var/lib/postgresql/data ${UPDATE_TO_IMAGE}:${UPDATE_TO_TAG}
|
||||
wait_for_pg timescaledb-updated
|
||||
|
||||
echo "Testing"
|
||||
docker exec timescaledb-updated /bin/bash \
|
||||
-c "psql -X -v ECHO=ALL -h localhost -U postgres -d single -f /src/test/sql/updates/test-0.1.1.sql" > ${PGTEST_TMPDIR}/updated.out
|
||||
|
@ -12,6 +12,7 @@ CREATE OR REPLACE FUNCTION _timescaledb_internal.dimension_calculate_default_ran
|
||||
OUT range_end BIGINT)
|
||||
AS '$libdir/timescaledb', 'dimension_calculate_closed_range_default' LANGUAGE C STABLE;
|
||||
|
||||
|
||||
-- Trigger for when chunk rows are changed.
|
||||
-- On Insert: create chunk table, add indexes, add triggers.
|
||||
-- On Delete: drop table
|
||||
@ -21,6 +22,7 @@ $BODY$
|
||||
DECLARE
|
||||
kind pg_class.relkind%type;
|
||||
hypertable_row _timescaledb_catalog.hypertable;
|
||||
main_table_oid OID;
|
||||
BEGIN
|
||||
IF TG_OP = 'INSERT' THEN
|
||||
PERFORM _timescaledb_internal.chunk_create_table(NEW.id);
|
||||
@ -28,14 +30,21 @@ BEGIN
|
||||
PERFORM _timescaledb_internal.create_chunk_index_row(NEW.schema_name, NEW.table_name,
|
||||
hi.main_schema_name, hi.main_index_name, hi.definition)
|
||||
FROM _timescaledb_catalog.hypertable_index hi
|
||||
WHERE hi.hypertable_id = NEW.hypertable_id;
|
||||
WHERE hi.hypertable_id = NEW.hypertable_id
|
||||
ORDER BY format('%I.%I',main_schema_name, main_index_name)::regclass;
|
||||
|
||||
SELECT * INTO STRICT hypertable_row FROM _timescaledb_catalog.hypertable WHERE id = NEW.hypertable_id;
|
||||
main_table_oid := format('%I.%I', hypertable_row.schema_name, hypertable_row.table_name)::regclass;
|
||||
|
||||
PERFORM _timescaledb_internal.create_chunk_constraint(NEW.id, oid)
|
||||
FROM pg_constraint
|
||||
WHERE conrelid = main_table_oid
|
||||
AND _timescaledb_internal.need_chunk_constraint(NEW.hypertable_id, oid);
|
||||
|
||||
PERFORM _timescaledb_internal.create_chunk_trigger(NEW.id, tgname,
|
||||
_timescaledb_internal.get_general_trigger_definition(oid))
|
||||
FROM pg_trigger
|
||||
WHERE tgrelid = format('%I.%I', hypertable_row.schema_name, hypertable_row.table_name)::regclass
|
||||
WHERE tgrelid = main_table_oid
|
||||
AND _timescaledb_internal.need_chunk_trigger(NEW.hypertable_id, oid);
|
||||
|
||||
RETURN NEW;
|
||||
|
177
sql/chunk_constraint.sql
Normal file
177
sql/chunk_constraint.sql
Normal file
@ -0,0 +1,177 @@
|
||||
-- Creates a constraint on a chunk.
|
||||
CREATE OR REPLACE FUNCTION _timescaledb_internal.create_chunk_constraint(
|
||||
chunk_id INTEGER,
|
||||
constraint_oid OID
|
||||
)
|
||||
RETURNS VOID LANGUAGE PLPGSQL AS
|
||||
$BODY$
|
||||
DECLARE
|
||||
sql_code TEXT;
|
||||
constraint_row pg_constraint;
|
||||
chunk_row _timescaledb_catalog.chunk;
|
||||
BEGIN
|
||||
SELECT * INTO STRICT constraint_row FROM pg_constraint WHERE OID = constraint_oid;
|
||||
SELECT * INTO STRICT chunk_row FROM _timescaledb_catalog.chunk WHERE id = chunk_id;
|
||||
|
||||
INSERT INTO _timescaledb_catalog.chunk_constraint (hypertable_constraint_name, chunk_id, constraint_name)
|
||||
SELECT constraint_row.conname, chunk_row.id, format('%s_%s_%s', chunk_id, nextval('_timescaledb_catalog.chunk_constraint_name'), constraint_row.conname);
|
||||
END
|
||||
$BODY$;
|
||||
|
||||
-- Drop a constraint on a chunk
|
||||
-- static
|
||||
CREATE OR REPLACE FUNCTION _timescaledb_internal.drop_chunk_constraint(
|
||||
chunk_id INTEGER,
|
||||
constraint_name NAME
|
||||
)
|
||||
RETURNS VOID LANGUAGE PLPGSQL AS
|
||||
$BODY$
|
||||
DECLARE
|
||||
chunk_row _timescaledb_catalog.chunk;
|
||||
BEGIN
|
||||
DELETE FROM _timescaledb_catalog.chunk_constraint c
|
||||
WHERE c.hypertable_constraint_name = drop_chunk_constraint.constraint_name
|
||||
AND c.chunk_id = drop_chunk_constraint.chunk_id;
|
||||
END
|
||||
$BODY$;
|
||||
|
||||
-- do I need to add a hypertable constraint to the chunks?;
|
||||
CREATE OR REPLACE FUNCTION _timescaledb_internal.need_chunk_constraint(
|
||||
hypertable_id INTEGER,
|
||||
constraint_oid OID
|
||||
)
|
||||
RETURNS BOOLEAN LANGUAGE PLPGSQL VOLATILE AS
|
||||
$BODY$
|
||||
DECLARE
|
||||
constraint_row record;
|
||||
BEGIN
|
||||
SELECT * INTO STRICT constraint_row FROM pg_constraint WHERE OID = constraint_oid;
|
||||
|
||||
IF constraint_row.contype IN ('c') THEN
|
||||
-- check and not null constraints handled by regular inheritance (from docs):
|
||||
-- All check constraints and not-null constraints on a parent table are automatically inherited by its children,
|
||||
-- unless explicitly specified otherwise with NO INHERIT clauses. Other types of constraints
|
||||
-- (unique, primary key, and foreign key constraints) are not inherited."
|
||||
|
||||
IF constraint_row.connoinherit THEN
|
||||
RAISE 'NO INHERIT option not supported on hypertables: %', constraint_row.conname
|
||||
USING ERRCODE = 'IO101';
|
||||
END IF;
|
||||
|
||||
RETURN FALSE;
|
||||
END IF;
|
||||
RETURN TRUE;
|
||||
END
|
||||
$BODY$;
|
||||
|
||||
-- Creates a constraint on all chunks for a hypertable.
|
||||
CREATE OR REPLACE FUNCTION _timescaledb_internal.add_constraint(
|
||||
hypertable_id INTEGER,
|
||||
constraint_oid OID
|
||||
)
|
||||
RETURNS VOID LANGUAGE PLPGSQL VOLATILE AS
|
||||
$BODY$
|
||||
DECLARE
|
||||
constraint_row pg_constraint;
|
||||
hypertable_row _timescaledb_catalog.hypertable;
|
||||
BEGIN
|
||||
IF _timescaledb_internal.need_chunk_constraint(hypertable_id, constraint_oid) THEN
|
||||
SELECT * INTO STRICT constraint_row FROM pg_constraint WHERE OID = constraint_oid;
|
||||
|
||||
--check the validity of an index if a constrain uses an index
|
||||
--note: foreign-key constraints are excluded because they point to indexes on the foreign table /not/ the hypertable
|
||||
IF constraint_row.conindid <> 0 AND constraint_row.contype != 'f' THEN
|
||||
SELECT * INTO STRICT hypertable_row FROM _timescaledb_catalog.hypertable WHERE id = hypertable_id;
|
||||
PERFORM _timescaledb_internal.check_index(constraint_row.conindid, hypertable_row);
|
||||
END IF;
|
||||
|
||||
PERFORM _timescaledb_internal.create_chunk_constraint(c.id, constraint_oid)
|
||||
FROM _timescaledb_catalog.chunk c
|
||||
WHERE c.hypertable_id = add_constraint.hypertable_id;
|
||||
END IF;
|
||||
END
|
||||
$BODY$;
|
||||
|
||||
CREATE OR REPLACE FUNCTION _timescaledb_internal.add_constraint_by_name(
|
||||
hypertable_id INTEGER,
|
||||
constraint_name name
|
||||
)
|
||||
RETURNS VOID LANGUAGE PLPGSQL VOLATILE AS
|
||||
$BODY$
|
||||
DECLARE
|
||||
constraint_oid OID;
|
||||
BEGIN
|
||||
SELECT oid INTO STRICT constraint_oid FROM pg_constraint WHERE conname = constraint_name
|
||||
AND conrelid = _timescaledb_internal.main_table_from_hypertable(hypertable_id);
|
||||
|
||||
PERFORM _timescaledb_internal.add_constraint(hypertable_id, constraint_oid);
|
||||
END
|
||||
$BODY$;
|
||||
|
||||
|
||||
-- Drops constraint on all chunks for a hypertable.
|
||||
CREATE OR REPLACE FUNCTION _timescaledb_internal.drop_constraint(
|
||||
hypertable_id INTEGER,
|
||||
constraint_name NAME
|
||||
)
|
||||
RETURNS VOID LANGUAGE PLPGSQL VOLATILE AS
|
||||
$BODY$
|
||||
BEGIN
|
||||
PERFORM _timescaledb_internal.drop_chunk_constraint(c.id, constraint_name)
|
||||
FROM _timescaledb_catalog.chunk c
|
||||
WHERE c.hypertable_id = drop_constraint.hypertable_id;
|
||||
END
|
||||
$BODY$;
|
||||
|
||||
CREATE OR REPLACE FUNCTION _timescaledb_internal.on_change_chunk_constraint()
|
||||
RETURNS TRIGGER LANGUAGE PLPGSQL AS
|
||||
$BODY$
|
||||
DECLARE
|
||||
chunk_row _timescaledb_catalog.chunk;
|
||||
constraint_oid OID;
|
||||
ds_row _timescaledb_catalog.dimension_slice;
|
||||
sql_code TEXT;
|
||||
BEGIN
|
||||
IF TG_OP = 'INSERT' THEN
|
||||
SELECT * INTO STRICT chunk_row FROM _timescaledb_catalog.chunk c WHERE c.id = NEW.chunk_id;
|
||||
IF NEW.dimension_slice_id IS NOT NULL THEN
|
||||
SELECT * INTO STRICT ds_row FROM _timescaledb_catalog.dimension_slice ds WHERE ds.id = NEW.dimension_slice_id;
|
||||
EXECUTE format(
|
||||
$$
|
||||
ALTER TABLE %1$I.%2$I
|
||||
ADD CONSTRAINT %3$s CHECK(%4$s)
|
||||
$$,
|
||||
chunk_row.schema_name, chunk_row.table_name,
|
||||
NEW.constraint_name,
|
||||
_timescaledb_internal.dimension_slice_get_constraint_sql(ds_row.id)
|
||||
);
|
||||
ELSIF NEW.hypertable_constraint_name IS NOT NULL THEN
|
||||
SELECT con.oid INTO STRICT constraint_oid
|
||||
FROM _timescaledb_catalog.chunk c
|
||||
INNER JOIN _timescaledb_catalog.hypertable h ON (h.id = c.hypertable_id)
|
||||
INNER JOIN pg_constraint con ON (con.conrelid = format('%I.%I',h.schema_name, h.table_name)::regclass
|
||||
AND con.conname = NEW.hypertable_constraint_name)
|
||||
WHERE c.id = NEW.chunk_id;
|
||||
|
||||
sql_code := format($$ ALTER TABLE %I.%I ADD CONSTRAINT %I %s $$,
|
||||
chunk_row.schema_name, chunk_row.table_name, NEW.constraint_name, pg_get_constraintdef(constraint_oid)
|
||||
);
|
||||
EXECUTE sql_code;
|
||||
ELSE
|
||||
RAISE 'Unknown constraint type';
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
ELSIF TG_OP = 'DELETE' THEN
|
||||
SELECT * INTO chunk_row FROM _timescaledb_catalog.chunk c WHERE c.id = OLD.chunk_id;
|
||||
IF FOUND THEN
|
||||
EXECUTE format($$ ALTER TABLE %I.%I DROP CONSTRAINT %I $$,
|
||||
chunk_row.schema_name, chunk_row.table_name, OLD.constraint_name
|
||||
);
|
||||
END IF;
|
||||
RETURN OLD;
|
||||
END IF;
|
||||
|
||||
PERFORM _timescaledb_internal.on_trigger_error(TG_OP, TG_TABLE_SCHEMA, TG_TABLE_NAME);
|
||||
END
|
||||
$BODY$;
|
||||
|
@ -138,6 +138,10 @@ BEGIN
|
||||
USING ERRCODE = 'IO101';
|
||||
END;
|
||||
|
||||
PERFORM _timescaledb_internal.add_constraint(hypertable_row.id, oid)
|
||||
FROM pg_constraint
|
||||
WHERE conrelid = main_table;
|
||||
|
||||
PERFORM 1
|
||||
FROM pg_index,
|
||||
LATERAL _timescaledb_internal.add_index(
|
||||
@ -146,7 +150,8 @@ BEGIN
|
||||
(SELECT relname FROM pg_class WHERE oid = indexrelid::regclass),
|
||||
_timescaledb_internal.get_general_index_definition(indexrelid, indrelid, hypertable_row)
|
||||
)
|
||||
WHERE indrelid = main_table;
|
||||
WHERE indrelid = main_table AND _timescaledb_internal.need_chunk_index(hypertable_row.id, pg_index.indexrelid)
|
||||
ORDER BY pg_index.indexrelid;
|
||||
|
||||
PERFORM 1
|
||||
FROM pg_trigger,
|
||||
|
@ -161,6 +161,24 @@ BEGIN
|
||||
END
|
||||
$BODY$;
|
||||
|
||||
-- do I need to add a hypertable index to the chunks?;
|
||||
CREATE OR REPLACE FUNCTION _timescaledb_internal.need_chunk_index(
|
||||
hypertable_id INTEGER,
|
||||
index_oid OID
|
||||
)
|
||||
RETURNS BOOLEAN LANGUAGE PLPGSQL VOLATILE AS
|
||||
$BODY$
|
||||
DECLARE
|
||||
associated_constraint BOOLEAN;
|
||||
BEGIN
|
||||
--do not add an index with associated constraints
|
||||
SELECT count(*) > 0 INTO STRICT associated_constraint FROM pg_constraint WHERE conindid = index_oid;
|
||||
|
||||
RETURN NOT associated_constraint;
|
||||
END
|
||||
$BODY$;
|
||||
|
||||
|
||||
-- Add an index to a hypertable
|
||||
CREATE OR REPLACE FUNCTION _timescaledb_internal.add_index(
|
||||
hypertable_id INTEGER,
|
||||
@ -174,26 +192,21 @@ INSERT INTO _timescaledb_catalog.hypertable_index (hypertable_id, main_schema_na
|
||||
VALUES (hypertable_id, main_schema_name, main_index_name, definition);
|
||||
$BODY$;
|
||||
|
||||
-- Add a trigger to a hypertable
|
||||
CREATE OR REPLACE FUNCTION _timescaledb_internal.trigger_is_row_trigger(tgtype int2) RETURNS BOOLEAN
|
||||
AS '$libdir/timescaledb', 'trigger_is_row_trigger' LANGUAGE C IMMUTABLE STRICT;
|
||||
|
||||
-- do I need to add a hypertable trigger to the chunks?
|
||||
CREATE OR REPLACE FUNCTION _timescaledb_internal.need_chunk_trigger(
|
||||
hypertable_id INTEGER,
|
||||
trigger_oid OID
|
||||
)
|
||||
RETURNS BOOLEAN LANGUAGE PLPGSQL VOLATILE AS
|
||||
RETURNS BOOLEAN LANGUAGE SQL STABLE AS
|
||||
$BODY$
|
||||
DECLARE
|
||||
trigger_row record;
|
||||
BEGIN
|
||||
SELECT * INTO STRICT trigger_row FROM pg_trigger WHERE OID = trigger_oid;
|
||||
|
||||
IF (trigger_row.tgtype & (1 << 0) != 0) THEN
|
||||
-- row trigger
|
||||
RETURN TRUE;
|
||||
END IF;
|
||||
RETURN FALSE;
|
||||
END
|
||||
-- row trigger and not an internal trigger used for constraints
|
||||
SELECT _timescaledb_internal.trigger_is_row_trigger(t.tgtype) AND NOT t.tgisinternal FROM pg_trigger t WHERE OID = trigger_oid;
|
||||
$BODY$;
|
||||
|
||||
|
||||
-- Add a trigger to a hypertable
|
||||
CREATE OR REPLACE FUNCTION _timescaledb_internal.add_trigger(
|
||||
hypertable_id INTEGER,
|
||||
@ -273,6 +286,37 @@ BEGIN
|
||||
END
|
||||
$BODY$;
|
||||
|
||||
--Makes sure the index is valid for a hypertable.
|
||||
CREATE OR REPLACE FUNCTION _timescaledb_internal.check_index(index_oid REGCLASS, hypertable_row _timescaledb_catalog.hypertable)
|
||||
RETURNS VOID LANGUAGE plpgsql STABLE AS
|
||||
$BODY$
|
||||
DECLARE
|
||||
index_row RECORD;
|
||||
missing_column TEXT;
|
||||
BEGIN
|
||||
SELECT * INTO STRICT index_row FROM pg_index WHERE indexrelid = index_oid;
|
||||
IF index_row.indisunique OR index_row.indisexclusion THEN
|
||||
-- unique/exclusion index must contain time and all partition dimension columns.
|
||||
|
||||
-- get any partitioning columns that are not included in the index.
|
||||
SELECT d.column_name INTO missing_column
|
||||
FROM _timescaledb_catalog.dimension d
|
||||
WHERE d.hypertable_id = hypertable_row.id AND
|
||||
d.column_name NOT IN (
|
||||
SELECT attname
|
||||
FROM pg_attribute
|
||||
WHERE attrelid = index_row.indrelid AND
|
||||
attnum = ANY(index_row.indkey)
|
||||
);
|
||||
|
||||
IF missing_column IS NOT NULL THEN
|
||||
RAISE EXCEPTION 'Cannot create a unique index without the column: % (used in partitioning)', missing_column
|
||||
USING ERRCODE = 'IO103';
|
||||
END IF;
|
||||
END IF;
|
||||
END
|
||||
$BODY$;
|
||||
|
||||
-- Create the "general definition" of an index. The general definition
|
||||
-- is the corresponding create index command with the placeholders /*TABLE_NAME*/
|
||||
-- and /*INDEX_NAME*/
|
||||
@ -288,8 +332,6 @@ DECLARE
|
||||
def TEXT;
|
||||
index_name TEXT;
|
||||
c INTEGER;
|
||||
index_row RECORD;
|
||||
missing_column TEXT;
|
||||
BEGIN
|
||||
-- Get index definition
|
||||
def := pg_get_indexdef(index_oid);
|
||||
@ -298,28 +340,7 @@ BEGIN
|
||||
RAISE EXCEPTION 'Cannot process index with no definition: %', index_oid::TEXT;
|
||||
END IF;
|
||||
|
||||
SELECT * INTO STRICT index_row FROM pg_index WHERE indexrelid = index_oid;
|
||||
|
||||
IF index_row.indisunique THEN
|
||||
-- unique index must contain time and all partition dimension columns.
|
||||
|
||||
-- get any partitioning columns that are not included in the index.
|
||||
SELECT d.column_name INTO missing_column
|
||||
FROM _timescaledb_catalog.dimension d
|
||||
WHERE d.hypertable_id = hypertable_row.id AND
|
||||
d.column_name NOT IN (
|
||||
SELECT attname
|
||||
FROM pg_attribute
|
||||
WHERE attrelid = table_oid AND
|
||||
attnum = ANY(index_row.indkey)
|
||||
);
|
||||
|
||||
IF missing_column IS NOT NULL THEN
|
||||
RAISE EXCEPTION 'Cannot create a unique index without the column: % (used in partitioning)', missing_column
|
||||
USING ERRCODE = 'IO103';
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
PERFORM _timescaledb_internal.check_index(index_oid, hypertable_row);
|
||||
|
||||
SELECT count(*) INTO c
|
||||
FROM regexp_matches(def, 'ON '||table_oid::TEXT || ' USING', 'g');
|
||||
|
@ -9,14 +9,6 @@
|
||||
|
||||
*/
|
||||
|
||||
CREATE OR REPLACE FUNCTION _timescaledb_internal.ddl_is_change_owner(pg_ddl_command)
|
||||
RETURNS bool IMMUTABLE STRICT
|
||||
AS '$libdir/timescaledb' LANGUAGE C;
|
||||
|
||||
CREATE OR REPLACE FUNCTION _timescaledb_internal.ddl_change_owner_to(pg_ddl_command)
|
||||
RETURNS name IMMUTABLE STRICT
|
||||
AS '$libdir/timescaledb' LANGUAGE C;
|
||||
|
||||
-- Handles ddl create index commands on hypertables
|
||||
CREATE OR REPLACE FUNCTION _timescaledb_internal.ddl_process_create_index()
|
||||
RETURNS event_trigger LANGUAGE plpgsql
|
||||
@ -49,7 +41,7 @@ BEGIN
|
||||
hypertable_row.schema_name,
|
||||
(SELECT relname FROM pg_class WHERE oid = info.objid::regclass),
|
||||
def
|
||||
);
|
||||
) WHERE _timescaledb_internal.need_chunk_index(hypertable_row.id, info.objid);
|
||||
END LOOP;
|
||||
END
|
||||
$BODY$;
|
||||
@ -135,6 +127,9 @@ BEGIN
|
||||
END
|
||||
$BODY$;
|
||||
|
||||
|
||||
|
||||
|
||||
-- Handles ddl drop index commands on hypertables
|
||||
CREATE OR REPLACE FUNCTION _timescaledb_internal.ddl_process_drop_index()
|
||||
RETURNS event_trigger LANGUAGE plpgsql
|
||||
@ -180,42 +175,29 @@ BEGIN
|
||||
END
|
||||
$BODY$;
|
||||
|
||||
CREATE OR REPLACE FUNCTION _timescaledb_internal.ddl_process_alter_table()
|
||||
RETURNS event_trigger LANGUAGE plpgsql
|
||||
CREATE OR REPLACE FUNCTION _timescaledb_internal.ddl_change_owner(main_table OID, new_table_owner NAME)
|
||||
RETURNS void LANGUAGE plpgsql
|
||||
SECURITY DEFINER SET search_path = ''
|
||||
AS
|
||||
$BODY$
|
||||
DECLARE
|
||||
info record;
|
||||
new_table_owner TEXT;
|
||||
chunk_row _timescaledb_catalog.chunk;
|
||||
hypertable_row _timescaledb_catalog.hypertable;
|
||||
chunk_row _timescaledb_catalog.chunk;
|
||||
BEGIN
|
||||
--NOTE: pg_event_trigger_ddl_commands prevents this SECURITY DEFINER function from being called outside trigger.
|
||||
FOR info IN SELECT * FROM pg_event_trigger_ddl_commands() LOOP
|
||||
IF NOT _timescaledb_internal.is_main_table(info.objid) THEN
|
||||
RETURN;
|
||||
END IF;
|
||||
|
||||
IF _timescaledb_internal.ddl_is_change_owner(info.command) THEN
|
||||
--if change owner then change owners on all chunks
|
||||
new_table_owner := _timescaledb_internal.ddl_change_owner_to(info.command);
|
||||
hypertable_row := _timescaledb_internal.hypertable_from_main_table(info.objid);
|
||||
|
||||
FOR chunk_row IN
|
||||
SELECT *
|
||||
FROM _timescaledb_catalog.chunk
|
||||
WHERE hypertable_id = hypertable_row.id
|
||||
LOOP
|
||||
EXECUTE format(
|
||||
$$
|
||||
ALTER TABLE %1$I.%2$I OWNER TO %3$I
|
||||
$$,
|
||||
chunk_row.schema_name, chunk_row.table_name,
|
||||
new_table_owner
|
||||
);
|
||||
END LOOP;
|
||||
END IF;
|
||||
hypertable_row := _timescaledb_internal.hypertable_from_main_table(main_table);
|
||||
FOR chunk_row IN
|
||||
SELECT *
|
||||
FROM _timescaledb_catalog.chunk
|
||||
WHERE hypertable_id = hypertable_row.id
|
||||
LOOP
|
||||
EXECUTE format(
|
||||
$$
|
||||
ALTER TABLE %1$I.%2$I OWNER TO %3$I
|
||||
$$,
|
||||
chunk_row.schema_name, chunk_row.table_name,
|
||||
new_table_owner
|
||||
);
|
||||
END LOOP;
|
||||
END
|
||||
$BODY$;
|
||||
|
||||
|
@ -4,6 +4,7 @@ sql/util_time.sql
|
||||
sql/util_internal_table_ddl.sql
|
||||
sql/chunk.sql
|
||||
sql/chunk_trigger.sql
|
||||
sql/chunk_constraint.sql
|
||||
sql/hypertable_triggers.sql
|
||||
sql/hypertable_index_triggers.sql
|
||||
sql/partitioning.sql
|
||||
|
@ -42,6 +42,17 @@ $BODY$
|
||||
WHERE c.OID = table_oid;
|
||||
$BODY$;
|
||||
|
||||
CREATE OR REPLACE FUNCTION _timescaledb_internal.main_table_from_hypertable(
|
||||
hypertable_id int
|
||||
)
|
||||
RETURNS regclass LANGUAGE SQL STABLE AS
|
||||
$BODY$
|
||||
SELECT format('%I.%I',h.schema_name, h.table_name)::regclass
|
||||
FROM _timescaledb_catalog.hypertable h
|
||||
WHERE id = hypertable_id;
|
||||
$BODY$;
|
||||
|
||||
|
||||
-- Get the name of the time column for a chunk.
|
||||
--
|
||||
-- schema_name, table_name - name of the schema and table for the table represented by the crn.
|
||||
|
@ -20,6 +20,12 @@ BEGIN
|
||||
AFTER UPDATE OR DELETE OR INSERT ON _timescaledb_catalog.chunk
|
||||
FOR EACH ROW EXECUTE PROCEDURE _timescaledb_internal.on_change_chunk();
|
||||
|
||||
DROP TRIGGER IF EXISTS trigger_main_on_change_chunk_constraint
|
||||
ON _timescaledb_catalog.chunk_constraint;
|
||||
CREATE TRIGGER trigger_main_on_change_chunk_constraint
|
||||
AFTER UPDATE OR DELETE OR INSERT ON _timescaledb_catalog.chunk_constraint
|
||||
FOR EACH ROW EXECUTE PROCEDURE _timescaledb_internal.on_change_chunk_constraint();
|
||||
|
||||
-- no DELETE: it would be a no-op
|
||||
DROP TRIGGER IF EXISTS trigger_1_main_on_change_hypertable
|
||||
ON _timescaledb_catalog.hypertable;
|
||||
@ -67,10 +73,6 @@ BEGIN
|
||||
ON sql_drop
|
||||
EXECUTE PROCEDURE _timescaledb_internal.ddl_process_drop_trigger();
|
||||
|
||||
CREATE EVENT TRIGGER ddl_alter_table ON ddl_command_end
|
||||
WHEN tag IN ('alter table')
|
||||
EXECUTE PROCEDURE _timescaledb_internal.ddl_process_alter_table();
|
||||
|
||||
CREATE EVENT TRIGGER ddl_check_drop_command
|
||||
ON sql_drop
|
||||
EXECUTE PROCEDURE _timescaledb_internal.ddl_process_drop_table();
|
||||
@ -81,7 +83,6 @@ BEGIN
|
||||
ALTER EXTENSION timescaledb ADD EVENT TRIGGER ddl_drop_index;
|
||||
ALTER EXTENSION timescaledb ADD EVENT TRIGGER ddl_create_trigger;
|
||||
ALTER EXTENSION timescaledb ADD EVENT TRIGGER ddl_drop_trigger;
|
||||
ALTER EXTENSION timescaledb ADD EVENT TRIGGER ddl_alter_table;
|
||||
ALTER EXTENSION timescaledb ADD EVENT TRIGGER ddl_check_drop_command;
|
||||
END IF;
|
||||
|
||||
|
@ -119,10 +119,17 @@ SELECT pg_catalog.pg_extension_config_dump(pg_get_serial_sequence('_timescaledb_
|
||||
-- on the chunk's data table.
|
||||
CREATE TABLE _timescaledb_catalog.chunk_constraint (
|
||||
chunk_id INTEGER NOT NULL REFERENCES _timescaledb_catalog.chunk(id) ON DELETE CASCADE,
|
||||
dimension_slice_id INTEGER NOT NULL REFERENCES _timescaledb_catalog.dimension_slice(id) ON DELETE CASCADE,
|
||||
PRIMARY KEY(chunk_id, dimension_slice_id)
|
||||
dimension_slice_id INTEGER NULL REFERENCES _timescaledb_catalog.dimension_slice(id) ON DELETE CASCADE,
|
||||
constraint_name NAME NOT NULL,
|
||||
hypertable_constraint_name NAME NULL,
|
||||
UNIQUE(chunk_id, constraint_name)
|
||||
);
|
||||
SELECT pg_catalog.pg_extension_config_dump('_timescaledb_catalog.chunk_constraint', '');
|
||||
CREATE INDEX ON _timescaledb_catalog.chunk_constraint(chunk_id, dimension_slice_id);
|
||||
|
||||
CREATE SEQUENCE _timescaledb_catalog.chunk_constraint_name;
|
||||
SELECT pg_catalog.pg_extension_config_dump('_timescaledb_catalog.chunk_constraint_name', '');
|
||||
|
||||
|
||||
-- Represents an index on the hypertable
|
||||
CREATE TABLE IF NOT EXISTS _timescaledb_catalog.hypertable_index (
|
||||
|
@ -17,3 +17,16 @@ DROP FUNCTION IF EXISTS _timescaledb_internal.chunk_calculate_new_ranges(INTEGER
|
||||
DROP FUNCTION IF EXISTS _timescaledb_internal.chunk_id_get_by_dimensions(INTEGER[], BIGINT[]) CASCADE;
|
||||
DROP FUNCTION IF EXISTS _timescaledb_internal.chunk_get_dimensions_constraint_sql(INTEGER[], BIGINT[]) CASCADE;
|
||||
DROP FUNCTION IF EXISTS _timescaledb_internal.chunk_get_dimension_constraint_sql(INTEGER, BIGINT) CASCADE;
|
||||
CREATE TRIGGER trigger_main_on_change_chunk_constraint
|
||||
AFTER UPDATE OR DELETE OR INSERT ON _timescaledb_catalog.chunk_constraint
|
||||
FOR EACH ROW EXECUTE PROCEDURE _timescaledb_internal.on_change_chunk_constraint();
|
||||
|
||||
SELECT _timescaledb_internal.add_constraint(h.id, c.oid)
|
||||
FROM _timescaledb_catalog.hypertable h
|
||||
INNER JOIN pg_constraint c ON (c.conrelid = format('%I.%I', h.schema_name, h.table_name)::regclass);
|
||||
|
||||
DELETE FROM _timescaledb_catalog.hypertable_index hi
|
||||
WHERE EXISTS (
|
||||
SELECT 1 FROM pg_constraint WHERE conindid = format('%I.%I', hi.main_schema_name, hi.main_index_name)::regclass
|
||||
);
|
||||
|
||||
|
@ -0,0 +1,25 @@
|
||||
DROP FUNCTION _timescaledb_internal.ddl_is_change_owner(pg_ddl_command);
|
||||
DROP FUNCTION _timescaledb_internal.ddl_change_owner_to(pg_ddl_command);
|
||||
|
||||
DROP FUNCTION _timescaledb_internal.chunk_add_constraints(integer);
|
||||
DROP FUNCTION _timescaledb_internal.ddl_process_alter_table() CASCADE;
|
||||
|
||||
CREATE INDEX ON _timescaledb_catalog.chunk_constraint(chunk_id, dimension_slice_id) WHERE dimension_slice_id IS NOT NULL;
|
||||
|
||||
ALTER TABLE _timescaledb_catalog.chunk_constraint
|
||||
DROP CONSTRAINT chunk_constraint_pkey,
|
||||
ADD COLUMN constraint_name NAME;
|
||||
|
||||
UPDATE _timescaledb_catalog.chunk_constraint
|
||||
SET constraint_name = format('constraint_%s', dimension_slice_id);
|
||||
|
||||
ALTER TABLE _timescaledb_catalog.chunk_constraint
|
||||
ALTER COLUMN constraint_name SET NOT NULL,
|
||||
ALTER COLUMN dimension_slice_id DROP NOT NULL;
|
||||
|
||||
ALTER TABLE _timescaledb_catalog.chunk_constraint
|
||||
ADD COLUMN hypertable_constraint_name NAME NULL,
|
||||
ADD CONSTRAINT chunk_constraint_pkey PRIMARY KEY (chunk_id, constraint_name);
|
||||
|
||||
CREATE SEQUENCE _timescaledb_catalog.chunk_constraint_name;
|
||||
SELECT pg_catalog.pg_extension_config_dump('_timescaledb_catalog.chunk_constraint_name', '');
|
@ -73,8 +73,6 @@ BEGIN
|
||||
chunk_row.schema_name, chunk_row.table_name,
|
||||
table_owner
|
||||
);
|
||||
|
||||
PERFORM _timescaledb_internal.chunk_add_constraints(chunk_id);
|
||||
END
|
||||
$BODY$;
|
||||
|
||||
@ -115,34 +113,6 @@ BEGIN
|
||||
END
|
||||
$BODY$;
|
||||
|
||||
CREATE OR REPLACE FUNCTION _timescaledb_internal.chunk_add_constraints(
|
||||
chunk_id INTEGER
|
||||
)
|
||||
RETURNS VOID LANGUAGE PLPGSQL VOLATILE AS
|
||||
$BODY$
|
||||
DECLARE
|
||||
constraint_row record;
|
||||
BEGIN
|
||||
FOR constraint_row IN
|
||||
SELECT c.schema_name, c.table_name, ds.id as dimension_slice_id
|
||||
FROM _timescaledb_catalog.chunk c
|
||||
INNER JOIN _timescaledb_catalog.chunk_constraint cc ON (cc.chunk_id = c.id)
|
||||
INNER JOIN _timescaledb_catalog.dimension_slice ds ON (ds.id = cc.dimension_slice_id)
|
||||
WHERE c.id = chunk_add_constraints.chunk_id
|
||||
LOOP
|
||||
EXECUTE format(
|
||||
$$
|
||||
ALTER TABLE %1$I.%2$I
|
||||
ADD CONSTRAINT constraint_%3$s CHECK(%4$s)
|
||||
$$,
|
||||
constraint_row.schema_name, constraint_row.table_name,
|
||||
constraint_row.dimension_slice_id,
|
||||
_timescaledb_internal.dimension_slice_get_constraint_sql(constraint_row.dimension_slice_id)
|
||||
);
|
||||
END LOOP;
|
||||
END
|
||||
$BODY$;
|
||||
|
||||
-- Outputs the create_hypertable command to recreate the given hypertable.
|
||||
--
|
||||
-- This is currently used internally for our single hypertable backup tool
|
||||
|
@ -79,9 +79,20 @@ invalidate_relcache_trigger(PG_FUNCTION_ARGS)
|
||||
if (!CALLED_AS_TRIGGER(fcinfo))
|
||||
elog(ERROR, "not called by trigger manager");
|
||||
|
||||
/* arg 0 = relid of the proxy table */
|
||||
/* arg 0 = name of the proxy table */
|
||||
proxy_oid = catalog_get_cache_proxy_id_by_name(catalog, trigdata->tg_trigger->tgargs[0]);
|
||||
CacheInvalidateRelcacheByRelid(proxy_oid);
|
||||
if (proxy_oid != 0)
|
||||
{
|
||||
CacheInvalidateRelcacheByRelid(proxy_oid);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* This can happen during upgrade scripts when the catalog is
|
||||
* unavailable
|
||||
*/
|
||||
CacheInvalidateRelcacheByRelid(get_relname_relid(trigdata->tg_trigger->tgargs[0], get_namespace_oid(CACHE_SCHEMA_NAME, false)));
|
||||
}
|
||||
|
||||
/* tuple to return to executor */
|
||||
if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <miscadmin.h>
|
||||
#include <commands/dbcommands.h>
|
||||
#include <commands/sequence.h>
|
||||
#include <access/xact.h>
|
||||
|
||||
#include "catalog.h"
|
||||
#include "extension.h"
|
||||
@ -60,7 +61,7 @@ static const TableIndexDef catalog_table_index_definitions[_MAX_CATALOG_TABLES]
|
||||
[CHUNK_CONSTRAINT] = {
|
||||
.length = _MAX_CHUNK_CONSTRAINT_INDEX,
|
||||
.names = (char *[]) {
|
||||
[CHUNK_CONSTRAINT_CHUNK_ID_DIMENSION_SLICE_ID_IDX] = "chunk_constraint_pkey",
|
||||
[CHUNK_CONSTRAINT_CHUNK_ID_DIMENSION_SLICE_ID_IDX] = "chunk_constraint_chunk_id_dimension_slice_id_idx",
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -73,6 +74,27 @@ static const char *catalog_table_serial_id_names[_MAX_CATALOG_TABLES] = {
|
||||
[CHUNK_CONSTRAINT] = NULL,
|
||||
};
|
||||
|
||||
typedef struct InternalFunctionDef
|
||||
{
|
||||
char *name;
|
||||
size_t args;
|
||||
} InternalFunctionDef;
|
||||
|
||||
const static InternalFunctionDef internal_function_definitions[_MAX_INTERNAL_FUNCTIONS] = {
|
||||
[DDL_CHANGE_OWNER] = {
|
||||
.name = "ddl_change_owner",
|
||||
.args = 2,
|
||||
},
|
||||
[DDL_ADD_CONSTRAINT] = {
|
||||
.name = "add_constraint_by_name",
|
||||
.args = 2,
|
||||
},
|
||||
[DDL_DROP_CONSTRAINT] = {
|
||||
.name = "drop_constraint",
|
||||
.args = 2,
|
||||
}
|
||||
};
|
||||
|
||||
/* Names for proxy tables used for cache invalidation. Must match names in
|
||||
* sql/cache.sql */
|
||||
static const char *cache_proxy_table_names[_MAX_CACHE_TYPES] = {
|
||||
@ -126,7 +148,7 @@ catalog_get(void)
|
||||
if (MyDatabaseId == catalog.database_id)
|
||||
return &catalog;
|
||||
|
||||
if (!extension_is_loaded())
|
||||
if (!extension_is_loaded() || !IsTransactionState())
|
||||
return &catalog;
|
||||
|
||||
memset(&catalog, 0, sizeof(Catalog));
|
||||
@ -187,6 +209,21 @@ catalog_get(void)
|
||||
catalog.caches[i].inval_proxy_id = get_relname_relid(cache_proxy_table_names[i],
|
||||
catalog.cache_schema_id);
|
||||
|
||||
catalog.internal_schema_id = get_namespace_oid(INTERNAL_SCHEMA_NAME, false);
|
||||
for (i = 0; i < _MAX_INTERNAL_FUNCTIONS; i++)
|
||||
{
|
||||
InternalFunctionDef def = internal_function_definitions[i];
|
||||
FuncCandidateList funclist =
|
||||
FuncnameGetCandidates(list_make2(makeString(INTERNAL_SCHEMA_NAME), makeString(def.name)),
|
||||
def.args, NULL, false, false, false);
|
||||
|
||||
if (funclist == NULL || funclist->next)
|
||||
elog(ERROR, "Oid lookup failed for the function %s with %lu args", def.name, def.args);
|
||||
|
||||
catalog.functions[i].function_id = funclist->oid;
|
||||
}
|
||||
|
||||
|
||||
return &catalog;
|
||||
}
|
||||
|
||||
@ -208,6 +245,12 @@ catalog_get_cache_proxy_id(Catalog *catalog, CacheType type)
|
||||
return catalog->caches[type].inval_proxy_id;
|
||||
}
|
||||
|
||||
Oid
|
||||
catalog_get_internal_function_id(Catalog *catalog, InternalFunction func)
|
||||
{
|
||||
return catalog->functions[func].function_id;
|
||||
}
|
||||
|
||||
Oid
|
||||
catalog_get_cache_proxy_id_by_name(Catalog *catalog, const char *relname)
|
||||
{
|
||||
|
@ -30,8 +30,17 @@ enum CatalogTable
|
||||
_MAX_CATALOG_TABLES,
|
||||
};
|
||||
|
||||
typedef enum InternalFunction
|
||||
{
|
||||
DDL_CHANGE_OWNER = 0,
|
||||
DDL_ADD_CONSTRAINT,
|
||||
DDL_DROP_CONSTRAINT,
|
||||
_MAX_INTERNAL_FUNCTIONS,
|
||||
} InternalFunction;
|
||||
|
||||
#define CATALOG_SCHEMA_NAME "_timescaledb_catalog"
|
||||
#define CACHE_SCHEMA_NAME "_timescaledb_cache"
|
||||
#define INTERNAL_SCHEMA_NAME "_timescaledb_internal"
|
||||
#define EXTENSION_NAME "timescaledb"
|
||||
|
||||
/******************************
|
||||
@ -259,6 +268,8 @@ enum Anum_chunk_constraint
|
||||
{
|
||||
Anum_chunk_constraint_chunk_id = 1,
|
||||
Anum_chunk_constraint_dimension_slice_id,
|
||||
Anum_chunk_constraint_constraint_name,
|
||||
Anum_chunk_constraint_hypertable_constraint_name,
|
||||
_Anum_chunk_constraint_max,
|
||||
};
|
||||
|
||||
@ -269,6 +280,8 @@ typedef struct FormData_chunk_constraint
|
||||
{
|
||||
int32 chunk_id;
|
||||
int32 dimension_slice_id;
|
||||
NameData constraint_name;
|
||||
NameData hypertable_constraint_name;
|
||||
} FormData_chunk_constraint;
|
||||
|
||||
typedef FormData_chunk_constraint *Form_chunk_constraint;
|
||||
@ -281,9 +294,9 @@ enum
|
||||
|
||||
enum Anum_chunk_constraint_chunk_id_dimension_slice_id_idx
|
||||
{
|
||||
Anum_chunk_constraint_chunk_id_dimension_id_idx_chunk_id = 1,
|
||||
Anum_chunk_constraint_chunk_id_dimension_id_idx_dimension_slice_id,
|
||||
_Anum_chunk_constraint_chunk_id_dimension_id_idx_max,
|
||||
Anum_chunk_constraint_chunk_id_dimension_slice_id_idx_chunk_id = 1,
|
||||
Anum_chunk_constraint_chunk_id_dimension_slice_id_idx_dimension_slice_id,
|
||||
_Anum_chunk_constraint_chunk_id_dimension_slice_id_idx_max,
|
||||
};
|
||||
|
||||
|
||||
@ -323,6 +336,11 @@ typedef struct Catalog
|
||||
Oid inval_proxy_id;
|
||||
} caches[_MAX_CACHE_TYPES];
|
||||
Oid owner_uid;
|
||||
Oid internal_schema_id;
|
||||
struct
|
||||
{
|
||||
Oid function_id;
|
||||
} functions[_MAX_INTERNAL_FUNCTIONS];
|
||||
} Catalog;
|
||||
|
||||
|
||||
@ -339,6 +357,8 @@ void catalog_reset(void);
|
||||
Oid catalog_get_cache_proxy_id(Catalog *catalog, CacheType type);
|
||||
Oid catalog_get_cache_proxy_id_by_name(Catalog *catalog, const char *relname);
|
||||
|
||||
Oid catalog_get_internal_function_id(Catalog *catalog, InternalFunction func);
|
||||
|
||||
const char *catalog_get_cache_proxy_name(CacheType type);
|
||||
|
||||
bool catalog_become_owner(Catalog *catalog, CatalogSecurityContext *sec_ctx);
|
||||
|
@ -18,13 +18,19 @@ chunk_constraint_fill(ChunkConstraint *cc, HeapTuple tuple)
|
||||
return cc;
|
||||
}
|
||||
|
||||
static bool
|
||||
chunk_constraint_for_dimension_slice(TupleInfo *ti, void *data)
|
||||
{
|
||||
return !heap_attisnull(ti->tuple, Anum_chunk_constraint_dimension_slice_id);
|
||||
}
|
||||
|
||||
static bool
|
||||
chunk_constraint_tuple_found(TupleInfo *ti, void *data)
|
||||
{
|
||||
Chunk *chunk = data;
|
||||
|
||||
chunk_constraint_fill(&chunk->constraints[chunk->num_constraints++], ti->tuple);
|
||||
|
||||
|
||||
if (chunk->capacity == chunk->num_constraints)
|
||||
return false;
|
||||
|
||||
@ -50,6 +56,7 @@ chunk_constraint_scan_by_chunk_id(Chunk *chunk)
|
||||
.nkeys = 1,
|
||||
.scankey = scankey,
|
||||
.data = chunk,
|
||||
.filter = chunk_constraint_for_dimension_slice,
|
||||
.tuple_found = chunk_constraint_tuple_found,
|
||||
.lockmode = AccessShareLock,
|
||||
.scandirection = ForwardScanDirection,
|
||||
@ -57,7 +64,7 @@ chunk_constraint_scan_by_chunk_id(Chunk *chunk)
|
||||
|
||||
chunk->num_constraints = 0;
|
||||
|
||||
ScanKeyInit(&scankey[0], Anum_chunk_constraint_chunk_id_dimension_id_idx_chunk_id,
|
||||
ScanKeyInit(&scankey[0], Anum_chunk_constraint_chunk_id_dimension_slice_id_idx_chunk_id,
|
||||
BTEqualStrategyNumber, F_INT4EQ, Int32GetDatum(chunk->fd.id));
|
||||
|
||||
num_found = scanner_scan(&scanCtx);
|
||||
@ -132,12 +139,13 @@ chunk_constraint_scan_by_dimension_slice_id(DimensionSlice *slice, ChunkScanCtx
|
||||
.nkeys = 1,
|
||||
.scankey = scankey,
|
||||
.data = &data,
|
||||
.filter = chunk_constraint_for_dimension_slice,
|
||||
.tuple_found = chunk_constraint_dimension_id_tuple_found,
|
||||
.lockmode = AccessShareLock,
|
||||
.scandirection = ForwardScanDirection,
|
||||
};
|
||||
|
||||
ScanKeyInit(&scankey[0], Anum_chunk_constraint_chunk_id_dimension_id_idx_dimension_slice_id,
|
||||
ScanKeyInit(&scankey[0], Anum_chunk_constraint_chunk_id_dimension_slice_id_idx_dimension_slice_id,
|
||||
BTEqualStrategyNumber, F_INT4EQ, Int32GetDatum(slice->fd.id));
|
||||
|
||||
num_found = scanner_scan(&scanCtx);
|
||||
@ -151,10 +159,16 @@ chunk_constraint_insert_relation(Relation rel, ChunkConstraint *constraint)
|
||||
TupleDesc desc = RelationGetDescr(rel);
|
||||
Datum values[Natts_chunk_constraint];
|
||||
bool nulls[Natts_chunk_constraint] = {false};
|
||||
NameData constraint_name;
|
||||
|
||||
sprintf(constraint_name.data, "constraint_%d", constraint->fd.dimension_slice_id);
|
||||
|
||||
memset(values, 0, sizeof(values));
|
||||
values[Anum_chunk_constraint_chunk_id - 1] = constraint->fd.chunk_id;
|
||||
values[Anum_chunk_constraint_dimension_slice_id - 1] = constraint->fd.dimension_slice_id;
|
||||
values[Anum_chunk_constraint_constraint_name - 1] = NameGetDatum(&constraint_name);
|
||||
|
||||
nulls[Anum_chunk_constraint_hypertable_constraint_name - 1] = true;
|
||||
|
||||
catalog_insert_values(rel, desc, values, nulls);
|
||||
}
|
||||
|
18
src/compat.c
18
src/compat.c
@ -20,3 +20,21 @@ insert_main_table_trigger_after(PG_FUNCTION_ARGS)
|
||||
elog(ERROR, "Deprecated trigger function should not be invoked");
|
||||
PG_RETURN_NULL();
|
||||
}
|
||||
|
||||
PG_FUNCTION_INFO_V1(ddl_is_change_owner);
|
||||
|
||||
Datum
|
||||
ddl_is_change_owner(PG_FUNCTION_ARGS)
|
||||
{
|
||||
elog(ERROR, "Deprecated function should not be invoked");
|
||||
PG_RETURN_NULL();
|
||||
}
|
||||
|
||||
PG_FUNCTION_INFO_V1(ddl_change_owner_to);
|
||||
|
||||
Datum
|
||||
ddl_change_owner_to(PG_FUNCTION_ARGS)
|
||||
{
|
||||
elog(ERROR, "Deprecated function should not be invoked");
|
||||
PG_RETURN_NULL();
|
||||
}
|
||||
|
@ -1,71 +0,0 @@
|
||||
#include <postgres.h>
|
||||
#include <tcop/deparse_utility.h>
|
||||
|
||||
#include "ddl_utils.h"
|
||||
|
||||
enum ddl_cmd_type
|
||||
{
|
||||
DDL_CHANGE_OWNER, DDL_OTHER
|
||||
};
|
||||
|
||||
static enum ddl_cmd_type
|
||||
ddl_alter_table_subcmd(CollectedCommand *cmd)
|
||||
{
|
||||
ListCell *cell;
|
||||
|
||||
if (cmd->type == SCT_AlterTable)
|
||||
{
|
||||
foreach(cell, cmd->d.alterTable.subcmds)
|
||||
{
|
||||
CollectedATSubcmd *sub = lfirst(cell);
|
||||
AlterTableCmd *subcmd = (AlterTableCmd *) sub->parsetree;
|
||||
|
||||
Assert(IsA(subcmd, AlterTableCmd));
|
||||
|
||||
switch (subcmd->subtype)
|
||||
{
|
||||
case AT_ChangeOwner:
|
||||
return DDL_CHANGE_OWNER;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return DDL_OTHER;
|
||||
}
|
||||
|
||||
|
||||
Datum
|
||||
ddl_change_owner_to(PG_FUNCTION_ARGS)
|
||||
{
|
||||
CollectedATSubcmd *sub;
|
||||
AlterTableCmd *altersub;
|
||||
RoleSpec *role;
|
||||
Name user = palloc0(NAMEDATALEN);
|
||||
CollectedCommand *cmd = (CollectedCommand *) PG_GETARG_POINTER(0);
|
||||
|
||||
Assert(cmd->type == SCT_AlterTable);
|
||||
Assert(list_length(cmd->d.alterTable.subcmds) == 1);
|
||||
sub = linitial(cmd->d.alterTable.subcmds);
|
||||
|
||||
Assert(IsA(sub->parsetree, AlterTableCmd));
|
||||
altersub = (AlterTableCmd *) sub->parsetree;
|
||||
|
||||
Assert(IsA(altersub->newowner, RoleSpec));
|
||||
role = (RoleSpec *) altersub->newowner;
|
||||
|
||||
memcpy(user->data, role->rolename, NAMEDATALEN);
|
||||
|
||||
PG_RETURN_NAME(user);
|
||||
}
|
||||
|
||||
Datum
|
||||
ddl_is_change_owner(PG_FUNCTION_ARGS)
|
||||
{
|
||||
bool ret =
|
||||
DDL_CHANGE_OWNER == ddl_alter_table_subcmd(
|
||||
(CollectedCommand *) PG_GETARG_POINTER(0)
|
||||
);
|
||||
|
||||
PG_RETURN_BOOL(ret);
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
#ifndef TIMESCALEDB_DDL_UTILS_H
|
||||
#define TIMESCALEDB_DDL_UTILS_H
|
||||
#include <postgres.h>
|
||||
#include <fmgr.h>
|
||||
|
||||
PG_FUNCTION_INFO_V1(ddl_is_change_owner);
|
||||
PG_FUNCTION_INFO_V1(ddl_change_owner_to);
|
||||
|
||||
#endif /* TIMESCALEDB_DDL_UTILS_H */
|
@ -180,6 +180,17 @@ extension_is_loaded(void)
|
||||
extension_update_state();
|
||||
}
|
||||
|
||||
if (creating_extension && OidIsValid(get_extension_oid(EXTENSION_NAME, true)) && get_extension_oid(EXTENSION_NAME, true) == CurrentExtensionObject)
|
||||
{
|
||||
/* turn off extension during upgrade scripts */
|
||||
|
||||
/*
|
||||
* This is necessary so that, for example, the catalog does not go
|
||||
* looking for things that aren't yet there.
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (extstate)
|
||||
{
|
||||
case EXTENSION_STATE_CREATED:
|
||||
|
@ -4,11 +4,14 @@
|
||||
#include <catalog/namespace.h>
|
||||
#include <catalog/pg_inherits_fn.h>
|
||||
#include <catalog/index.h>
|
||||
#include <catalog/objectaddress.h>
|
||||
#include <commands/copy.h>
|
||||
#include <commands/vacuum.h>
|
||||
#include <commands/defrem.h>
|
||||
#include <commands/tablecmds.h>
|
||||
#include <utils/rel.h>
|
||||
#include <utils/lsyscache.h>
|
||||
#include <utils/builtins.h>
|
||||
#include <miscadmin.h>
|
||||
|
||||
#include "utils.h"
|
||||
@ -308,6 +311,185 @@ process_reindex(Node *parsetree)
|
||||
return false;
|
||||
}
|
||||
|
||||
static void
|
||||
process_altertable_change_owner(Hypertable *ht, AlterTableCmd *cmd, Oid relid)
|
||||
{
|
||||
RoleSpec *role;
|
||||
|
||||
Assert(IsA(cmd->newowner, RoleSpec));
|
||||
role = (RoleSpec *) cmd->newowner;
|
||||
OidFunctionCall2(catalog_get_internal_function_id(catalog_get(), DDL_CHANGE_OWNER),
|
||||
ObjectIdGetDatum(relid), CStringGetDatum(role->rolename));
|
||||
}
|
||||
|
||||
static void
|
||||
process_altertable_add_constraint(Hypertable *ht, const char* constraint_name)
|
||||
{
|
||||
Assert(constraint_name != NULL);
|
||||
OidFunctionCall2(catalog_get_internal_function_id(catalog_get(), DDL_ADD_CONSTRAINT),
|
||||
Int32GetDatum(ht->fd.id), CStringGetDatum(constraint_name));
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
process_altertable_drop_constraint(Hypertable *ht, AlterTableCmd *cmd, Oid relid)
|
||||
{
|
||||
char *constraint_name = NULL;
|
||||
|
||||
constraint_name = cmd->name;
|
||||
Assert(constraint_name != NULL);
|
||||
OidFunctionCall2(catalog_get_internal_function_id(catalog_get(), DDL_DROP_CONSTRAINT),
|
||||
Int32GetDatum(ht->fd.id), CStringGetDatum(constraint_name));
|
||||
}
|
||||
|
||||
/* foreign-key constraints to hypertables are not allowed */
|
||||
static void
|
||||
verify_constraint(Constraint *stmt)
|
||||
{
|
||||
if (stmt->contype == CONSTR_FOREIGN)
|
||||
{
|
||||
RangeVar *primary_table = stmt->pktable;
|
||||
Oid primary_oid = RangeVarGetRelid(primary_table, NoLock, true);
|
||||
|
||||
if (OidIsValid(primary_oid))
|
||||
{
|
||||
Cache *hcache = hypertable_cache_pin();
|
||||
Hypertable *ht = hypertable_cache_get_entry(hcache, primary_oid);
|
||||
|
||||
if (NULL != ht)
|
||||
{
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("Foreign keys to hypertables are not supported.")));
|
||||
}
|
||||
cache_release(hcache);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
verify_constraint_list(List *constraint_list)
|
||||
{
|
||||
ListCell *lc;
|
||||
|
||||
foreach(lc, constraint_list)
|
||||
{
|
||||
Constraint *constraint = (Constraint *) lfirst(lc);
|
||||
|
||||
verify_constraint(constraint);
|
||||
}
|
||||
}
|
||||
|
||||
/* process all create table commands to make sure their constraints are kosher */
|
||||
static void
|
||||
process_create_table(Node *parsetree)
|
||||
{
|
||||
CreateStmt *stmt = (CreateStmt *) parsetree;
|
||||
ListCell *lc;
|
||||
|
||||
verify_constraint_list(stmt->constraints);
|
||||
foreach(lc, stmt->tableElts)
|
||||
{
|
||||
ColumnDef *column_def = (ColumnDef *) lfirst(lc);
|
||||
|
||||
verify_constraint_list(column_def->constraints);
|
||||
}
|
||||
}
|
||||
|
||||
/* process all regular-table alter commands to make sure they aren't adding
|
||||
* foreign-key constraints to hypertables */
|
||||
static void
|
||||
process_altertable_plain_table(Node *parsetree)
|
||||
{
|
||||
AlterTableStmt *stmt = (AlterTableStmt *) parsetree;
|
||||
ListCell *lc;
|
||||
|
||||
foreach(lc, stmt->cmds)
|
||||
{
|
||||
AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lc);
|
||||
|
||||
switch (cmd->subtype)
|
||||
{
|
||||
case AT_AddConstraint:
|
||||
case AT_AddConstraintRecurse:
|
||||
{
|
||||
Constraint *constraint = (Constraint *) cmd->def;
|
||||
|
||||
Assert(IsA(cmd->def, Constraint));
|
||||
|
||||
verify_constraint(constraint);
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
process_altertable(Node *parsetree)
|
||||
{
|
||||
AlterTableStmt *stmt = (AlterTableStmt *) parsetree;
|
||||
Oid relid = AlterTableLookupRelation(stmt, NoLock);
|
||||
Cache *hcache = NULL;
|
||||
ListCell *lc;
|
||||
Hypertable *ht;
|
||||
|
||||
if (!OidIsValid(relid))
|
||||
return;
|
||||
|
||||
hcache = hypertable_cache_pin();
|
||||
|
||||
ht = hypertable_cache_get_entry(hcache, relid);
|
||||
if (NULL == ht)
|
||||
{
|
||||
cache_release(hcache);
|
||||
process_altertable_plain_table(parsetree);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach(lc, stmt->cmds)
|
||||
{
|
||||
AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lc);
|
||||
|
||||
switch (cmd->subtype)
|
||||
{
|
||||
case AT_ChangeOwner:
|
||||
process_altertable_change_owner(ht, cmd, relid);
|
||||
break;
|
||||
case AT_AddIndex:
|
||||
{
|
||||
Assert(IsA(cmd->def, IndexStmt));
|
||||
IndexStmt *stmt = (IndexStmt *) cmd->def;
|
||||
|
||||
Assert(stmt->isconstraint);
|
||||
process_altertable_add_constraint(ht, stmt->idxname);
|
||||
}
|
||||
/*
|
||||
* AddConstraint sometimes transformed to AddIndex if Index is
|
||||
* involved. different path than CREATE INDEX.
|
||||
*/
|
||||
case AT_AddConstraint:
|
||||
case AT_AddConstraintRecurse:
|
||||
{
|
||||
Assert(IsA(cmd->def, Constraint));
|
||||
Constraint *stmt = (Constraint *) cmd->def;
|
||||
|
||||
process_altertable_add_constraint(ht, stmt->conname);
|
||||
}
|
||||
|
||||
break;
|
||||
case AT_DropConstraint:
|
||||
case AT_DropConstraintRecurse:
|
||||
process_altertable_drop_constraint(ht, cmd, relid);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cache_release(hcache);
|
||||
}
|
||||
|
||||
|
||||
/* Hook-intercept for ProcessUtility. */
|
||||
static void
|
||||
@ -329,12 +511,20 @@ timescaledb_ProcessUtility(Node *parsetree,
|
||||
case T_TruncateStmt:
|
||||
process_truncate(parsetree);
|
||||
break;
|
||||
case T_AlterTableStmt:
|
||||
/* Process main table first to error out if not a valid alter */
|
||||
prev_ProcessUtility(parsetree, query_string, context, params, dest, completion_tag);
|
||||
process_altertable(parsetree);
|
||||
return;
|
||||
case T_AlterObjectSchemaStmt:
|
||||
process_alterobjectschema(parsetree);
|
||||
break;
|
||||
case T_RenameStmt:
|
||||
process_rename(parsetree);
|
||||
break;
|
||||
case T_CreateStmt:
|
||||
process_create_table(parsetree);
|
||||
break;
|
||||
case T_CopyStmt:
|
||||
if (process_copy(parsetree, query_string, completion_tag))
|
||||
return;
|
||||
|
11
src/utils.c
11
src/utils.c
@ -4,6 +4,7 @@
|
||||
#include <fmgr.h>
|
||||
#include <utils/datetime.h>
|
||||
#include <catalog/pg_type.h>
|
||||
#include <catalog/pg_trigger.h>
|
||||
#include <catalog/namespace.h>
|
||||
#include <utils/guc.h>
|
||||
#include <utils/date.h>
|
||||
@ -373,3 +374,13 @@ date_bucket(PG_FUNCTION_ARGS)
|
||||
bucketed = DirectFunctionCall2(timestamp_bucket, PG_GETARG_DATUM(0), converted_ts);
|
||||
return DirectFunctionCall1(timestamp_date, bucketed);
|
||||
}
|
||||
|
||||
PG_FUNCTION_INFO_V1(trigger_is_row_trigger);
|
||||
|
||||
Datum
|
||||
trigger_is_row_trigger(PG_FUNCTION_ARGS)
|
||||
{
|
||||
int16 tgtype = PG_GETARG_INT16(0);
|
||||
|
||||
PG_RETURN_BOOL(TRIGGER_FOR_ROW(tgtype));
|
||||
}
|
||||
|
@ -133,7 +133,7 @@ SELECT * FROM _timescaledb_catalog.hypertable;
|
||||
4 | customSchema | Hypertable_1 | _timescaledb_internal | _hyper_4 | 1
|
||||
(4 rows)
|
||||
|
||||
SELECT * FROM _timescaledb_catalog.hypertable_index;
|
||||
SELECT * FROM _timescaledb_catalog.hypertable_index ORDER BY format('%I.%I', main_schema_name, main_index_name)::regclass;
|
||||
hypertable_id | main_schema_name | main_index_name | definition
|
||||
---------------+------------------+------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------
|
||||
1 | public | one_Partition_device_id_timeCustom_idx | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree (device_id, "timeCustom" DESC NULLS LAST) WHERE (device_id IS NOT NULL)
|
||||
@ -144,9 +144,9 @@ SELECT * FROM _timescaledb_catalog.hypertable_index;
|
||||
1 | public | one_Partition_timeCustom_idx | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("timeCustom" DESC)
|
||||
2 | public | 1dim_time_idx | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("time" DESC)
|
||||
3 | public | Hypertable_1_time_Device_id_idx | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("time", "Device_id")
|
||||
4 | customSchema | Hypertable_1_time_Device_id_idx | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("time", "Device_id")
|
||||
3 | public | Hypertable_1_time_idx | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("time" DESC)
|
||||
3 | public | Hypertable_1_Device_id_time_idx | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("Device_id", "time" DESC)
|
||||
4 | customSchema | Hypertable_1_time_Device_id_idx | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("time", "Device_id")
|
||||
4 | customSchema | Hypertable_1_time_idx | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("time" DESC)
|
||||
(12 rows)
|
||||
|
||||
@ -161,7 +161,7 @@ INSERT INTO "customSchema"."Hypertable_1"(time, "Device_id", temp_c, humidity, s
|
||||
VALUES(1257894000000000000, 'dev1', 30, 70, 1, 2, 3, 100);
|
||||
INSERT INTO "customSchema"."Hypertable_1"(time, "Device_id", temp_c, humidity, sensor_1, sensor_2, sensor_3, sensor_4)
|
||||
VALUES(1257894000000000001, 'dev1', 30, 70, 1, 2, 3, 100);
|
||||
SELECT * FROM _timescaledb_catalog.hypertable_index;
|
||||
SELECT * FROM _timescaledb_catalog.hypertable_index ORDER BY format('%I.%I', main_schema_name, main_index_name)::regclass;
|
||||
hypertable_id | main_schema_name | main_index_name | definition
|
||||
---------------+------------------+------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------
|
||||
1 | public | one_Partition_device_id_timeCustom_idx | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree (device_id, "timeCustom" DESC NULLS LAST) WHERE (device_id IS NOT NULL)
|
||||
@ -172,9 +172,9 @@ SELECT * FROM _timescaledb_catalog.hypertable_index;
|
||||
1 | public | one_Partition_timeCustom_idx | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("timeCustom" DESC)
|
||||
2 | public | 1dim_time_idx | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("time" DESC)
|
||||
3 | public | Hypertable_1_time_Device_id_idx | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("time", "Device_id")
|
||||
4 | customSchema | Hypertable_1_time_Device_id_idx | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("time", "Device_id")
|
||||
3 | public | Hypertable_1_time_idx | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("time" DESC)
|
||||
3 | public | Hypertable_1_Device_id_time_idx | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("Device_id", "time" DESC)
|
||||
4 | customSchema | Hypertable_1_time_Device_id_idx | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("time", "Device_id")
|
||||
4 | customSchema | Hypertable_1_time_idx | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("time" DESC)
|
||||
3 | public | Hypertable_1_time_temp_c_idx | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("time", temp_c)
|
||||
3 | public | ind_humidity | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("time", humidity)
|
||||
|
378
test/expected/constraint.out
Normal file
378
test/expected/constraint.out
Normal file
@ -0,0 +1,378 @@
|
||||
\o /dev/null
|
||||
\ir include/create_single_db.sql
|
||||
SET client_min_messages = WARNING;
|
||||
DROP DATABASE IF EXISTS single;
|
||||
SET client_min_messages = NOTICE;
|
||||
CREATE DATABASE single;
|
||||
\c single
|
||||
CREATE EXTENSION IF NOT EXISTS timescaledb;
|
||||
SET timescaledb.disable_optimizations = :DISABLE_OPTIMIZATIONS;
|
||||
\o
|
||||
CREATE TABLE hyper (
|
||||
time BIGINT NOT NULL,
|
||||
device_id TEXT NOT NULL,
|
||||
sensor_1 NUMERIC NULL DEFAULT 1 CHECK (sensor_1 > 10)
|
||||
);
|
||||
SELECT * FROM create_hypertable('hyper', 'time', chunk_time_interval => 10);
|
||||
create_hypertable
|
||||
-------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
--check and not-null constraints are inherited through regular inheritance.
|
||||
\set ON_ERROR_STOP 0
|
||||
INSERT INTO hyper(time, device_id,sensor_1) VALUES
|
||||
(1257987700000000000, 'dev2', 9);
|
||||
ERROR: new row for relation "_hyper_1_1_chunk" violates check constraint "hyper_sensor_1_check"
|
||||
INSERT INTO hyper(time, device_id,sensor_1) VALUES
|
||||
(1257987700000000000, NULL, 11);
|
||||
ERROR: null value in column "device_id" violates not-null constraint
|
||||
\set ON_ERROR_STOP 1
|
||||
INSERT INTO hyper(time, device_id,sensor_1) VALUES
|
||||
(1257987700000000000, 'dev2', 11);
|
||||
INSERT INTO hyper(time, device_id,sensor_1) VALUES
|
||||
(1257987700000000000, 'dev2', 11);
|
||||
----------------------- UNIQUE CONSTRAINTS ------------------
|
||||
CREATE TABLE hyper_unique (
|
||||
time BIGINT NOT NULL UNIQUE,
|
||||
device_id TEXT NOT NULL,
|
||||
sensor_1 NUMERIC NULL DEFAULT 1 CHECK (sensor_1 > 10)
|
||||
);
|
||||
SELECT * FROM create_hypertable('hyper_unique', 'time', chunk_time_interval => 10);
|
||||
create_hypertable
|
||||
-------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
INSERT INTO hyper_unique(time, device_id,sensor_1) VALUES
|
||||
(1257987700000000000, 'dev2', 11);
|
||||
INSERT INTO hyper_unique(time, device_id,sensor_1) VALUES
|
||||
(1257987800000000000, 'dev2', 11);
|
||||
\set ON_ERROR_STOP 0
|
||||
INSERT INTO hyper_unique(time, device_id,sensor_1) VALUES
|
||||
(1257987700000000000, 'dev2', 11);
|
||||
ERROR: duplicate key value violates unique constraint "4_1_hyper_unique_time_key"
|
||||
\set ON_ERROR_STOP 1
|
||||
\d+ hyper
|
||||
Table "public.hyper"
|
||||
Column | Type | Modifiers | Storage | Stats target | Description
|
||||
-----------+---------+-----------+----------+--------------+-------------
|
||||
time | bigint | not null | plain | |
|
||||
device_id | text | not null | extended | |
|
||||
sensor_1 | numeric | default 1 | main | |
|
||||
Indexes:
|
||||
"hyper_time_idx" btree ("time" DESC)
|
||||
Check constraints:
|
||||
"hyper_sensor_1_check" CHECK (sensor_1 > 10::numeric)
|
||||
Child tables: _timescaledb_internal._hyper_1_3_chunk
|
||||
|
||||
\d+ hyper_unique
|
||||
Table "public.hyper_unique"
|
||||
Column | Type | Modifiers | Storage | Stats target | Description
|
||||
-----------+---------+-----------+----------+--------------+-------------
|
||||
time | bigint | not null | plain | |
|
||||
device_id | text | not null | extended | |
|
||||
sensor_1 | numeric | default 1 | main | |
|
||||
Indexes:
|
||||
"hyper_unique_time_key" UNIQUE CONSTRAINT, btree ("time")
|
||||
Check constraints:
|
||||
"hyper_unique_sensor_1_check" CHECK (sensor_1 > 10::numeric)
|
||||
Child tables: _timescaledb_internal._hyper_2_4_chunk,
|
||||
_timescaledb_internal._hyper_2_5_chunk
|
||||
|
||||
--should have unique constraint not just unique index
|
||||
\d+ _timescaledb_internal._hyper_2_4_chunk
|
||||
Table "_timescaledb_internal._hyper_2_4_chunk"
|
||||
Column | Type | Modifiers | Storage | Stats target | Description
|
||||
-----------+---------+-----------+----------+--------------+-------------
|
||||
time | bigint | not null | plain | |
|
||||
device_id | text | not null | extended | |
|
||||
sensor_1 | numeric | default 1 | main | |
|
||||
Indexes:
|
||||
"4_1_hyper_unique_time_key" UNIQUE CONSTRAINT, btree ("time")
|
||||
Check constraints:
|
||||
"constraint_4" CHECK ("time" >= '1257987700000000000'::bigint AND "time" < '1257987700000000010'::bigint)
|
||||
"hyper_unique_sensor_1_check" CHECK (sensor_1 > 10::numeric)
|
||||
Inherits: hyper_unique
|
||||
|
||||
ALTER TABLE hyper_unique DROP CONSTRAINT hyper_unique_time_key;
|
||||
\d+ _timescaledb_internal._hyper_2_4_chunk
|
||||
Table "_timescaledb_internal._hyper_2_4_chunk"
|
||||
Column | Type | Modifiers | Storage | Stats target | Description
|
||||
-----------+---------+-----------+----------+--------------+-------------
|
||||
time | bigint | not null | plain | |
|
||||
device_id | text | not null | extended | |
|
||||
sensor_1 | numeric | default 1 | main | |
|
||||
Check constraints:
|
||||
"constraint_4" CHECK ("time" >= '1257987700000000000'::bigint AND "time" < '1257987700000000010'::bigint)
|
||||
"hyper_unique_sensor_1_check" CHECK (sensor_1 > 10::numeric)
|
||||
Inherits: hyper_unique
|
||||
|
||||
--uniqueness not enforced
|
||||
INSERT INTO hyper_unique(time, device_id,sensor_1) VALUES
|
||||
(1257987700000000000, 'dev3', 11);
|
||||
--shouldn't be able to create constraint
|
||||
\set ON_ERROR_STOP 0
|
||||
ALTER TABLE hyper_unique ADD CONSTRAINT hyper_unique_time_key UNIQUE (time);
|
||||
ERROR: could not create unique index "4_3_hyper_unique_time_key"
|
||||
\set ON_ERROR_STOP 1
|
||||
DELETE FROM hyper_unique WHERE device_id = 'dev3';
|
||||
--now can create
|
||||
ALTER TABLE hyper_unique ADD CONSTRAINT hyper_unique_time_key UNIQUE (time);
|
||||
\d+ _timescaledb_internal._hyper_2_4_chunk
|
||||
Table "_timescaledb_internal._hyper_2_4_chunk"
|
||||
Column | Type | Modifiers | Storage | Stats target | Description
|
||||
-----------+---------+-----------+----------+--------------+-------------
|
||||
time | bigint | not null | plain | |
|
||||
device_id | text | not null | extended | |
|
||||
sensor_1 | numeric | default 1 | main | |
|
||||
Indexes:
|
||||
"4_4_hyper_unique_time_key" UNIQUE CONSTRAINT, btree ("time")
|
||||
Check constraints:
|
||||
"constraint_4" CHECK ("time" >= '1257987700000000000'::bigint AND "time" < '1257987700000000010'::bigint)
|
||||
"hyper_unique_sensor_1_check" CHECK (sensor_1 > 10::numeric)
|
||||
Inherits: hyper_unique
|
||||
|
||||
--test adding constraint with same name to different table -- should fail
|
||||
\set ON_ERROR_STOP 0
|
||||
ALTER TABLE hyper ADD CONSTRAINT hyper_unique_time_key UNIQUE (time);
|
||||
ERROR: relation "hyper_unique_time_key" already exists
|
||||
\set ON_ERROR_STOP 1
|
||||
--uniquness violation fails
|
||||
\set ON_ERROR_STOP 0
|
||||
INSERT INTO hyper_unique(time, device_id,sensor_1) VALUES
|
||||
(1257987700000000000, 'dev2', 11);
|
||||
ERROR: duplicate key value violates unique constraint "4_4_hyper_unique_time_key"
|
||||
\set ON_ERROR_STOP 1
|
||||
--cannot create unique constraint on non-partition column
|
||||
\set ON_ERROR_STOP 0
|
||||
ALTER TABLE hyper_unique ADD CONSTRAINT hyper_unique_invalid UNIQUE (device_id);
|
||||
ERROR: Cannot create a unique index without the column: time (used in partitioning)
|
||||
\set ON_ERROR_STOP 1
|
||||
----------------------- PRIMARY KEY ------------------
|
||||
CREATE TABLE hyper_pk (
|
||||
time BIGINT NOT NULL PRIMARY KEY,
|
||||
device_id TEXT NOT NULL,
|
||||
sensor_1 NUMERIC NULL DEFAULT 1 CHECK (sensor_1 > 10)
|
||||
);
|
||||
SELECT * FROM create_hypertable('hyper_pk', 'time', chunk_time_interval => 10);
|
||||
create_hypertable
|
||||
-------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
INSERT INTO hyper_pk(time, device_id,sensor_1) VALUES
|
||||
(1257987700000000000, 'dev2', 11);
|
||||
\set ON_ERROR_STOP 0
|
||||
INSERT INTO hyper_pk(time, device_id,sensor_1) VALUES
|
||||
(1257987700000000000, 'dev2', 11);
|
||||
ERROR: duplicate key value violates unique constraint "6_6_hyper_pk_pkey"
|
||||
\set ON_ERROR_STOP 1
|
||||
--should have unique constraint not just unique index
|
||||
\d+ _timescaledb_internal._hyper_3_6_chunk
|
||||
Table "_timescaledb_internal._hyper_3_6_chunk"
|
||||
Column | Type | Modifiers | Storage | Stats target | Description
|
||||
-----------+---------+-----------+----------+--------------+-------------
|
||||
time | bigint | not null | plain | |
|
||||
device_id | text | not null | extended | |
|
||||
sensor_1 | numeric | default 1 | main | |
|
||||
Indexes:
|
||||
"6_6_hyper_pk_pkey" PRIMARY KEY, btree ("time")
|
||||
Check constraints:
|
||||
"constraint_6" CHECK ("time" >= '1257987700000000000'::bigint AND "time" < '1257987700000000010'::bigint)
|
||||
"hyper_pk_sensor_1_check" CHECK (sensor_1 > 10::numeric)
|
||||
Inherits: hyper_pk
|
||||
|
||||
ALTER TABLE hyper_pk DROP CONSTRAINT hyper_pk_pkey;
|
||||
\d+ _timescaledb_internal._hyper_3_6_chunk
|
||||
Table "_timescaledb_internal._hyper_3_6_chunk"
|
||||
Column | Type | Modifiers | Storage | Stats target | Description
|
||||
-----------+---------+-----------+----------+--------------+-------------
|
||||
time | bigint | not null | plain | |
|
||||
device_id | text | not null | extended | |
|
||||
sensor_1 | numeric | default 1 | main | |
|
||||
Check constraints:
|
||||
"constraint_6" CHECK ("time" >= '1257987700000000000'::bigint AND "time" < '1257987700000000010'::bigint)
|
||||
"hyper_pk_sensor_1_check" CHECK (sensor_1 > 10::numeric)
|
||||
Inherits: hyper_pk
|
||||
|
||||
--uniqueness not enforced
|
||||
INSERT INTO hyper_pk(time, device_id,sensor_1) VALUES
|
||||
(1257987700000000000, 'dev3', 11);
|
||||
--shouldn't be able to create pk
|
||||
\set ON_ERROR_STOP 0
|
||||
ALTER TABLE hyper_pk ADD CONSTRAINT hyper_pk_pkey PRIMARY KEY (time);
|
||||
ERROR: could not create unique index "6_7_hyper_pk_pkey"
|
||||
\set ON_ERROR_STOP 1
|
||||
DELETE FROM hyper_pk WHERE device_id = 'dev3';
|
||||
--cannot create pk constraint on non-partition column
|
||||
\set ON_ERROR_STOP 0
|
||||
ALTER TABLE hyper_pk ADD CONSTRAINT hyper_pk_invalid PRIMARY KEY (device_id);
|
||||
ERROR: Cannot create a unique index without the column: time (used in partitioning)
|
||||
\set ON_ERROR_STOP 1
|
||||
--now can create
|
||||
ALTER TABLE hyper_pk ADD CONSTRAINT hyper_pk_pkey PRIMARY KEY (time);
|
||||
\d+ _timescaledb_internal._hyper_3_6_chunk
|
||||
Table "_timescaledb_internal._hyper_3_6_chunk"
|
||||
Column | Type | Modifiers | Storage | Stats target | Description
|
||||
-----------+---------+-----------+----------+--------------+-------------
|
||||
time | bigint | not null | plain | |
|
||||
device_id | text | not null | extended | |
|
||||
sensor_1 | numeric | default 1 | main | |
|
||||
Indexes:
|
||||
"6_8_hyper_pk_pkey" PRIMARY KEY, btree ("time")
|
||||
Check constraints:
|
||||
"constraint_6" CHECK ("time" >= '1257987700000000000'::bigint AND "time" < '1257987700000000010'::bigint)
|
||||
"hyper_pk_sensor_1_check" CHECK (sensor_1 > 10::numeric)
|
||||
Inherits: hyper_pk
|
||||
|
||||
--test adding constraint with same name to different table -- should fail
|
||||
\set ON_ERROR_STOP 0
|
||||
ALTER TABLE hyper ADD CONSTRAINT hyper_pk_pkey UNIQUE (time);
|
||||
ERROR: relation "hyper_pk_pkey" already exists
|
||||
\set ON_ERROR_STOP 1
|
||||
--uniquness violation fails
|
||||
\set ON_ERROR_STOP 0
|
||||
INSERT INTO hyper_pk(time, device_id,sensor_1) VALUES
|
||||
(1257987700000000000, 'dev2', 11);
|
||||
ERROR: duplicate key value violates unique constraint "6_8_hyper_pk_pkey"
|
||||
\set ON_ERROR_STOP 1
|
||||
----------------------- FOREIGN KEY ------------------
|
||||
CREATE TABLE devices(
|
||||
device_id TEXT NOT NULL,
|
||||
PRIMARY KEY (device_id)
|
||||
);
|
||||
CREATE TABLE hyper_fk (
|
||||
time BIGINT NOT NULL PRIMARY KEY,
|
||||
device_id TEXT NOT NULL REFERENCES devices(device_id),
|
||||
sensor_1 NUMERIC NULL DEFAULT 1 CHECK (sensor_1 > 10)
|
||||
);
|
||||
SELECT * FROM create_hypertable('hyper_fk', 'time', chunk_time_interval => 10);
|
||||
create_hypertable
|
||||
-------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
--fail fk constraint
|
||||
\set ON_ERROR_STOP 0
|
||||
INSERT INTO hyper_fk(time, device_id,sensor_1) VALUES
|
||||
(1257987700000000000, 'dev2', 11);
|
||||
ERROR: insert or update on table "_hyper_4_7_chunk" violates foreign key constraint "7_10_hyper_fk_device_id_fkey"
|
||||
\set ON_ERROR_STOP 1
|
||||
INSERT INTO devices VALUES ('dev2');
|
||||
INSERT INTO hyper_fk(time, device_id,sensor_1) VALUES
|
||||
(1257987700000000000, 'dev2', 11);
|
||||
--delete should fail
|
||||
\set ON_ERROR_STOP 0
|
||||
DELETE FROM devices;
|
||||
ERROR: update or delete on table "devices" violates foreign key constraint "8_12_hyper_fk_device_id_fkey" on table "_hyper_4_8_chunk"
|
||||
\set ON_ERROR_STOP 1
|
||||
ALTER TABLE hyper_fk DROP CONSTRAINT hyper_fk_device_id_fkey;
|
||||
--should now be able to add non-fk rows
|
||||
INSERT INTO hyper_fk(time, device_id,sensor_1) VALUES
|
||||
(1257987700000000001, 'dev3', 11);
|
||||
--can't add fk because of dev3 row
|
||||
\set ON_ERROR_STOP 0
|
||||
ALTER TABLE hyper_fk ADD CONSTRAINT hyper_fk_device_id_fkey
|
||||
FOREIGN KEY (device_id) REFERENCES devices(device_id);
|
||||
ERROR: insert or update on table "_hyper_4_8_chunk" violates foreign key constraint "8_13_hyper_fk_device_id_fkey"
|
||||
\set ON_ERROR_STOP 1
|
||||
DELETE FROM hyper_fk WHERE device_id = 'dev3';
|
||||
ALTER TABLE hyper_fk ADD CONSTRAINT hyper_fk_device_id_fkey
|
||||
FOREIGN KEY (device_id) REFERENCES devices(device_id);
|
||||
\set ON_ERROR_STOP 0
|
||||
INSERT INTO hyper_fk(time, device_id,sensor_1) VALUES
|
||||
(1257987700000000002, 'dev3', 11);
|
||||
ERROR: insert or update on table "_hyper_4_8_chunk" violates foreign key constraint "8_14_hyper_fk_device_id_fkey"
|
||||
\set ON_ERROR_STOP 1
|
||||
----------------------- FOREIGN KEY INTO A HYPERTABLE ------------------
|
||||
--FOREIGN KEY references into a hypertable are currently broken.
|
||||
--The referencing table will never find the corresponding row in the hypertable
|
||||
--since it will only search the parent. Thus any insert will result in an ERROR
|
||||
--TODO: block such foreign keys or fix. (Hard to block on create table so punting for now)
|
||||
CREATE TABLE hyper_for_ref (
|
||||
time BIGINT NOT NULL PRIMARY KEY,
|
||||
device_id TEXT NOT NULL,
|
||||
sensor_1 NUMERIC NULL DEFAULT 1 CHECK (sensor_1 > 10)
|
||||
);
|
||||
SELECT * FROM create_hypertable('hyper_for_ref', 'time', chunk_time_interval => 10);
|
||||
create_hypertable
|
||||
-------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
\set ON_ERROR_STOP 0
|
||||
CREATE TABLE referrer (
|
||||
time BIGINT NOT NULL REFERENCES hyper_for_ref(time)
|
||||
);
|
||||
ERROR: Foreign keys to hypertables are not supported.
|
||||
\set ON_ERROR_STOP 1
|
||||
CREATE TABLE referrer2 (
|
||||
time BIGINT NOT NULL
|
||||
);
|
||||
\set ON_ERROR_STOP 0
|
||||
ALTER TABLE referrer2 ADD CONSTRAINT hyper_fk_device_id_fkey
|
||||
FOREIGN KEY (time) REFERENCES hyper_for_ref(time);
|
||||
ERROR: Foreign keys to hypertables are not supported.
|
||||
\set ON_ERROR_STOP 1
|
||||
----------------------- EXCLUSION CONSTRAINT ------------------
|
||||
CREATE TABLE hyper_ex (
|
||||
time BIGINT,
|
||||
device_id TEXT NOT NULL REFERENCES devices(device_id),
|
||||
sensor_1 NUMERIC NULL DEFAULT 1 CHECK (sensor_1 > 10),
|
||||
canceled boolean DEFAULT false,
|
||||
EXCLUDE USING btree (
|
||||
time WITH =, device_id WITH =
|
||||
) WHERE (not canceled)
|
||||
);
|
||||
SELECT * FROM create_hypertable('hyper_ex', 'time', chunk_time_interval=>_timescaledb_internal.interval_to_usec('1 month'));
|
||||
create_hypertable
|
||||
-------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
INSERT INTO hyper_ex(time, device_id,sensor_1) VALUES
|
||||
(1257987700000000000, 'dev2', 11);
|
||||
\set ON_ERROR_STOP 0
|
||||
INSERT INTO hyper_ex(time, device_id,sensor_1) VALUES
|
||||
(1257987700000000000, 'dev2', 12);
|
||||
ERROR: conflicting key value violates exclusion constraint "9_15_hyper_ex_time_device_id_excl"
|
||||
\set ON_ERROR_STOP 1
|
||||
ALTER TABLE hyper_ex DROP CONSTRAINT hyper_ex_time_device_id_excl;
|
||||
--can now add
|
||||
INSERT INTO hyper_ex(time, device_id,sensor_1) VALUES
|
||||
(1257987700000000000, 'dev2', 12);
|
||||
--cannot add because of conflicts
|
||||
\set ON_ERROR_STOP 0
|
||||
ALTER TABLE hyper_ex ADD CONSTRAINT hyper_ex_time_device_id_excl
|
||||
EXCLUDE USING btree (
|
||||
time WITH =, device_id WITH =
|
||||
) WHERE (not canceled)
|
||||
;
|
||||
ERROR: could not create exclusion constraint "9_17_hyper_ex_time_device_id_excl"
|
||||
\set ON_ERROR_STOP 1
|
||||
DELETE FROM hyper_ex WHERE sensor_1 = 12;
|
||||
ALTER TABLE hyper_ex ADD CONSTRAINT hyper_ex_time_device_id_excl
|
||||
EXCLUDE USING btree (
|
||||
time WITH =, device_id WITH =
|
||||
) WHERE (not canceled)
|
||||
;
|
||||
\set ON_ERROR_STOP 0
|
||||
INSERT INTO hyper_ex(time, device_id,sensor_1) VALUES
|
||||
(1257987700000000000, 'dev2', 12);
|
||||
ERROR: conflicting key value violates exclusion constraint "9_18_hyper_ex_time_device_id_excl"
|
||||
\set ON_ERROR_STOP 1
|
||||
--cannot add exclusion constraint without partition key.
|
||||
CREATE TABLE hyper_ex_invalid (
|
||||
time BIGINT,
|
||||
device_id TEXT NOT NULL REFERENCES devices(device_id),
|
||||
sensor_1 NUMERIC NULL DEFAULT 1 CHECK (sensor_1 > 10),
|
||||
canceled boolean DEFAULT false,
|
||||
EXCLUDE USING btree (
|
||||
device_id WITH =
|
||||
) WHERE (not canceled)
|
||||
);
|
||||
\set ON_ERROR_STOP 0
|
||||
SELECT * FROM create_hypertable('hyper_ex_invalid', 'time', chunk_time_interval=>_timescaledb_internal.interval_to_usec('1 month'));
|
||||
ERROR: Cannot create a unique index without the column: time (used in partitioning)
|
||||
\set ON_ERROR_STOP 1
|
@ -71,13 +71,95 @@ SELECT set_chunk_time_interval('chunk_test', 1::bigint);
|
||||
|
||||
(1 row)
|
||||
|
||||
<<<<<<< HEAD
|
||||
INSERT INTO chunk_test VALUES (8, 24.3, 79669, 1);
|
||||
SELECT set_chunk_time_interval('chunk_test', 5::bigint);
|
||||
=======
|
||||
INSERT INTO chunk_test VALUES(23, 3, 'dev3');
|
||||
SELECT * FROM chunk_test order by time, metric, device_id;
|
||||
time | metric | device_id
|
||||
------+--------+-----------
|
||||
1 | 1 | dev1
|
||||
2 | 2 | dev2
|
||||
23 | 3 | dev3
|
||||
45 | 2 | dev2
|
||||
46 | 2 | dev2
|
||||
(5 rows)
|
||||
|
||||
SELECT * FROM _timescaledb_catalog.chunk;
|
||||
id | hypertable_id | schema_name | table_name
|
||||
----+---------------+-----------------------+------------------
|
||||
1 | 1 | _timescaledb_internal | _hyper_1_1_chunk
|
||||
2 | 1 | _timescaledb_internal | _hyper_1_2_chunk
|
||||
3 | 1 | _timescaledb_internal | _hyper_1_3_chunk
|
||||
4 | 1 | _timescaledb_internal | _hyper_1_4_chunk
|
||||
(4 rows)
|
||||
|
||||
SELECT * FROM _timescaledb_catalog.hypertable;
|
||||
id | schema_name | table_name | associated_schema_name | associated_table_prefix | num_dimensions
|
||||
----+-------------+------------+------------------------+-------------------------+----------------
|
||||
1 | public | chunk_test | _timescaledb_internal | _hyper_1 | 2
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM ONLY chunk_test;
|
||||
time | metric | device_id
|
||||
------+--------+-----------
|
||||
(0 rows)
|
||||
|
||||
SELECT * 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;
|
||||
id | hypertable_id | schema_name | table_name | chunk_id | dimension_slice_id | constraint_name | hypertable_constraint_name | id | dimension_id | range_start | range_end | id | hypertable_id | column_name | column_type | aligned | num_slices | partitioning_func_schema | partitioning_func | interval_length | id | schema_name | table_name | associated_schema_name | associated_table_prefix | num_dimensions
|
||||
----+---------------+-----------------------+------------------+----------+--------------------+-----------------+----------------------------+----+--------------+-------------+------------+----+---------------+-------------+-------------+---------+------------+--------------------------+-----------------------+-----------------+----+-------------+------------+------------------------+-------------------------+----------------
|
||||
1 | 1 | _timescaledb_internal | _hyper_1_1_chunk | 1 | 1 | constraint_1 | | 1 | 1 | 0 | 10 | 1 | 1 | time | bigint | t | | | | 40 | 1 | public | chunk_test | _timescaledb_internal | _hyper_1 | 2
|
||||
1 | 1 | _timescaledb_internal | _hyper_1_1_chunk | 1 | 2 | constraint_2 | | 2 | 2 | 1073741823 | 2147483647 | 2 | 1 | device_id | text | f | 2 | _timescaledb_internal | get_partition_for_key | | 1 | public | chunk_test | _timescaledb_internal | _hyper_1 | 2
|
||||
2 | 1 | _timescaledb_internal | _hyper_1_2_chunk | 2 | 1 | constraint_1 | | 1 | 1 | 0 | 10 | 1 | 1 | time | bigint | t | | | | 40 | 1 | public | chunk_test | _timescaledb_internal | _hyper_1 | 2
|
||||
2 | 1 | _timescaledb_internal | _hyper_1_2_chunk | 2 | 4 | constraint_4 | | 4 | 2 | 0 | 1073741823 | 2 | 1 | device_id | text | f | 2 | _timescaledb_internal | get_partition_for_key | | 1 | public | chunk_test | _timescaledb_internal | _hyper_1 | 2
|
||||
3 | 1 | _timescaledb_internal | _hyper_1_3_chunk | 3 | 5 | constraint_5 | | 5 | 1 | 40 | 50 | 1 | 1 | time | bigint | t | | | | 40 | 1 | public | chunk_test | _timescaledb_internal | _hyper_1 | 2
|
||||
3 | 1 | _timescaledb_internal | _hyper_1_3_chunk | 3 | 4 | constraint_4 | | 4 | 2 | 0 | 1073741823 | 2 | 1 | device_id | text | f | 2 | _timescaledb_internal | get_partition_for_key | | 1 | public | chunk_test | _timescaledb_internal | _hyper_1 | 2
|
||||
4 | 1 | _timescaledb_internal | _hyper_1_4_chunk | 4 | 7 | constraint_7 | | 7 | 1 | 10 | 40 | 1 | 1 | time | bigint | t | | | | 40 | 1 | public | chunk_test | _timescaledb_internal | _hyper_1 | 2
|
||||
4 | 1 | _timescaledb_internal | _hyper_1_4_chunk | 4 | 4 | constraint_4 | | 4 | 2 | 0 | 1073741823 | 2 | 1 | device_id | text | f | 2 | _timescaledb_internal | get_partition_for_key | | 1 | public | chunk_test | _timescaledb_internal | _hyper_1 | 2
|
||||
(8 rows)
|
||||
|
||||
-- Test chunk aligning between partitions
|
||||
CREATE TABLE chunk_align_test(
|
||||
time BIGINT,
|
||||
metric INTEGER,
|
||||
device_id TEXT
|
||||
);
|
||||
SELECT * FROM create_hypertable('chunk_align_test', 'time', 'device_id', 2, chunk_time_interval => 10);
|
||||
create_hypertable
|
||||
-------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
INSERT INTO chunk_align_test VALUES (1, 1, 'dev1'); -- this should create a 10 wide chunk
|
||||
SELECT * 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_align_test'
|
||||
AND d.column_name = 'time'
|
||||
ORDER BY c.id, d.id;
|
||||
id | hypertable_id | schema_name | table_name | chunk_id | dimension_slice_id | constraint_name | hypertable_constraint_name | id | dimension_id | range_start | range_end | id | hypertable_id | column_name | column_type | aligned | num_slices | partitioning_func_schema | partitioning_func | interval_length | id | schema_name | table_name | associated_schema_name | associated_table_prefix | num_dimensions
|
||||
----+---------------+-----------------------+------------------+----------+--------------------+-----------------+----------------------------+----+--------------+-------------+-----------+----+---------------+-------------+-------------+---------+------------+--------------------------+-------------------+-----------------+----+-------------+------------------+------------------------+-------------------------+----------------
|
||||
5 | 2 | _timescaledb_internal | _hyper_2_5_chunk | 5 | 9 | constraint_9 | | 9 | 3 | 0 | 10 | 3 | 2 | time | bigint | t | | | | 10 | 2 | public | chunk_align_test | _timescaledb_internal | _hyper_2 | 2
|
||||
(1 row)
|
||||
|
||||
|
||||
SELECT * FROM set_chunk_time_interval('chunk_align_test', 40::bigint);
|
||||
>>>>>>> 98565c0... Add support for hypertable constraints
|
||||
set_chunk_time_interval
|
||||
-------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
<<<<<<< HEAD
|
||||
SELECT * FROM _timescaledb_catalog.dimension;
|
||||
id | hypertable_id | column_name | column_type | aligned | num_slices | partitioning_func_schema | partitioning_func | interval_length
|
||||
----+---------------+-------------+-------------+---------+------------+--------------------------+-----------------------+-----------------
|
||||
@ -115,4 +197,46 @@ ORDER BY c.id, d.id;
|
||||
_hyper_1_8_chunk | 1 | 9 | 15 | 20
|
||||
_hyper_1_8_chunk | 2 | 6 | 0 | 715827882
|
||||
(16 rows)
|
||||
=======
|
||||
INSERT INTO chunk_align_test VALUES (5, 1, 'dev2'); -- this should still create a 10 wide chunk
|
||||
INSERT INTO chunk_align_test VALUES (45, 1, 'dev2'); -- this should create a 40 wide chunk
|
||||
SELECT * 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_align_test'
|
||||
AND d.column_name = 'time'
|
||||
ORDER BY c.id, d.id;
|
||||
id | hypertable_id | schema_name | table_name | chunk_id | dimension_slice_id | constraint_name | hypertable_constraint_name | id | dimension_id | range_start | range_end | id | hypertable_id | column_name | column_type | aligned | num_slices | partitioning_func_schema | partitioning_func | interval_length | id | schema_name | table_name | associated_schema_name | associated_table_prefix | num_dimensions
|
||||
----+---------------+-----------------------+------------------+----------+--------------------+-----------------+----------------------------+----+--------------+-------------+-----------+----+---------------+-------------+-------------+---------+------------+--------------------------+-------------------+-----------------+----+-------------+------------------+------------------------+-------------------------+----------------
|
||||
5 | 2 | _timescaledb_internal | _hyper_2_5_chunk | 5 | 9 | constraint_9 | | 9 | 3 | 0 | 10 | 3 | 2 | time | bigint | t | | | | 40 | 2 | public | chunk_align_test | _timescaledb_internal | _hyper_2 | 2
|
||||
6 | 2 | _timescaledb_internal | _hyper_2_6_chunk | 6 | 9 | constraint_9 | | 9 | 3 | 0 | 10 | 3 | 2 | time | bigint | t | | | | 40 | 2 | public | chunk_align_test | _timescaledb_internal | _hyper_2 | 2
|
||||
7 | 2 | _timescaledb_internal | _hyper_2_7_chunk | 7 | 13 | constraint_13 | | 13 | 3 | 40 | 80 | 3 | 2 | time | bigint | t | | | | 40 | 2 | public | chunk_align_test | _timescaledb_internal | _hyper_2 | 2
|
||||
(3 rows)
|
||||
|
||||
--check the cut-to-size with aligned dimensions code
|
||||
INSERT INTO chunk_align_test VALUES (35, 1, 'dev1');
|
||||
INSERT INTO chunk_align_test VALUES (35, 1, 'dev2');
|
||||
INSERT INTO chunk_align_test VALUES (81, 1, 'dev1');
|
||||
INSERT INTO chunk_align_test VALUES (81, 1, 'dev2');
|
||||
SELECT * 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_align_test'
|
||||
AND d.column_name = 'time'
|
||||
ORDER BY c.id, d.id;
|
||||
id | hypertable_id | schema_name | table_name | chunk_id | dimension_slice_id | constraint_name | hypertable_constraint_name | id | dimension_id | range_start | range_end | id | hypertable_id | column_name | column_type | aligned | num_slices | partitioning_func_schema | partitioning_func | interval_length | id | schema_name | table_name | associated_schema_name | associated_table_prefix | num_dimensions
|
||||
----+---------------+-----------------------+-------------------+----------+--------------------+-----------------+----------------------------+----+--------------+-------------+-----------+----+---------------+-------------+-------------+---------+------------+--------------------------+-------------------+-----------------+----+-------------+------------------+------------------------+-------------------------+----------------
|
||||
5 | 2 | _timescaledb_internal | _hyper_2_5_chunk | 5 | 9 | constraint_9 | | 9 | 3 | 0 | 10 | 3 | 2 | time | bigint | t | | | | 40 | 2 | public | chunk_align_test | _timescaledb_internal | _hyper_2 | 2
|
||||
6 | 2 | _timescaledb_internal | _hyper_2_6_chunk | 6 | 9 | constraint_9 | | 9 | 3 | 0 | 10 | 3 | 2 | time | bigint | t | | | | 40 | 2 | public | chunk_align_test | _timescaledb_internal | _hyper_2 | 2
|
||||
7 | 2 | _timescaledb_internal | _hyper_2_7_chunk | 7 | 13 | constraint_13 | | 13 | 3 | 40 | 80 | 3 | 2 | time | bigint | t | | | | 40 | 2 | public | chunk_align_test | _timescaledb_internal | _hyper_2 | 2
|
||||
8 | 2 | _timescaledb_internal | _hyper_2_8_chunk | 8 | 15 | constraint_15 | | 15 | 3 | 10 | 40 | 3 | 2 | time | bigint | t | | | | 40 | 2 | public | chunk_align_test | _timescaledb_internal | _hyper_2 | 2
|
||||
9 | 2 | _timescaledb_internal | _hyper_2_9_chunk | 9 | 15 | constraint_15 | | 15 | 3 | 10 | 40 | 3 | 2 | time | bigint | t | | | | 40 | 2 | public | chunk_align_test | _timescaledb_internal | _hyper_2 | 2
|
||||
10 | 2 | _timescaledb_internal | _hyper_2_10_chunk | 10 | 19 | constraint_19 | | 19 | 3 | 80 | 120 | 3 | 2 | time | bigint | t | | | | 40 | 2 | public | chunk_align_test | _timescaledb_internal | _hyper_2 | 2
|
||||
11 | 2 | _timescaledb_internal | _hyper_2_11_chunk | 11 | 19 | constraint_19 | | 19 | 3 | 80 | 120 | 3 | 2 | time | bigint | t | | | | 40 | 2 | public | chunk_align_test | _timescaledb_internal | _hyper_2 | 2
|
||||
(7 rows)
|
||||
>>>>>>> 98565c0... Add support for hypertable constraints
|
||||
|
||||
|
@ -53,13 +53,13 @@ SELECT * FROM _timescaledb_catalog.hypertable;
|
||||
2 | customSchema | Hypertable_1 | _timescaledb_internal | _hyper_2 | 1
|
||||
(2 rows)
|
||||
|
||||
SELECT * FROM _timescaledb_catalog.hypertable_index;
|
||||
SELECT * FROM _timescaledb_catalog.hypertable_index ORDER BY format('%I.%I', main_schema_name, main_index_name)::regclass;
|
||||
hypertable_id | main_schema_name | main_index_name | definition
|
||||
---------------+------------------+---------------------------------+--------------------------------------------------------------------------------------
|
||||
1 | public | Hypertable_1_time_Device_id_idx | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("time", "Device_id")
|
||||
2 | customSchema | Hypertable_1_time_Device_id_idx | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("time", "Device_id")
|
||||
1 | public | Hypertable_1_time_idx | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("time" DESC)
|
||||
1 | public | Hypertable_1_Device_id_time_idx | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("Device_id", "time" DESC)
|
||||
2 | customSchema | Hypertable_1_time_Device_id_idx | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("time", "Device_id")
|
||||
2 | customSchema | Hypertable_1_time_idx | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("time" DESC)
|
||||
(5 rows)
|
||||
|
||||
@ -74,13 +74,13 @@ INSERT INTO "customSchema"."Hypertable_1"(time, "Device_id", temp_c, humidity, s
|
||||
VALUES(1257894000000000000, 'dev1', 30, 70, 1, 2, 3, 100);
|
||||
INSERT INTO "customSchema"."Hypertable_1"(time, "Device_id", temp_c, humidity, sensor_1, sensor_2, sensor_3, sensor_4)
|
||||
VALUES(1257894000000000001, 'dev1', 30, 70, 1, 2, 3, 100);
|
||||
SELECT * FROM _timescaledb_catalog.hypertable_index;
|
||||
SELECT * FROM _timescaledb_catalog.hypertable_index ORDER BY format('%I.%I', main_schema_name, main_index_name)::regclass;
|
||||
hypertable_id | main_schema_name | main_index_name | definition
|
||||
---------------+------------------+---------------------------------+----------------------------------------------------------------------------------------
|
||||
1 | public | Hypertable_1_time_Device_id_idx | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("time", "Device_id")
|
||||
2 | customSchema | Hypertable_1_time_Device_id_idx | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("time", "Device_id")
|
||||
1 | public | Hypertable_1_time_idx | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("time" DESC)
|
||||
1 | public | Hypertable_1_Device_id_time_idx | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("Device_id", "time" DESC)
|
||||
2 | customSchema | Hypertable_1_time_Device_id_idx | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("time", "Device_id")
|
||||
2 | customSchema | Hypertable_1_time_idx | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("time" DESC)
|
||||
1 | public | Hypertable_1_time_temp_c_idx | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("time", temp_c)
|
||||
1 | public | ind_humidity | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("time", humidity)
|
||||
|
@ -53,13 +53,13 @@ SELECT * FROM _timescaledb_catalog.hypertable;
|
||||
2 | customSchema | Hypertable_1 | _timescaledb_internal | _hyper_2 | 1
|
||||
(2 rows)
|
||||
|
||||
SELECT * FROM _timescaledb_catalog.hypertable_index;
|
||||
SELECT * FROM _timescaledb_catalog.hypertable_index ORDER BY format('%I.%I', main_schema_name, main_index_name)::regclass;
|
||||
hypertable_id | main_schema_name | main_index_name | definition
|
||||
---------------+------------------+---------------------------------+--------------------------------------------------------------------------------------
|
||||
1 | public | Hypertable_1_time_Device_id_idx | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("time", "Device_id")
|
||||
2 | customSchema | Hypertable_1_time_Device_id_idx | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("time", "Device_id")
|
||||
1 | public | Hypertable_1_time_idx | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("time" DESC)
|
||||
1 | public | Hypertable_1_Device_id_time_idx | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("Device_id", "time" DESC)
|
||||
2 | customSchema | Hypertable_1_time_Device_id_idx | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("time", "Device_id")
|
||||
2 | customSchema | Hypertable_1_time_idx | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("time" DESC)
|
||||
(5 rows)
|
||||
|
||||
@ -74,13 +74,13 @@ INSERT INTO "customSchema"."Hypertable_1"(time, "Device_id", temp_c, humidity, s
|
||||
VALUES(1257894000000000000, 'dev1', 30, 70, 1, 2, 3, 100);
|
||||
INSERT INTO "customSchema"."Hypertable_1"(time, "Device_id", temp_c, humidity, sensor_1, sensor_2, sensor_3, sensor_4)
|
||||
VALUES(1257894000000000001, 'dev1', 30, 70, 1, 2, 3, 100);
|
||||
SELECT * FROM _timescaledb_catalog.hypertable_index;
|
||||
SELECT * FROM _timescaledb_catalog.hypertable_index ORDER BY format('%I.%I', main_schema_name, main_index_name)::regclass;
|
||||
hypertable_id | main_schema_name | main_index_name | definition
|
||||
---------------+------------------+---------------------------------+----------------------------------------------------------------------------------------
|
||||
1 | public | Hypertable_1_time_Device_id_idx | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("time", "Device_id")
|
||||
2 | customSchema | Hypertable_1_time_Device_id_idx | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("time", "Device_id")
|
||||
1 | public | Hypertable_1_time_idx | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("time" DESC)
|
||||
1 | public | Hypertable_1_Device_id_time_idx | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("Device_id", "time" DESC)
|
||||
2 | customSchema | Hypertable_1_time_Device_id_idx | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("time", "Device_id")
|
||||
2 | customSchema | Hypertable_1_time_idx | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("time" DESC)
|
||||
1 | public | Hypertable_1_time_temp_c_idx | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("time", temp_c)
|
||||
1 | public | ind_humidity | CREATE INDEX /*INDEX_NAME*/ ON /*TABLE_NAME*/ USING btree ("time", humidity)
|
||||
|
@ -407,7 +407,7 @@ INSERT INTO "3dim" VALUES('2017-01-20T09:00:47', 25.1, 'yellow', 'la');
|
||||
time | integer | | plain | |
|
||||
temp | double precision | | plain | |
|
||||
Indexes:
|
||||
"25-1dim_neg_time_idx" btree ("time" DESC)
|
||||
"21-1dim_neg_time_idx" btree ("time" DESC)
|
||||
Check constraints:
|
||||
"constraint_10" CHECK ("time" >= 0 AND "time" < 10)
|
||||
Inherits: "1dim_neg"
|
||||
|
@ -43,7 +43,15 @@ SELECT count(*)
|
||||
AND refobjid = (SELECT oid FROM pg_extension WHERE extname = 'timescaledb');
|
||||
count
|
||||
-------
|
||||
<<<<<<< HEAD
|
||||
121
|
||||
=======
|
||||
<<<<<<< HEAD
|
||||
120
|
||||
=======
|
||||
135
|
||||
>>>>>>> 98565c0... Add support for hypertable constraints
|
||||
>>>>>>> Add support for hypertable constraints
|
||||
(1 row)
|
||||
|
||||
\c postgres
|
||||
@ -67,7 +75,15 @@ SELECT count(*)
|
||||
AND refobjid = (SELECT oid FROM pg_extension WHERE extname = 'timescaledb');
|
||||
count
|
||||
-------
|
||||
<<<<<<< HEAD
|
||||
121
|
||||
=======
|
||||
<<<<<<< HEAD
|
||||
120
|
||||
=======
|
||||
135
|
||||
>>>>>>> 98565c0... Add support for hypertable constraints
|
||||
>>>>>>> Add support for hypertable constraints
|
||||
(1 row)
|
||||
|
||||
\c single
|
||||
|
@ -46,7 +46,7 @@ INSERT INTO upsert_test VALUES ('2017-01-21T09:00:01', 22.5, 'yellow') RETURNING
|
||||
(1 row)
|
||||
|
||||
UPDATE upsert_test SET time = '2017-01-20T09:00:01';
|
||||
ERROR: duplicate key value violates unique constraint "1-upsert_test_pkey"
|
||||
ERROR: duplicate key value violates unique constraint "1_1_upsert_test_pkey"
|
||||
\set ON_ERROR_STOP 1
|
||||
-- Test with UNIQUE index on multiple columns instead of PRIMARY KEY constraint
|
||||
CREATE TABLE upsert_test_unique(time timestamp, temp float, color text);
|
||||
@ -126,5 +126,5 @@ SELECT * FROM upsert_test_multi_unique ORDER BY time, color DESC;
|
||||
\set ON_ERROR_STOP 0
|
||||
INSERT INTO upsert_test_multi_unique VALUES ('2017-01-20T09:00:01', 23.5, 'purple') ON CONFLICT (time, color)
|
||||
DO UPDATE set temp = 23.5;
|
||||
ERROR: duplicate key value violates unique constraint "5-multi_time_temp_idx"
|
||||
ERROR: duplicate key value violates unique constraint "4-multi_time_temp_idx"
|
||||
\set ON_ERROR_STOP 1
|
||||
|
301
test/sql/constraint.sql
Normal file
301
test/sql/constraint.sql
Normal file
@ -0,0 +1,301 @@
|
||||
\o /dev/null
|
||||
\ir include/create_single_db.sql
|
||||
\o
|
||||
|
||||
CREATE TABLE hyper (
|
||||
time BIGINT NOT NULL,
|
||||
device_id TEXT NOT NULL,
|
||||
sensor_1 NUMERIC NULL DEFAULT 1 CHECK (sensor_1 > 10)
|
||||
);
|
||||
|
||||
|
||||
SELECT * FROM create_hypertable('hyper', 'time', chunk_time_interval => 10);
|
||||
|
||||
--check and not-null constraints are inherited through regular inheritance.
|
||||
|
||||
\set ON_ERROR_STOP 0
|
||||
INSERT INTO hyper(time, device_id,sensor_1) VALUES
|
||||
(1257987700000000000, 'dev2', 9);
|
||||
|
||||
INSERT INTO hyper(time, device_id,sensor_1) VALUES
|
||||
(1257987700000000000, NULL, 11);
|
||||
|
||||
\set ON_ERROR_STOP 1
|
||||
|
||||
INSERT INTO hyper(time, device_id,sensor_1) VALUES
|
||||
(1257987700000000000, 'dev2', 11);
|
||||
|
||||
INSERT INTO hyper(time, device_id,sensor_1) VALUES
|
||||
(1257987700000000000, 'dev2', 11);
|
||||
|
||||
----------------------- UNIQUE CONSTRAINTS ------------------
|
||||
|
||||
CREATE TABLE hyper_unique (
|
||||
time BIGINT NOT NULL UNIQUE,
|
||||
device_id TEXT NOT NULL,
|
||||
sensor_1 NUMERIC NULL DEFAULT 1 CHECK (sensor_1 > 10)
|
||||
);
|
||||
|
||||
|
||||
SELECT * FROM create_hypertable('hyper_unique', 'time', chunk_time_interval => 10);
|
||||
|
||||
INSERT INTO hyper_unique(time, device_id,sensor_1) VALUES
|
||||
(1257987700000000000, 'dev2', 11);
|
||||
|
||||
INSERT INTO hyper_unique(time, device_id,sensor_1) VALUES
|
||||
(1257987800000000000, 'dev2', 11);
|
||||
|
||||
|
||||
\set ON_ERROR_STOP 0
|
||||
INSERT INTO hyper_unique(time, device_id,sensor_1) VALUES
|
||||
(1257987700000000000, 'dev2', 11);
|
||||
\set ON_ERROR_STOP 1
|
||||
|
||||
\d+ hyper
|
||||
\d+ hyper_unique
|
||||
--should have unique constraint not just unique index
|
||||
\d+ _timescaledb_internal._hyper_2_4_chunk
|
||||
|
||||
ALTER TABLE hyper_unique DROP CONSTRAINT hyper_unique_time_key;
|
||||
\d+ _timescaledb_internal._hyper_2_4_chunk
|
||||
|
||||
--uniqueness not enforced
|
||||
INSERT INTO hyper_unique(time, device_id,sensor_1) VALUES
|
||||
(1257987700000000000, 'dev3', 11);
|
||||
|
||||
|
||||
--shouldn't be able to create constraint
|
||||
\set ON_ERROR_STOP 0
|
||||
ALTER TABLE hyper_unique ADD CONSTRAINT hyper_unique_time_key UNIQUE (time);
|
||||
\set ON_ERROR_STOP 1
|
||||
|
||||
DELETE FROM hyper_unique WHERE device_id = 'dev3';
|
||||
|
||||
--now can create
|
||||
ALTER TABLE hyper_unique ADD CONSTRAINT hyper_unique_time_key UNIQUE (time);
|
||||
\d+ _timescaledb_internal._hyper_2_4_chunk
|
||||
|
||||
--test adding constraint with same name to different table -- should fail
|
||||
\set ON_ERROR_STOP 0
|
||||
ALTER TABLE hyper ADD CONSTRAINT hyper_unique_time_key UNIQUE (time);
|
||||
\set ON_ERROR_STOP 1
|
||||
|
||||
--uniquness violation fails
|
||||
\set ON_ERROR_STOP 0
|
||||
INSERT INTO hyper_unique(time, device_id,sensor_1) VALUES
|
||||
(1257987700000000000, 'dev2', 11);
|
||||
\set ON_ERROR_STOP 1
|
||||
|
||||
--cannot create unique constraint on non-partition column
|
||||
\set ON_ERROR_STOP 0
|
||||
ALTER TABLE hyper_unique ADD CONSTRAINT hyper_unique_invalid UNIQUE (device_id);
|
||||
\set ON_ERROR_STOP 1
|
||||
|
||||
|
||||
----------------------- PRIMARY KEY ------------------
|
||||
|
||||
CREATE TABLE hyper_pk (
|
||||
time BIGINT NOT NULL PRIMARY KEY,
|
||||
device_id TEXT NOT NULL,
|
||||
sensor_1 NUMERIC NULL DEFAULT 1 CHECK (sensor_1 > 10)
|
||||
);
|
||||
|
||||
|
||||
SELECT * FROM create_hypertable('hyper_pk', 'time', chunk_time_interval => 10);
|
||||
|
||||
INSERT INTO hyper_pk(time, device_id,sensor_1) VALUES
|
||||
(1257987700000000000, 'dev2', 11);
|
||||
|
||||
\set ON_ERROR_STOP 0
|
||||
INSERT INTO hyper_pk(time, device_id,sensor_1) VALUES
|
||||
(1257987700000000000, 'dev2', 11);
|
||||
\set ON_ERROR_STOP 1
|
||||
|
||||
--should have unique constraint not just unique index
|
||||
\d+ _timescaledb_internal._hyper_3_6_chunk
|
||||
|
||||
ALTER TABLE hyper_pk DROP CONSTRAINT hyper_pk_pkey;
|
||||
\d+ _timescaledb_internal._hyper_3_6_chunk
|
||||
|
||||
--uniqueness not enforced
|
||||
INSERT INTO hyper_pk(time, device_id,sensor_1) VALUES
|
||||
(1257987700000000000, 'dev3', 11);
|
||||
|
||||
|
||||
--shouldn't be able to create pk
|
||||
\set ON_ERROR_STOP 0
|
||||
ALTER TABLE hyper_pk ADD CONSTRAINT hyper_pk_pkey PRIMARY KEY (time);
|
||||
\set ON_ERROR_STOP 1
|
||||
|
||||
DELETE FROM hyper_pk WHERE device_id = 'dev3';
|
||||
|
||||
--cannot create pk constraint on non-partition column
|
||||
\set ON_ERROR_STOP 0
|
||||
ALTER TABLE hyper_pk ADD CONSTRAINT hyper_pk_invalid PRIMARY KEY (device_id);
|
||||
\set ON_ERROR_STOP 1
|
||||
|
||||
--now can create
|
||||
ALTER TABLE hyper_pk ADD CONSTRAINT hyper_pk_pkey PRIMARY KEY (time);
|
||||
\d+ _timescaledb_internal._hyper_3_6_chunk
|
||||
|
||||
--test adding constraint with same name to different table -- should fail
|
||||
\set ON_ERROR_STOP 0
|
||||
ALTER TABLE hyper ADD CONSTRAINT hyper_pk_pkey UNIQUE (time);
|
||||
\set ON_ERROR_STOP 1
|
||||
|
||||
--uniquness violation fails
|
||||
\set ON_ERROR_STOP 0
|
||||
INSERT INTO hyper_pk(time, device_id,sensor_1) VALUES
|
||||
(1257987700000000000, 'dev2', 11);
|
||||
\set ON_ERROR_STOP 1
|
||||
|
||||
|
||||
----------------------- FOREIGN KEY ------------------
|
||||
|
||||
CREATE TABLE devices(
|
||||
device_id TEXT NOT NULL,
|
||||
PRIMARY KEY (device_id)
|
||||
);
|
||||
|
||||
CREATE TABLE hyper_fk (
|
||||
time BIGINT NOT NULL PRIMARY KEY,
|
||||
device_id TEXT NOT NULL REFERENCES devices(device_id),
|
||||
sensor_1 NUMERIC NULL DEFAULT 1 CHECK (sensor_1 > 10)
|
||||
);
|
||||
|
||||
|
||||
SELECT * FROM create_hypertable('hyper_fk', 'time', chunk_time_interval => 10);
|
||||
|
||||
--fail fk constraint
|
||||
\set ON_ERROR_STOP 0
|
||||
INSERT INTO hyper_fk(time, device_id,sensor_1) VALUES
|
||||
(1257987700000000000, 'dev2', 11);
|
||||
\set ON_ERROR_STOP 1
|
||||
|
||||
INSERT INTO devices VALUES ('dev2');
|
||||
|
||||
INSERT INTO hyper_fk(time, device_id,sensor_1) VALUES
|
||||
(1257987700000000000, 'dev2', 11);
|
||||
|
||||
--delete should fail
|
||||
\set ON_ERROR_STOP 0
|
||||
DELETE FROM devices;
|
||||
\set ON_ERROR_STOP 1
|
||||
|
||||
ALTER TABLE hyper_fk DROP CONSTRAINT hyper_fk_device_id_fkey;
|
||||
|
||||
--should now be able to add non-fk rows
|
||||
INSERT INTO hyper_fk(time, device_id,sensor_1) VALUES
|
||||
(1257987700000000001, 'dev3', 11);
|
||||
|
||||
--can't add fk because of dev3 row
|
||||
\set ON_ERROR_STOP 0
|
||||
ALTER TABLE hyper_fk ADD CONSTRAINT hyper_fk_device_id_fkey
|
||||
FOREIGN KEY (device_id) REFERENCES devices(device_id);
|
||||
\set ON_ERROR_STOP 1
|
||||
|
||||
DELETE FROM hyper_fk WHERE device_id = 'dev3';
|
||||
|
||||
ALTER TABLE hyper_fk ADD CONSTRAINT hyper_fk_device_id_fkey
|
||||
FOREIGN KEY (device_id) REFERENCES devices(device_id);
|
||||
|
||||
\set ON_ERROR_STOP 0
|
||||
INSERT INTO hyper_fk(time, device_id,sensor_1) VALUES
|
||||
(1257987700000000002, 'dev3', 11);
|
||||
\set ON_ERROR_STOP 1
|
||||
|
||||
----------------------- FOREIGN KEY INTO A HYPERTABLE ------------------
|
||||
|
||||
|
||||
--FOREIGN KEY references into a hypertable are currently broken.
|
||||
--The referencing table will never find the corresponding row in the hypertable
|
||||
--since it will only search the parent. Thus any insert will result in an ERROR
|
||||
--TODO: block such foreign keys or fix. (Hard to block on create table so punting for now)
|
||||
CREATE TABLE hyper_for_ref (
|
||||
time BIGINT NOT NULL PRIMARY KEY,
|
||||
device_id TEXT NOT NULL,
|
||||
sensor_1 NUMERIC NULL DEFAULT 1 CHECK (sensor_1 > 10)
|
||||
);
|
||||
|
||||
SELECT * FROM create_hypertable('hyper_for_ref', 'time', chunk_time_interval => 10);
|
||||
|
||||
\set ON_ERROR_STOP 0
|
||||
CREATE TABLE referrer (
|
||||
time BIGINT NOT NULL REFERENCES hyper_for_ref(time)
|
||||
);
|
||||
\set ON_ERROR_STOP 1
|
||||
|
||||
CREATE TABLE referrer2 (
|
||||
time BIGINT NOT NULL
|
||||
);
|
||||
|
||||
\set ON_ERROR_STOP 0
|
||||
ALTER TABLE referrer2 ADD CONSTRAINT hyper_fk_device_id_fkey
|
||||
FOREIGN KEY (time) REFERENCES hyper_for_ref(time);
|
||||
\set ON_ERROR_STOP 1
|
||||
|
||||
----------------------- EXCLUSION CONSTRAINT ------------------
|
||||
|
||||
CREATE TABLE hyper_ex (
|
||||
time BIGINT,
|
||||
device_id TEXT NOT NULL REFERENCES devices(device_id),
|
||||
sensor_1 NUMERIC NULL DEFAULT 1 CHECK (sensor_1 > 10),
|
||||
canceled boolean DEFAULT false,
|
||||
EXCLUDE USING btree (
|
||||
time WITH =, device_id WITH =
|
||||
) WHERE (not canceled)
|
||||
);
|
||||
|
||||
SELECT * FROM create_hypertable('hyper_ex', 'time', chunk_time_interval=>_timescaledb_internal.interval_to_usec('1 month'));
|
||||
|
||||
INSERT INTO hyper_ex(time, device_id,sensor_1) VALUES
|
||||
(1257987700000000000, 'dev2', 11);
|
||||
|
||||
\set ON_ERROR_STOP 0
|
||||
INSERT INTO hyper_ex(time, device_id,sensor_1) VALUES
|
||||
(1257987700000000000, 'dev2', 12);
|
||||
\set ON_ERROR_STOP 1
|
||||
|
||||
ALTER TABLE hyper_ex DROP CONSTRAINT hyper_ex_time_device_id_excl;
|
||||
|
||||
--can now add
|
||||
INSERT INTO hyper_ex(time, device_id,sensor_1) VALUES
|
||||
(1257987700000000000, 'dev2', 12);
|
||||
|
||||
--cannot add because of conflicts
|
||||
\set ON_ERROR_STOP 0
|
||||
ALTER TABLE hyper_ex ADD CONSTRAINT hyper_ex_time_device_id_excl
|
||||
EXCLUDE USING btree (
|
||||
time WITH =, device_id WITH =
|
||||
) WHERE (not canceled)
|
||||
;
|
||||
\set ON_ERROR_STOP 1
|
||||
|
||||
DELETE FROM hyper_ex WHERE sensor_1 = 12;
|
||||
|
||||
ALTER TABLE hyper_ex ADD CONSTRAINT hyper_ex_time_device_id_excl
|
||||
EXCLUDE USING btree (
|
||||
time WITH =, device_id WITH =
|
||||
) WHERE (not canceled)
|
||||
;
|
||||
|
||||
\set ON_ERROR_STOP 0
|
||||
INSERT INTO hyper_ex(time, device_id,sensor_1) VALUES
|
||||
(1257987700000000000, 'dev2', 12);
|
||||
\set ON_ERROR_STOP 1
|
||||
|
||||
|
||||
--cannot add exclusion constraint without partition key.
|
||||
CREATE TABLE hyper_ex_invalid (
|
||||
time BIGINT,
|
||||
device_id TEXT NOT NULL REFERENCES devices(device_id),
|
||||
sensor_1 NUMERIC NULL DEFAULT 1 CHECK (sensor_1 > 10),
|
||||
canceled boolean DEFAULT false,
|
||||
EXCLUDE USING btree (
|
||||
device_id WITH =
|
||||
) WHERE (not canceled)
|
||||
);
|
||||
|
||||
\set ON_ERROR_STOP 0
|
||||
SELECT * FROM create_hypertable('hyper_ex_invalid', 'time', chunk_time_interval=>_timescaledb_internal.interval_to_usec('1 month'));
|
||||
\set ON_ERROR_STOP 1
|
@ -31,7 +31,7 @@ SELECT * FROM create_hypertable('"public"."Hypertable_1"', 'time', 'Device_id',
|
||||
SELECT * FROM create_hypertable('"customSchema"."Hypertable_1"', 'time', NULL, 1, chunk_time_interval=>_timescaledb_internal.interval_to_usec('1 month'));
|
||||
|
||||
SELECT * FROM _timescaledb_catalog.hypertable;
|
||||
SELECT * FROM _timescaledb_catalog.hypertable_index;
|
||||
SELECT * FROM _timescaledb_catalog.hypertable_index ORDER BY format('%I.%I', main_schema_name, main_index_name)::regclass;
|
||||
|
||||
CREATE INDEX ON PUBLIC."Hypertable_1" (time, "temp_c");
|
||||
CREATE INDEX "ind_humidity" ON PUBLIC."Hypertable_1" (time, "humidity");
|
||||
@ -49,7 +49,7 @@ VALUES(1257894000000000000, 'dev1', 30, 70, 1, 2, 3, 100);
|
||||
INSERT INTO "customSchema"."Hypertable_1"(time, "Device_id", temp_c, humidity, sensor_1, sensor_2, sensor_3, sensor_4)
|
||||
VALUES(1257894000000000001, 'dev1', 30, 70, 1, 2, 3, 100);
|
||||
|
||||
SELECT * FROM _timescaledb_catalog.hypertable_index;
|
||||
SELECT * FROM _timescaledb_catalog.hypertable_index ORDER BY format('%I.%I', main_schema_name, main_index_name)::regclass;
|
||||
|
||||
--expect error cases
|
||||
\set ON_ERROR_STOP 0
|
||||
|
@ -9,7 +9,8 @@ CREATE TABLE PUBLIC."two_Partitions" (
|
||||
series_0 DOUBLE PRECISION NULL,
|
||||
series_1 DOUBLE PRECISION NULL,
|
||||
series_2 DOUBLE PRECISION NULL,
|
||||
series_bool BOOLEAN NULL
|
||||
series_bool BOOLEAN NULL,
|
||||
UNIQUE("timeCustom", device_id, series_2)
|
||||
);
|
||||
CREATE INDEX ON PUBLIC."two_Partitions" (device_id, "timeCustom" DESC NULLS LAST) WHERE device_id IS NOT NULL;
|
||||
CREATE INDEX ON PUBLIC."two_Partitions" ("timeCustom" DESC NULLS LAST, series_0) WHERE series_0 IS NOT NULL;
|
||||
@ -21,11 +22,11 @@ CREATE INDEX ON PUBLIC."two_Partitions" ("timeCustom" DESC NULLS LAST, device_id
|
||||
SELECT * FROM create_hypertable('"public"."two_Partitions"'::regclass, 'timeCustom'::name, 'device_id'::name, associated_schema_name=>'_timescaledb_internal'::text, number_partitions => 2, chunk_time_interval=>_timescaledb_internal.interval_to_usec('1 month'));
|
||||
|
||||
|
||||
INSERT INTO public."two_Partitions"("timeCustom", device_id, series_0, series_1) VALUES
|
||||
(1257987600000000000, 'dev1', 1.5, 1),
|
||||
(1257987600000000000, 'dev1', 1.5, 2),
|
||||
(1257894000000000000, 'dev2', 1.5, 1),
|
||||
(1257894002000000000, 'dev1', 2.5, 3);
|
||||
INSERT INTO public."two_Partitions"("timeCustom", device_id, series_0, series_1, series_2) VALUES
|
||||
(1257987600000000000, 'dev1', 1.5, 1, 1),
|
||||
(1257987600000000000, 'dev1', 1.5, 2, 2),
|
||||
(1257894000000000000, 'dev2', 1.5, 1, 3),
|
||||
(1257894002000000000, 'dev1', 2.5, 3, 4);
|
||||
|
||||
INSERT INTO "two_Partitions"("timeCustom", device_id, series_0, series_1) VALUES
|
||||
(1257894000000000000, 'dev2', 1.5, 2);
|
||||
INSERT INTO "two_Partitions"("timeCustom", device_id, series_0, series_1, series_2) VALUES
|
||||
(1257894000000000000, 'dev2', 1.5, 2, 6);
|
||||
|
@ -1,6 +1,7 @@
|
||||
\d+ _timescaledb_catalog.*;
|
||||
\df+ _timescaledb_internal.*;
|
||||
\dy
|
||||
\d+ PUBLIC.*
|
||||
|
||||
SELECT count(*)
|
||||
FROM pg_depend
|
||||
@ -9,3 +10,9 @@ SELECT count(*)
|
||||
|
||||
-- The list of tables configured to be dumped.
|
||||
SELECT unnest(extconfig)::regclass::text c from pg_extension where extname='timescaledb' ORDER BY c;
|
||||
|
||||
SELECT * FROM _timescaledb_catalog.chunk_constraint ORDER BY chunk_id, dimension_slice_id, constraint_name;
|
||||
SELECT * FROM _timescaledb_catalog.hypertable_index ORDER BY hypertable_id, main_index_name;
|
||||
SELECT schema_name, table_name, main_schema_name, main_index_name FROM _timescaledb_catalog.chunk_index ORDER BY schema_name, table_name, main_schema_name, main_index_name;
|
||||
|
||||
SELECT * FROM public."two_Partitions";
|
||||
|
Loading…
x
Reference in New Issue
Block a user