-- This file contains functions associated with creating new -- hypertables. -- Creates a new schema if it does not exist. CREATE OR REPLACE FUNCTION _timescaledb_internal.create_schema( schema_name NAME ) RETURNS VOID LANGUAGE PLPGSQL VOLATILE AS $BODY$ BEGIN EXECUTE format( $$ CREATE SCHEMA IF NOT EXISTS %I $$, schema_name); END $BODY$ SET client_min_messages = WARNING -- suppress NOTICE on IF EXISTS ; CREATE OR REPLACE FUNCTION _timescaledb_internal.chunk_create_table( chunk_id INT, tablespace_name NAME ) RETURNS VOID LANGUAGE PLPGSQL VOLATILE AS $BODY$ DECLARE chunk_row _timescaledb_catalog.chunk; hypertable_row _timescaledb_catalog.hypertable; tablespace_clause TEXT := ''; table_owner NAME; tablespace_oid OID; BEGIN SELECT * INTO STRICT chunk_row FROM _timescaledb_catalog.chunk WHERE id = chunk_id; SELECT * INTO STRICT hypertable_row FROM _timescaledb_catalog.hypertable WHERE id = chunk_row.hypertable_id; SELECT t.oid INTO tablespace_oid FROM pg_catalog.pg_tablespace t WHERE t.spcname = tablespace_name; SELECT tableowner INTO STRICT table_owner FROM pg_catalog.pg_tables WHERE schemaname = hypertable_row.schema_name AND tablename = hypertable_row.table_name; IF tablespace_oid IS NOT NULL THEN tablespace_clause := format('TABLESPACE %s', tablespace_name); ELSIF tablespace_name IS NOT NULL THEN RAISE EXCEPTION 'No tablespace % in database %', tablespace_name, current_database() USING ERRCODE = 'IO501'; END IF; EXECUTE format( $$ CREATE TABLE IF NOT EXISTS %1$I.%2$I () INHERITS(%3$I.%4$I) %5$s; $$, chunk_row.schema_name, chunk_row.table_name, hypertable_row.schema_name, hypertable_row.table_name, tablespace_clause ); EXECUTE format( $$ ALTER TABLE %1$I.%2$I OWNER TO %3$I $$, chunk_row.schema_name, chunk_row.table_name, table_owner ); END $BODY$; CREATE OR REPLACE FUNCTION _timescaledb_internal.dimension_is_finite( val BIGINT ) RETURNS BOOLEAN LANGUAGE SQL IMMUTABLE PARALLEL SAFE AS $BODY$ --end values of bigint reserved for infinite SELECT val > (-9223372036854775808)::bigint AND val < 9223372036854775807::bigint $BODY$; CREATE OR REPLACE FUNCTION _timescaledb_internal.dimension_slice_get_constraint_sql( dimension_slice_id INTEGER ) RETURNS TEXT LANGUAGE PLPGSQL VOLATILE AS $BODY$ DECLARE dimension_slice_row _timescaledb_catalog.dimension_slice; dimension_row _timescaledb_catalog.dimension; parts TEXT[]; BEGIN SELECT * INTO STRICT dimension_slice_row FROM _timescaledb_catalog.dimension_slice WHERE id = dimension_slice_id; SELECT * INTO STRICT dimension_row FROM _timescaledb_catalog.dimension WHERE id = dimension_slice_row.dimension_id; IF dimension_row.partitioning_func IS NOT NULL THEN IF _timescaledb_internal.dimension_is_finite(dimension_slice_row.range_start) THEN parts = parts || format( $$ %1$I.%2$I(%3$I) >= %4$L $$, dimension_row.partitioning_func_schema, dimension_row.partitioning_func, dimension_row.column_name, dimension_slice_row.range_start); END IF; IF _timescaledb_internal.dimension_is_finite(dimension_slice_row.range_end) THEN parts = parts || format( $$ %1$I.%2$I(%3$I) < %4$L $$, dimension_row.partitioning_func_schema, dimension_row.partitioning_func, dimension_row.column_name, dimension_slice_row.range_end); END IF; return array_to_string(parts, 'AND'); ELSE --TODO: only works with time for now IF _timescaledb_internal.time_literal_sql(dimension_slice_row.range_start, dimension_row.column_type) = _timescaledb_internal.time_literal_sql(dimension_slice_row.range_end, dimension_row.column_type) THEN RAISE 'Time based constraints have the same start and end values for column "%": %', dimension_row.column_name, _timescaledb_internal.time_literal_sql(dimension_slice_row.range_end, dimension_row.column_type); END IF; parts = ARRAY[]::text[]; IF _timescaledb_internal.dimension_is_finite(dimension_slice_row.range_start) THEN parts = parts || format( $$ %1$I >= %2$s $$, dimension_row.column_name, _timescaledb_internal.time_literal_sql(dimension_slice_row.range_start, dimension_row.column_type)); END IF; IF _timescaledb_internal.dimension_is_finite(dimension_slice_row.range_end) THEN parts = parts || format( $$ %1$I < %2$s $$, dimension_row.column_name, _timescaledb_internal.time_literal_sql(dimension_slice_row.range_end, dimension_row.column_type)); END IF; return array_to_string(parts, 'AND'); END IF; END $BODY$; -- Outputs the create_hypertable command to recreate the given hypertable. -- -- This is currently used internally for our single hypertable backup tool -- so that it knows how to restore the hypertable without user intervention. -- -- It only works for hypertables with up to 2 dimensions. CREATE OR REPLACE FUNCTION _timescaledb_internal.get_create_command( table_name NAME ) RETURNS TEXT LANGUAGE PLPGSQL VOLATILE AS $BODY$ DECLARE h_id INTEGER; schema_name NAME; time_column NAME; time_interval BIGINT; space_column NAME; space_partitions INTEGER; dimension_cnt INTEGER; dimension_row record; ret TEXT; BEGIN SELECT h.id, h.schema_name FROM _timescaledb_catalog.hypertable AS h WHERE h.table_name = get_create_command.table_name INTO h_id, schema_name; IF h_id IS NULL THEN RAISE EXCEPTION 'hypertable % not found', table_name USING ERRCODE = 'IO101'; END IF; SELECT COUNT(*) FROM _timescaledb_catalog.dimension d WHERE d.hypertable_id = h_id INTO STRICT dimension_cnt; IF dimension_cnt > 2 THEN RAISE EXCEPTION 'get_create_command only supports hypertables with up to 2 dimensions' USING ERRCODE = 'IO101'; END IF; FOR dimension_row IN SELECT * FROM _timescaledb_catalog.dimension d WHERE d.hypertable_id = h_id LOOP IF dimension_row.interval_length IS NOT NULL THEN time_column := dimension_row.column_name; time_interval := dimension_row.interval_length; ELSIF dimension_row.num_slices IS NOT NULL THEN space_column := dimension_row.column_name; space_partitions := dimension_row.num_slices; END IF; END LOOP; ret := format($$SELECT create_hypertable('%I.%I', '%I'$$, schema_name, table_name, time_column); IF space_column IS NOT NULL THEN ret := ret || format($$, '%I', %s$$, space_column, space_partitions); END IF; ret := ret || format($$, chunk_time_interval => %s, create_default_indexes=>FALSE);$$, time_interval); RETURN ret; END $BODY$; -- Used to make sure all time dimension columns are set as NOT NULL. CREATE OR REPLACE FUNCTION _timescaledb_internal.set_time_columns_not_null() RETURNS VOID LANGUAGE PLPGSQL VOLATILE AS $BODY$ DECLARE ht_time_column RECORD; BEGIN FOR ht_time_column IN SELECT ht.schema_name, ht.table_name, d.column_name FROM _timescaledb_catalog.hypertable ht, _timescaledb_catalog.dimension d WHERE ht.id = d.hypertable_id AND d.partitioning_func IS NULL LOOP EXECUTE format( $$ ALTER TABLE %I.%I ALTER %I SET NOT NULL $$, ht_time_column.schema_name, ht_time_column.table_name, ht_time_column.column_name); END LOOP; END $BODY$; CREATE OR REPLACE FUNCTION _timescaledb_internal.validate_triggers(main_table REGCLASS) RETURNS VOID AS '$libdir/timescaledb', 'hypertable_validate_triggers' LANGUAGE C STRICT;