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:
Matvey Arye 2017-08-16 14:47:32 -04:00 committed by Matvey Arye
parent 3b2afb6e9a
commit 4dcbe6114d
36 changed files with 1523 additions and 232 deletions

View File

@ -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 \

View File

@ -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

View File

@ -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
View 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$;

View File

@ -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,

View File

@ -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');

View File

@ -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$;

View File

@ -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

View File

@ -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.

View File

@ -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;

View File

@ -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 (

View File

@ -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
);

View File

@ -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', '');

View File

@ -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

View File

@ -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))

View File

@ -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)
{

View File

@ -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);

View File

@ -18,6 +18,12 @@ 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)
{
@ -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);
}

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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 */

View File

@ -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:

View File

@ -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;

View File

@ -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));
}

View File

@ -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)

View 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

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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"

View File

@ -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

View File

@ -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
View 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

View File

@ -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

View File

@ -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);

View File

@ -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";