timescaledb/sql/size_utils.sql
Brian Rowe 79fb46456f Rename server to data node
The timescale clustering code so far has been written referring to the
remote databases as 'servers'.  This terminology is a bit overloaded,
and in particular we don't enforce any network topology limitations
that the term 'server' would suggest.  In light of this we've decided
to change to use the term 'node' when referring to the different
databases in a distributed database.  Specifically we refer to the
frontend as an 'access node' and to the backends as 'data nodes',
though we may omit the access or data qualifier where it's unambiguous.

As the vast bulk of the code so far has been written for the case where
there was a single access node, almost all instances of 'server' were
references to data nodes.  This change has updated the code to rename
those instances.
2020-05-27 17:31:09 +02:00

595 lines
21 KiB
PL/PgSQL

-- This file and its contents are licensed under the Apache License 2.0.
-- Please see the included NOTICE for copyright information and
-- LICENSE-APACHE for a copy of the license.
-- This file contains utility functions to get the relation size
-- of hypertables, chunks, and indexes on hypertables.
CREATE OR REPLACE FUNCTION _timescaledb_internal.hypertable_relation_local_size(
schema_name_in name,
table_name_in name)
RETURNS TABLE (
table_bytes bigint,
index_bytes bigint,
toast_bytes bigint,
total_bytes bigint)
LANGUAGE PLPGSQL STABLE STRICT AS
$BODY$
BEGIN
RETURN QUERY
SELECT
sub2.table_bytes,
sub2.index_bytes,
sub2.toast_bytes,
sub2.total_bytes
FROM (
SELECT
*,
sub1.total_bytes - sub1.index_bytes - COALESCE(sub1.toast_bytes, 0) AS table_bytes
FROM (
SELECT
sum(pg_total_relation_size(format('%I.%I', c.schema_name, c.table_name)))::bigint AS total_bytes,
sum(pg_indexes_size(format('%I.%I', c.schema_name, c.table_name)))::bigint AS index_bytes,
sum(pg_total_relation_size(reltoastrelid))::bigint AS toast_bytes
FROM
_timescaledb_catalog.hypertable h,
_timescaledb_catalog.chunk c,
pg_class pgc,
pg_namespace pns
WHERE
h.schema_name = schema_name_in
AND h.table_name = table_name_in
AND c.dropped = FALSE
AND c.hypertable_id = h.id
AND pgc.relname = h.table_name
AND pns.oid = pgc.relnamespace
AND pns.nspname = h.schema_name
AND relkind = 'r'
) sub1
) sub2;
END;
$BODY$;
CREATE OR REPLACE FUNCTION _timescaledb_internal.hypertable_relation_remote_size(
schema_name_in name,
table_name_in name)
RETURNS TABLE (
table_bytes bigint,
index_bytes bigint,
toast_bytes bigint,
total_bytes bigint)
LANGUAGE PLPGSQL STABLE STRICT AS
$BODY$
BEGIN
RETURN QUERY
SELECT
sum(entry.table_bytes)::bigint AS table_bytes,
sum(entry.index_bytes)::bigint AS index_bytes,
sum(entry.toast_bytes)::bigint AS toast_bytes,
sum(entry.total_bytes)::bigint AS total_bytes
FROM (
SELECT
s.node_name,
_timescaledb_internal.ping_data_node (node_name) AS node_up
FROM
_timescaledb_catalog.hypertable AS ht,
_timescaledb_catalog.hypertable_data_node AS s
WHERE
ht.schema_name = schema_name_in
AND ht.table_name = table_name_in
AND s.hypertable_id = ht.id
) AS srv
LEFT OUTER JOIN LATERAL @extschema@.data_node_hypertable_info(
CASE WHEN srv.node_up THEN
srv.node_name
ELSE
NULL
END) entry ON TRUE
WHERE
entry.table_schema = schema_name_in
AND entry.table_name = table_name_in;
END;
$BODY$;
-- Get relation size of hypertable
-- like pg_relation_size(hypertable)
-- (https://www.postgresql.org/docs/9.6/static/functions-admin.html#FUNCTIONS-ADMIN-DBSIZE)
--
-- main_table - hypertable to get size of
--
-- Returns:
-- table_bytes - Disk space used by main_table (like pg_relation_size(main_table))
-- index_bytes - Disk space used by indexes
-- toast_bytes - Disk space of toast tables
-- total_bytes - Total disk space used by the specified table, including all indexes and TOAST data
CREATE OR REPLACE FUNCTION hypertable_relation_size(
main_table REGCLASS
)
RETURNS TABLE (table_bytes BIGINT,
index_bytes BIGINT,
toast_bytes BIGINT,
total_bytes BIGINT
) LANGUAGE PLPGSQL STABLE STRICT
AS
$BODY$
DECLARE
table_name NAME;
schema_name NAME;
is_distributed BOOL;
BEGIN
SELECT relname, nspname, replication_factor > 0
INTO STRICT table_name, schema_name, is_distributed
FROM pg_class c
INNER JOIN pg_namespace n ON (n.OID = c.relnamespace)
INNER JOIN _timescaledb_catalog.hypertable ht ON (ht.schema_name = n.nspname AND ht.table_name = c.relname)
WHERE c.OID = main_table;
CASE WHEN is_distributed THEN
RETURN QUERY SELECT * FROM _timescaledb_internal.hypertable_relation_remote_size(schema_name, table_name);
ELSE
RETURN QUERY SELECT * FROM _timescaledb_internal.hypertable_relation_local_size(schema_name, table_name);
END CASE;
END;
$BODY$;
CREATE OR REPLACE FUNCTION _timescaledb_internal.range_value_to_pretty(
time_value BIGINT,
column_type REGTYPE
)
RETURNS TEXT LANGUAGE PLPGSQL STABLE AS
$BODY$
DECLARE
BEGIN
IF NOT _timescaledb_internal.dimension_is_finite(time_value) THEN
RETURN '';
END IF;
IF time_value IS NULL THEN
RETURN format('%L', NULL);
END IF;
CASE column_type
WHEN 'BIGINT'::regtype, 'INTEGER'::regtype, 'SMALLINT'::regtype THEN
RETURN format('%L', time_value); -- scale determined by user.
WHEN 'TIMESTAMP'::regtype, 'TIMESTAMPTZ'::regtype THEN
-- assume time_value is in microsec
RETURN format('%1$L', _timescaledb_internal.to_timestamp(time_value)); -- microseconds
WHEN 'DATE'::regtype THEN
RETURN format('%L', timezone('UTC',_timescaledb_internal.to_timestamp(time_value))::date);
ELSE
RETURN time_value;
END CASE;
END
$BODY$;
CREATE OR REPLACE FUNCTION _timescaledb_internal.partitioning_column_to_pretty(
d _timescaledb_catalog.dimension
)
RETURNS TEXT LANGUAGE PLPGSQL STABLE STRICT AS
$BODY$
DECLARE
BEGIN
IF d.partitioning_func IS NULL THEN
RETURN d.column_name;
ELSE
RETURN format('%I.%I(%I)', d.partitioning_func_schema, d.partitioning_func, d.column_name);
END IF;
END
$BODY$;
-- Get relation size of hypertable
-- like pg_relation_size(hypertable)
-- (https://www.postgresql.org/docs/9.6/static/functions-admin.html#FUNCTIONS-ADMIN-DBSIZE)
--
-- main_table - hypertable to get size of
--
-- Returns:
-- table_size - Pretty output of table_bytes
-- index_bytes - Pretty output of index_bytes
-- toast_bytes - Pretty output of toast_bytes
-- total_size - Pretty output of total_bytes
CREATE OR REPLACE FUNCTION hypertable_relation_size_pretty(
main_table REGCLASS
)
RETURNS TABLE (table_size TEXT,
index_size TEXT,
toast_size TEXT,
total_size TEXT) LANGUAGE PLPGSQL STABLE STRICT
AS
$BODY$
DECLARE
table_name NAME;
schema_name NAME;
BEGIN
RETURN QUERY
SELECT pg_size_pretty(table_bytes) as table,
pg_size_pretty(index_bytes) as index,
pg_size_pretty(toast_bytes) as toast,
pg_size_pretty(total_bytes) as total
FROM @extschema@.hypertable_relation_size(main_table);
END;
$BODY$;
-- Get the per-node relation size of a distributed hypertable across all nodes in a distributed database
-- like pg_relation_size(hypertable)
-- (https://www.postgresql.org/docs/current/functions-admin.html#FUNCTIONS-ADMIN-DBSIZE)
--
-- main_table - hypertable to get size of
--
-- Returns:
-- node_name - Data node hosting part of the table
-- num_chunks - Number of chunks hosted on the data node
-- table_size - Pretty output of table_bytes for the data node
-- index_bytes - Pretty output of index_bytes for the data node
-- toast_bytes - Pretty output of toast_bytes for the data node
-- total_size - Pretty output of total_bytes for the data node
CREATE OR REPLACE FUNCTION hypertable_data_node_relation_size(
main_table REGCLASS
)
RETURNS TABLE (node_name NAME,
num_chunks BIGINT,
table_size TEXT,
index_size TEXT,
toast_size TEXT,
total_size TEXT) LANGUAGE PLPGSQL STABLE STRICT
AS
$BODY$
DECLARE
local_table_name NAME;
local_schema_name NAME;
BEGIN
SELECT relname, nspname
INTO STRICT local_table_name, local_schema_name
FROM pg_class c
INNER JOIN pg_namespace n ON (n.OID = c.relnamespace)
WHERE c.OID = main_table;
IF NOT (SELECT distributed FROM timescaledb_information.hypertable ht WHERE ht.table_name = local_table_name AND ht.table_schema = local_schema_name)
THEN RAISE NOTICE 'calling hypertable_data_node_relation_size on a non-distributed hypertable';
END IF;
RETURN QUERY EXECUTE format(
$$
SELECT s.node_name as node,
size.num_chunks as chunks,
pg_size_pretty(table_bytes) as table,
pg_size_pretty(index_bytes) as index,
pg_size_pretty(toast_bytes) as toast,
pg_size_pretty(total_bytes) as total
FROM timescaledb_information.data_node s
LEFT OUTER JOIN LATERAL @extschema@.data_node_hypertable_info(s.node_name) size ON TRUE
WHERE size.table_schema = %L
AND size.table_name = %L;
$$,
local_schema_name, local_table_name);
END;
$BODY$;
-- Get relation size of the chunks of an hypertable
-- like pg_relation_size
-- (https://www.postgresql.org/docs/9.6/static/functions-admin.html#FUNCTIONS-ADMIN-DBSIZE)
--
-- main_table - hypertable to get size of
--
-- Returns:
-- chunk_id - Timescaledb id of a chunk
-- chunk_table - Table used for the chunk
-- partitioning_columns - Partitioning column names
-- partitioning_column_types - Type of partitioning columns
-- partitioning_hash_functions - Hash functions of partitioning columns
-- ranges - Partition ranges for each dimension of the chunk
-- table_bytes - Disk space used by main_table
-- index_bytes - Disk space used by indexes
-- toast_bytes - Disk space of toast tables
-- total_bytes - Disk space used in total
CREATE OR REPLACE FUNCTION chunk_relation_size(
main_table REGCLASS
)
RETURNS TABLE (chunk_id INT,
chunk_table TEXT,
partitioning_columns NAME[],
partitioning_column_types REGTYPE[],
partitioning_hash_functions TEXT[],
ranges int8range[],
table_bytes BIGINT,
index_bytes BIGINT,
toast_bytes BIGINT,
total_bytes BIGINT)
LANGUAGE PLPGSQL STABLE STRICT
AS
$BODY$
DECLARE
table_name NAME;
schema_name NAME;
BEGIN
SELECT relname, nspname
INTO STRICT table_name, schema_name
FROM pg_class c
INNER JOIN pg_namespace n ON (n.OID = c.relnamespace)
WHERE c.OID = main_table;
RETURN QUERY EXECUTE format(
$$
SELECT chunk_id,
chunk_table,
partitioning_columns,
partitioning_column_types,
partitioning_hash_functions,
ranges,
table_bytes,
index_bytes,
toast_bytes,
total_bytes
FROM (
SELECT *,
total_bytes-index_bytes-COALESCE(toast_bytes,0) AS table_bytes
FROM (
SELECT c.id as chunk_id,
format('%%I.%%I', c.schema_name, c.table_name) as chunk_table,
pg_total_relation_size(format('%%I.%%I', c.schema_name, c.table_name)) AS total_bytes,
pg_indexes_size(format('%%I.%%I', c.schema_name, c.table_name)) AS index_bytes,
pg_total_relation_size(reltoastrelid) AS toast_bytes,
array_agg(d.column_name ORDER BY d.interval_length, d.column_name ASC) as partitioning_columns,
array_agg(d.column_type ORDER BY d.interval_length, d.column_name ASC) as partitioning_column_types,
array_agg(d.partitioning_func_schema || '.' || d.partitioning_func ORDER BY d.interval_length, d.column_name ASC) as partitioning_hash_functions,
array_agg(int8range(range_start, range_end) ORDER BY d.interval_length, d.column_name ASC) as ranges
FROM
_timescaledb_catalog.hypertable h,
_timescaledb_catalog.chunk c,
_timescaledb_catalog.chunk_constraint cc,
_timescaledb_catalog.dimension d,
_timescaledb_catalog.dimension_slice ds,
pg_class pgc,
pg_namespace pns
WHERE h.schema_name = %L
AND h.table_name = %L
AND pgc.relname = c.table_name
AND pns.oid = pgc.relnamespace
AND pns.nspname = c.schema_name
AND relkind = 'r'
AND c.hypertable_id = h.id
AND c.id = cc.chunk_id
AND cc.dimension_slice_id = ds.id
AND ds.dimension_id = d.id
GROUP BY c.id, pgc.reltoastrelid, pgc.oid ORDER BY c.id
) sub1
) sub2;
$$,
schema_name, table_name);
END;
$BODY$;
-- Get relation size of the chunks of an hypertable
-- like pg_relation_size
-- (https://www.postgresql.org/docs/9.6/static/functions-admin.html#FUNCTIONS-ADMIN-DBSIZE)
--
-- main_table - hypertable to get size of
--
-- Returns:
-- chunk_id - Timescaledb id of a chunk
-- chunk_table - Table used for the chunk
-- partitioning_columns - Partitioning column names
-- partitioning_column_types - Type of partitioning columns
-- partitioning_hash_functions - Hash functions of partitioning columns
-- ranges - Partition ranges for each dimension of the chunk
-- table_size - Pretty output of table_bytes
-- index_size - Pretty output of index_bytes
-- toast_size - Pretty output of toast_bytes
-- total_size - Pretty output of total_bytes
CREATE OR REPLACE FUNCTION chunk_relation_size_pretty(
main_table REGCLASS
)
RETURNS TABLE (chunk_id INT,
chunk_table TEXT,
partitioning_columns NAME[],
partitioning_column_types REGTYPE[],
partitioning_hash_functions TEXT[],
ranges TEXT[],
table_size TEXT,
index_size TEXT,
toast_size TEXT,
total_size TEXT
)
LANGUAGE PLPGSQL STABLE STRICT
AS
$BODY$
DECLARE
table_name NAME;
schema_name NAME;
BEGIN
SELECT relname, nspname
INTO STRICT table_name, schema_name
FROM pg_class c
INNER JOIN pg_namespace n ON (n.OID = c.relnamespace)
WHERE c.OID = main_table;
RETURN QUERY EXECUTE format(
$$
SELECT chunk_id,
chunk_table,
partitioning_columns,
partitioning_column_types,
partitioning_functions,
ranges,
pg_size_pretty(table_bytes) AS table,
pg_size_pretty(index_bytes) AS index,
pg_size_pretty(toast_bytes) AS toast,
pg_size_pretty(total_bytes) AS total
FROM (
SELECT *,
total_bytes-index_bytes-COALESCE(toast_bytes,0) AS table_bytes
FROM (
SELECT c.id as chunk_id,
format('%%I.%%I', c.schema_name, c.table_name) as chunk_table,
pg_total_relation_size(format('%%I.%%I', c.schema_name, c.table_name)) AS total_bytes,
pg_indexes_size(format('%%I.%%I', c.schema_name, c.table_name)) AS index_bytes,
pg_total_relation_size(reltoastrelid) AS toast_bytes,
array_agg(d.column_name ORDER BY d.interval_length, d.column_name ASC) as partitioning_columns,
array_agg(d.column_type ORDER BY d.interval_length, d.column_name ASC) as partitioning_column_types,
array_agg(d.partitioning_func_schema || '.' || d.partitioning_func ORDER BY d.interval_length, d.column_name ASC) as partitioning_functions,
array_agg('[' || _timescaledb_internal.range_value_to_pretty(range_start, column_type) ||
',' ||
_timescaledb_internal.range_value_to_pretty(range_end, column_type) || ')' ORDER BY d.interval_length, d.column_name ASC) as ranges
FROM
_timescaledb_catalog.hypertable h,
_timescaledb_catalog.chunk c,
_timescaledb_catalog.chunk_constraint cc,
_timescaledb_catalog.dimension d,
_timescaledb_catalog.dimension_slice ds,
pg_class pgc,
pg_namespace pns
WHERE h.schema_name = %L
AND h.table_name = %L
AND pgc.relname = c.table_name
AND pns.oid = pgc.relnamespace
AND pns.nspname = c.schema_name
AND relkind = 'r'
AND c.hypertable_id = h.id
AND c.id = cc.chunk_id
AND cc.dimension_slice_id = ds.id
AND ds.dimension_id = d.id
GROUP BY c.id, pgc.reltoastrelid, pgc.oid ORDER BY c.id
) sub1
) sub2;
$$,
schema_name, table_name);
END;
$BODY$;
-- Get sizes of indexes on a hypertable
--
-- main_table - hypertable to get index sizes of
--
-- Returns:
-- index_name - index on hyper table
-- total_bytes - size of index on disk
CREATE OR REPLACE FUNCTION indexes_relation_size(
main_table REGCLASS
)
RETURNS TABLE (index_name TEXT,
total_bytes BIGINT)
LANGUAGE PLPGSQL STABLE STRICT
AS
$BODY$
<<main>>
DECLARE
table_name NAME;
schema_name NAME;
BEGIN
SELECT relname, nspname
INTO STRICT table_name, schema_name
FROM pg_class c
INNER JOIN pg_namespace n ON (n.OID = c.relnamespace)
WHERE c.OID = main_table;
RETURN QUERY
SELECT format('%I.%I', h.schema_name, ci.hypertable_index_name),
sum(pg_relation_size(c.oid))::bigint
FROM
pg_class c,
pg_namespace n,
_timescaledb_catalog.hypertable h,
_timescaledb_catalog.chunk ch,
_timescaledb_catalog.chunk_index ci
WHERE ch.schema_name = n.nspname
AND c.relnamespace = n.oid
AND c.relname = ci.index_name
AND ch.id = ci.chunk_id
AND h.id = ci.hypertable_id
AND h.schema_name = main.schema_name
AND h.table_name = main.table_name
GROUP BY h.schema_name, ci.hypertable_index_name;
END;
$BODY$;
-- Get sizes of indexes on a hypertable
--
-- main_table - hypertable to get index sizes of
--
-- Returns:
-- index_name - index on hyper table
-- total_size - pretty output of total_bytes
CREATE OR REPLACE FUNCTION indexes_relation_size_pretty(
main_table REGCLASS
)
RETURNS TABLE (index_name TEXT,
total_size TEXT) LANGUAGE PLPGSQL STABLE STRICT
AS
$BODY$
BEGIN
RETURN QUERY
SELECT s.index_name,
pg_size_pretty(s.total_bytes)
FROM @extschema@.indexes_relation_size(main_table) s;
END;
$BODY$;
-- Convenience function to return approximate row count
--
-- main_table - hypertable to get approximate row count for; if NULL, get count
-- for all hypertables
--
-- Returns:
-- schema_name - Schema name of the hypertable
-- table_name - Table name of the hypertable
-- row_estimate - Estimated number of rows according to catalog tables
CREATE OR REPLACE FUNCTION hypertable_approximate_row_count(
main_table REGCLASS = NULL
)
RETURNS TABLE (schema_name NAME,
table_name NAME,
row_estimate BIGINT
) LANGUAGE PLPGSQL VOLATILE
AS
$BODY$
<<main>>
DECLARE
table_name NAME;
schema_name NAME;
BEGIN
IF main_table IS NOT NULL THEN
SELECT relname, nspname
INTO STRICT table_name, schema_name
FROM pg_class c
INNER JOIN pg_namespace n ON (n.OID = c.relnamespace)
WHERE c.OID = main_table;
END IF;
-- Thanks to @fvannee on Github for providing the initial draft
-- of this query
RETURN QUERY
SELECT h.schema_name,
h.table_name,
row_estimate.row_estimate
FROM _timescaledb_catalog.hypertable h
CROSS JOIN LATERAL (
SELECT sum(cl.reltuples)::BIGINT AS row_estimate
FROM _timescaledb_catalog.chunk c
JOIN pg_class cl ON cl.relname = c.table_name
WHERE c.hypertable_id = h.id
GROUP BY h.schema_name, h.table_name
) row_estimate
WHERE (main.table_name IS NULL OR h.table_name = main.table_name)
AND (main.schema_name IS NULL OR h.schema_name = main.schema_name)
ORDER BY h.schema_name, h.table_name;
END
$BODY$;