timescaledb/sql/common/tables.sql
Matvey Arye 67ad21ee36 Cache hypertable metadata information for faster inserts.
Add caches for hypertable metadata to make it faster to map
INSERT rows to the chunk they should go into.
2017-03-02 12:24:53 +01:00

256 lines
15 KiB
SQL

-- This file contains table definitions for various abstractions and data
-- structures for representing hypertables and lower level concepts.
-- Data node information for the cluster. database_name is the postgres database
-- located on at hostname. server_name is used to identify the connection.
-- schema_name is the name of the schema used to represent the node on the meta
-- node (it stores remote wrappers to update meta tables on data nodes).
CREATE TABLE IF NOT EXISTS _iobeamdb_catalog.node (
database_name NAME NOT NULL PRIMARY KEY,
schema_name NAME NOT NULL UNIQUE, --the schema name with remote tables to _iobeamdb_catalog schema of the node
server_name NAME NOT NULL UNIQUE,
hostname TEXT NOT NULL,
port INT NOT NULL CONSTRAINT valid_port CHECK (port >= 0 AND port <= 65535),
active BOOLEAN NOT NULL DEFAULT TRUE,
id SERIAL NOT NULL UNIQUE -- id for node. used in naming
);
SELECT pg_catalog.pg_extension_config_dump('_iobeamdb_catalog.node', '');
SELECT pg_catalog.pg_extension_config_dump(pg_get_serial_sequence('_iobeamdb_catalog.node','id'), '');
-- Singleton (i.e. should only contain one row) holding info about meta db.
CREATE TABLE IF NOT EXISTS _iobeamdb_catalog.meta (
database_name NAME NOT NULL PRIMARY KEY,
hostname TEXT NOT NULL,
port INT NOT NULL,
server_name NAME NOT NULL
);
SELECT pg_catalog.pg_extension_config_dump('_iobeamdb_catalog.meta', '');
CREATE UNIQUE INDEX there_can_be_only_one_meta
ON _iobeamdb_catalog.meta ((1));
-- Users should exist an all nodes+meta in the cluster.
CREATE TABLE IF NOT EXISTS _iobeamdb_catalog.cluster_user (
username TEXT NOT NULL PRIMARY KEY,
password TEXT NULL --not any more of a security hole than usual since stored in pg_user_mapping anyway
);
SELECT pg_catalog.pg_extension_config_dump('_iobeamdb_catalog.cluster_user', '');
-- The hypertable is an abstraction that represents a replicated table that is
-- partitioned on 2 dimensions: time and another (user-)chosen one.
--
-- Each row, representing a hypertable, creates 3 tables:
-- 1) main table - an alias to the 0'th replica for now. Represents the
-- hypertable to the user for insertion and modification.
-- 2) root table - ancesstor of all the data tables (across replicas).
-- Should not be queryable for data (TODO).
--
-- Additionally, a schema for associated tables (partitioned, replicated data
-- tables) is created.
--
-- The name and type of the time column (used to partition on time) are defined
-- in `time_column_name` and `time_column_type`.
CREATE TABLE IF NOT EXISTS _iobeamdb_catalog.hypertable (
id SERIAL PRIMARY KEY,
schema_name NAME NOT NULL CHECK (schema_name != '_iobeamdb_catalog'),
table_name NAME NOT NULL,
associated_schema_name NAME NOT NULL,
associated_table_prefix NAME NOT NULL,
root_schema_name NAME NOT NULL,
root_table_name NAME NOT NULL,
replication_factor SMALLINT NOT NULL CHECK (replication_factor > 0),
placement _iobeamdb_catalog.chunk_placement_type NOT NULL,
time_column_name NAME NOT NULL,
time_column_type REGTYPE NOT NULL,
created_on NAME NOT NULL REFERENCES _iobeamdb_catalog.node(database_name),
chunk_size_bytes BIGINT NOT NULL CHECK (chunk_size_bytes > 0),
UNIQUE (schema_name, table_name),
UNIQUE (associated_schema_name, associated_table_prefix),
UNIQUE (root_schema_name, root_table_name)
);
SELECT pg_catalog.pg_extension_config_dump('_iobeamdb_catalog.hypertable', '');
SELECT pg_catalog.pg_extension_config_dump(pg_get_serial_sequence('_iobeamdb_catalog.hypertable','id'), '');
-- deleted_hypertable is used to avoid deadlocks when doing multinode drops.
CREATE TABLE IF NOT EXISTS _iobeamdb_catalog.deleted_hypertable (
LIKE _iobeamdb_catalog.hypertable,
deleted_on NAME
);
SELECT pg_catalog.pg_extension_config_dump('_iobeamdb_catalog.deleted_hypertable', '');
-- hypertable_replica contains information on how a hypertable's data replicas
-- are stored. A replica of the data is across all partitions and time.
--
-- Each row identifies a table for each hypertable + replica_id combination:
-- - data replica table (schema_name.table_name) -
-- All the data for a hypertable.
-- Parent: hypertable's `root table`
-- Children: hypertable's `partition_replica` tables
CREATE TABLE IF NOT EXISTS _iobeamdb_catalog.hypertable_replica (
hypertable_id INTEGER NOT NULL REFERENCES _iobeamdb_catalog.hypertable(id) ON DELETE CASCADE,
replica_id SMALLINT NOT NULL CHECK (replica_id >= 0),
schema_name NAME NOT NULL,
table_name NAME NOT NULL,
PRIMARY KEY (hypertable_id, replica_id),
UNIQUE (schema_name, table_name)
);
SELECT pg_catalog.pg_extension_config_dump('_iobeamdb_catalog.hypertable_replica', '');
-- Mapping that shows which replica is pointed to by the main table on
-- each node. The translation from main table to replica should happen
-- in C tranformation right after the parsing step.
-- (Postgres RULES cannot be used, unfortunately)
CREATE TABLE IF NOT EXISTS _iobeamdb_catalog.default_replica_node (
database_name NAME NOT NULL REFERENCES _iobeamdb_catalog.node(database_name),
hypertable_id INTEGER NOT NULL REFERENCES _iobeamdb_catalog.hypertable(id) ON DELETE CASCADE,
replica_id SMALLINT NOT NULL CHECK (replica_id >= 0),
PRIMARY KEY (database_name, hypertable_id),
FOREIGN KEY (hypertable_id, replica_id) REFERENCES _iobeamdb_catalog.hypertable_replica(hypertable_id, replica_id)
);
SELECT pg_catalog.pg_extension_config_dump('_iobeamdb_catalog.default_replica_node', '');
-- A partition_epoch represents a different partitioning of the data.
-- It has a start and end time (data time). Data needs to be placed in the correct epoch by time.
-- Partitionings are defined by a function, column, and modulo:
-- 1) partitioning_func - Takes the partitioning_column and returns a number
-- which is modulo'd to place the data correctly
-- 2) partitioning_mod - Number used in modulo operation
-- 3) partitioning_column - column in data to partition by (input to partitioning_func)
--
-- Changing a data's partitioning, and thus creating a new epoch, should be done
-- INFREQUENTLY as it's expensive operation.
CREATE TABLE IF NOT EXISTS _iobeamdb_catalog.partition_epoch (
id SERIAL NOT NULL PRIMARY KEY,
hypertable_id INTEGER NOT NULL REFERENCES _iobeamdb_catalog.hypertable(id) ON DELETE CASCADE,
start_time BIGINT NULL CHECK (start_time >= 0),
end_time BIGINT NULL CHECK (end_time >= 0),
num_partitions SMALLINT NOT NULL CHECK (num_partitions >= 0),
partitioning_func_schema NAME NULL,
partitioning_func NAME NULL, --function name of a function of the form func(data_value, partitioning_mod) -> [0, partitioning_mod)
partitioning_mod INT NOT NULL CHECK (partitioning_mod < 65536),
partitioning_column NAME NULL,
UNIQUE (hypertable_id, start_time),
UNIQUE (hypertable_id, end_time),
CHECK (start_time <= end_time),
CHECK (num_partitions <= partitioning_mod),
CHECK ((partitioning_func_schema IS NULL AND partitioning_func IS NULL) OR (partitioning_func_schema IS NOT NULL AND partitioning_func IS NOT NULL))
);
CREATE INDEX ON _iobeamdb_catalog.partition_epoch(hypertable_id, start_time, end_time);
SELECT pg_catalog.pg_extension_config_dump('_iobeamdb_catalog.partition_epoch', '');
SELECT pg_catalog.pg_extension_config_dump(pg_get_serial_sequence('_iobeamdb_catalog.partition_epoch','id'), '');
-- A partition defines a partition witin a partition_epoch.
-- For any partition the keyspace is defined as [keyspace_start, keyspace_end].
-- For any epoch, there must be a partition that covers every element in the
-- keyspace, i.e. from [0, partition_epoch.partitioning_mod].
CREATE TABLE IF NOT EXISTS _iobeamdb_catalog.partition (
id SERIAL NOT NULL PRIMARY KEY,
epoch_id INT NOT NULL REFERENCES _iobeamdb_catalog.partition_epoch (id) ON DELETE CASCADE,
keyspace_start SMALLINT NOT NULL CHECK (keyspace_start >= 0), --start inclusive
keyspace_end SMALLINT NOT NULL CHECK (keyspace_end > 0), --end inclusive; compatible with between operator
tablespace NAME NULL,
UNIQUE (epoch_id, keyspace_start),
CHECK (keyspace_end > keyspace_start)
);
SELECT pg_catalog.pg_extension_config_dump('_iobeamdb_catalog.partition', '');
SELECT pg_catalog.pg_extension_config_dump(pg_get_serial_sequence('_iobeamdb_catalog.partition','id'), '');
--Represents a replica for a partition.
--Each row creates a table:
-- Parent: "hypertable_replica.schema_name"."hypertable_replica.table_name"
-- Children: "chunk_replica_node.schema_name"."chunk_replica_node.table_name"
--TODO: trigger to verify partition_epoch hypertable id matches this hypertable_id
CREATE TABLE IF NOT EXISTS _iobeamdb_catalog.partition_replica (
id SERIAL NOT NULL PRIMARY KEY,
partition_id INTEGER NOT NULL REFERENCES _iobeamdb_catalog.partition(id) ON DELETE CASCADE,
hypertable_id INTEGER NOT NULL,
replica_id SMALLINT NOT NULL,
schema_name NAME NOT NULL,
table_name NAME NOT NULL,
UNIQUE (schema_name, table_name),
UNIQUE (partition_id, replica_id),
FOREIGN KEY (hypertable_id, replica_id) REFERENCES _iobeamdb_catalog.hypertable_replica(hypertable_id, replica_id) ON DELETE CASCADE
);
SELECT pg_catalog.pg_extension_config_dump('_iobeamdb_catalog.partition_replica', '');
SELECT pg_catalog.pg_extension_config_dump(pg_get_serial_sequence('_iobeamdb_catalog.partition_replica','id'), '');
-- Represent a (replicated) chunk of data, which is data in a hypertable that is
-- both partitioned by both the partition_column and time.
--
-- For each partition, there can be 0 or more chunks, which are replicated.
-- At most two chunks per partition are "open-ended", i.e. having a NULL
-- start_time or a NULL end_time. A NULL start_time means the chunk has
-- data from the beginning of time until end_time. A NULL end_time means the
-- chunk has data from start_time until the end of time. Only when there is
-- one chunk for a partition can it be open-ended on BOTH start_time and end_time.
--
-- TODO(erik) - Describe conditions of closure.
CREATE TABLE IF NOT EXISTS _iobeamdb_catalog.chunk (
id SERIAL NOT NULL PRIMARY KEY,
partition_id INT NOT NULL REFERENCES _iobeamdb_catalog.partition (id) ON DELETE CASCADE,
start_time BIGINT NULL CHECK (start_time >= 0),
end_time BIGINT NULL CHECK (end_time >= 0),
UNIQUE (partition_id, start_time),
UNIQUE (partition_id, end_time),
CHECK (start_time <= end_time)
);
SELECT pg_catalog.pg_extension_config_dump('_iobeamdb_catalog.chunk', '');
SELECT pg_catalog.pg_extension_config_dump(pg_get_serial_sequence('_iobeamdb_catalog.chunk','id'), '');
-- A mapping between chunks, partition_replica, and nodes representing where
-- actual data is stored. That is, a chunk_replica_node is a particular
-- replication instance of a chunk.
--
-- Each row represents a table:
-- Parent table: "partition_replica.schema_name"."partition_replica.table_name"
CREATE TABLE IF NOT EXISTS _iobeamdb_catalog.chunk_replica_node (
chunk_id INT NOT NULL REFERENCES _iobeamdb_catalog.chunk(id) ON DELETE CASCADE,
partition_replica_id INT NOT NULL REFERENCES _iobeamdb_catalog.partition_replica(id) ON DELETE CASCADE,
database_name NAME NOT NULL REFERENCES _iobeamdb_catalog.node(database_name),
schema_name NAME NOT NULL,
table_name NAME NOT NULL,
PRIMARY KEY (chunk_id, partition_replica_id), --a single chunk, replica tuple
UNIQUE (chunk_id, database_name), --no two chunk replicas on same node
UNIQUE (schema_name, table_name)
);
SELECT pg_catalog.pg_extension_config_dump('_iobeamdb_catalog.chunk_replica_node', '');
-- Represents a hypertable column.
CREATE TABLE IF NOT EXISTS _iobeamdb_catalog.hypertable_column (
hypertable_id INTEGER NOT NULL REFERENCES _iobeamdb_catalog.hypertable(id) ON DELETE CASCADE,
name NAME NOT NULL,
attnum INT2 NOT NULL, --MUST match pg_attribute.attnum on main table. SHOULD match on root/hierarchy table as well.
data_type REGTYPE NOT NULL,
default_value TEXT NULL,
not_null BOOLEAN NOT NULL,
created_on NAME NOT NULL REFERENCES _iobeamdb_catalog.node(database_name),
modified_on NAME NOT NULL REFERENCES _iobeamdb_catalog.node(database_name),
PRIMARY KEY (hypertable_id, name),
UNIQUE(hypertable_id, attnum)
);
SELECT pg_catalog.pg_extension_config_dump('_iobeamdb_catalog.hypertable_column', '');
-- TODO(mat) - Description?
CREATE TABLE IF NOT EXISTS _iobeamdb_catalog.deleted_hypertable_column (
LIKE _iobeamdb_catalog.hypertable_column,
deleted_on NAME
);
SELECT pg_catalog.pg_extension_config_dump('_iobeamdb_catalog.deleted_hypertable_column', '');
CREATE TABLE IF NOT EXISTS _iobeamdb_catalog.hypertable_index (
hypertable_id INTEGER NOT NULL REFERENCES _iobeamdb_catalog.hypertable(id) ON DELETE CASCADE,
main_schema_name NAME NOT NULL, --schema name of main table (needed for a uniqueness constraint)
main_index_name NAME NOT NULL, --index name on main table
definition TEXT NOT NULL, --def with /*INDEX_NAME*/ and /*TABLE_NAME*/ placeholders
created_on NAME NOT NULL REFERENCES _iobeamdb_catalog.node(database_name),
PRIMARY KEY (hypertable_id, main_index_name),
UNIQUE(main_schema_name, main_index_name) --globally unique since index names globally unique
);
SELECT pg_catalog.pg_extension_config_dump('_iobeamdb_catalog.hypertable_index', '');
-- TODO(mat) - Description?
CREATE TABLE IF NOT EXISTS _iobeamdb_catalog.deleted_hypertable_index (
LIKE _iobeamdb_catalog.hypertable_index,
deleted_on NAME
);
SELECT pg_catalog.pg_extension_config_dump('_iobeamdb_catalog.deleted_hypertable_index', '');