timescaledb/sql/util_internal_table_ddl.sql
Matvey Arye 97681c2328 Fixes permission handling
Previously, catalog tables were not fully protected from malicious
non-superusers. This PR fixes permission handling be severely
restricting permissions to the catalog and instead using SECURITY
DEFINER functions to alter the catalog when needed without giving
users permission to do those same operations outside of these functions.
In addition, these functions check for proper permissions themselves
so are safe to use.

This PR also makes sure that chunk tables have the same owner as the
hypertable and correctly handles `ALTER TABLE...OWNER TO` commands to
keep this info in sync.
2017-06-27 11:20:41 -04:00

145 lines
4.5 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
)
RETURNS VOID LANGUAGE PLPGSQL VOLATILE AS
$BODY$
DECLARE
chunk_row _timescaledb_catalog.chunk;
hypertable_row _timescaledb_catalog.hypertable;
tablespace_clause TEXT := '';
tablespace_name NAME;
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;
tablespace_name := _timescaledb_internal.select_tablespace(chunk_row.hypertable_id,
chunk_row.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
);
PERFORM _timescaledb_internal.chunk_add_constraints(chunk_id);
END
$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;
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
return format(
$$
%1$I.%2$s(%3$I::text) >= %4$L AND %1$I.%2$s(%3$I::text) < %5$L
$$,
dimension_row.partitioning_func_schema, dimension_row.partitioning_func,
dimension_row.column_name, dimension_slice_row.range_start, dimension_slice_row.range_end);
ELSE
--TODO: only works with time for now
return format(
$$
%1$I >= %2$s AND %1$I < %3$s
$$,
dimension_row.column_name,
_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));
END IF;
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$;