mirror of
https://github.com/timescale/timescaledb.git
synced 2025-05-17 02:53:51 +08:00
Functions marked IMMUTABLE should also be parallel safe, but aren't by default. This change marks all immutable functions as parallel safe and removes the IMMUTABLE definitions on some functions that have been wrongly labeled as IMMUTABLE. If functions that are IMMUTABLE does not have the PARALLEL SAFE label, then some standard PostgreSQL regression tests will fail (this is true for PostgreSQL >= 10).
253 lines
8.1 KiB
PL/PgSQL
253 lines
8.1 KiB
PL/PgSQL
-- 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;
|