diff --git a/.unreleased/pr_6463 b/.unreleased/pr_6463 new file mode 100644 index 00000000000..4b9d4c70a00 --- /dev/null +++ b/.unreleased/pr_6463 @@ -0,0 +1 @@ +Implements: #6463 Support approximate hypertable size diff --git a/sql/size_utils.sql b/sql/size_utils.sql index d968b030533..27bd7da9438 100644 --- a/sql/size_utils.sql +++ b/sql/size_utils.sql @@ -9,6 +9,10 @@ CREATE OR REPLACE FUNCTION _timescaledb_functions.relation_size(relation REGCLAS RETURNS TABLE (total_size BIGINT, heap_size BIGINT, index_size BIGINT, toast_size BIGINT) AS '@MODULE_PATHNAME@', 'ts_relation_size' LANGUAGE C VOLATILE; +CREATE OR REPLACE FUNCTION _timescaledb_functions.relation_approximate_size(relation REGCLASS) +RETURNS TABLE (total_size BIGINT, heap_size BIGINT, index_size BIGINT, toast_size BIGINT) +AS '@MODULE_PATHNAME@', 'ts_relation_approximate_size' LANGUAGE C STRICT VOLATILE; + CREATE OR REPLACE VIEW _timescaledb_internal.hypertable_chunk_local_size AS SELECT h.schema_name AS hypertable_schema, @@ -169,6 +173,29 @@ $BODY$ FROM @extschema@.hypertable_detailed_size(hypertable); $BODY$ SET search_path TO pg_catalog, pg_temp; +-- Get approximate relation size of hypertable +-- +-- hypertable - hypertable to get approximate size of +-- +-- Returns: +-- table_bytes - Approximate disk space used by hypertable +-- index_bytes - Approximate disk space used by indexes +-- toast_bytes - Approximate disk space of toast tables +-- total_bytes - Total approximate disk space used by the specified table, including all indexes and TOAST data +CREATE OR REPLACE FUNCTION @extschema@.hypertable_approximate_detailed_size(relation REGCLASS) +RETURNS TABLE (table_bytes BIGINT, index_bytes BIGINT, toast_bytes BIGINT, total_bytes BIGINT) +AS '@MODULE_PATHNAME@', 'ts_hypertable_approximate_size' LANGUAGE C VOLATILE; + +--- returns approximate total-bytes for a hypertable (includes table + index) +CREATE OR REPLACE FUNCTION @extschema@.hypertable_approximate_size( + hypertable REGCLASS) +RETURNS BIGINT +LANGUAGE SQL VOLATILE STRICT AS +$BODY$ + SELECT sum(total_bytes)::bigint + FROM @extschema@.hypertable_approximate_detailed_size(hypertable); +$BODY$ SET search_path TO pg_catalog, pg_temp; + CREATE OR REPLACE FUNCTION _timescaledb_functions.chunks_local_size( schema_name_in name, table_name_in name) diff --git a/sql/updates/latest-dev.sql b/sql/updates/latest-dev.sql index a29f5458bf8..a727f285a7b 100644 --- a/sql/updates/latest-dev.sql +++ b/sql/updates/latest-dev.sql @@ -427,3 +427,21 @@ ALTER EXTENSION timescaledb ADD TABLE _timescaledb_internal.job_errors; ALTER EXTENSION timescaledb DROP TABLE _timescaledb_catalog.hypertable; ALTER EXTENSION timescaledb ADD TABLE _timescaledb_catalog.hypertable; SELECT pg_catalog.pg_extension_config_dump('_timescaledb_catalog.hypertable', 'WHERE id >= 1'); + +CREATE FUNCTION _timescaledb_functions.relation_approximate_size(relation REGCLASS) +RETURNS TABLE (total_size BIGINT, heap_size BIGINT, index_size BIGINT, toast_size BIGINT) +AS '@MODULE_PATHNAME@', 'ts_relation_approximate_size' LANGUAGE C STRICT VOLATILE; + +CREATE FUNCTION @extschema@.hypertable_approximate_detailed_size(relation REGCLASS) +RETURNS TABLE (table_bytes BIGINT, index_bytes BIGINT, toast_bytes BIGINT, total_bytes BIGINT) +AS '@MODULE_PATHNAME@', 'ts_hypertable_approximate_size' LANGUAGE C VOLATILE; + +--- returns approximate total-bytes for a hypertable (includes table + index) +CREATE FUNCTION @extschema@.hypertable_approximate_size( + hypertable REGCLASS) +RETURNS BIGINT +LANGUAGE SQL VOLATILE STRICT AS +$BODY$ + SELECT sum(total_bytes)::bigint + FROM @extschema@.hypertable_approximate_detailed_size(hypertable); +$BODY$ SET search_path TO pg_catalog, pg_temp; diff --git a/sql/updates/reverse-dev.sql b/sql/updates/reverse-dev.sql index 556427441b0..43d0f493123 100644 --- a/sql/updates/reverse-dev.sql +++ b/sql/updates/reverse-dev.sql @@ -788,3 +788,6 @@ ALTER EXTENSION timescaledb DROP TABLE _timescaledb_catalog.hypertable; ALTER EXTENSION timescaledb ADD TABLE _timescaledb_catalog.hypertable; -- include this now in the config SELECT pg_catalog.pg_extension_config_dump('_timescaledb_catalog.hypertable', ''); +DROP FUNCTION IF EXISTS _timescaledb_functions.relation_approximate_size(relation REGCLASS); +DROP FUNCTION IF EXISTS @extschema@.hypertable_approximate_detailed_size(relation REGCLASS); +DROP FUNCTION IF EXISTS @extschema@.hypertable_approximate_size(hypertable REGCLASS); diff --git a/src/utils.c b/src/utils.c index 98288350bd1..78a5d7e98a3 100644 --- a/src/utils.c +++ b/src/utils.c @@ -1025,6 +1025,274 @@ ts_relation_size_impl(Oid relid) return relsize; } +/* + * Try to get cached size for a provided relation across all forks. The + * size is returned in terms of number of blocks. + * + * The function calls the underlying smgrnblocks if there is no cached + * data. That call populates the cache for subsequent invocations. This + * cached data gets removed asynchronously by PG relcache invalidations + * and then the refresh/cache cycle repeats till the next invalidation. + */ +static int64 +ts_try_relation_cached_size(Relation rel, bool verbose) +{ + BlockNumber result = 0, nblocks = 0; + ForkNumber forkNum; + bool cached = true; + + /* Get heap size, including FSM and VM */ + for (forkNum = 0; forkNum <= MAX_FORKNUM; forkNum++) + { +#if PG14_LT + /* PG13 does not have smgr_cached_nblocks */ + result = InvalidBlockNumber; +#else + result = RelationGetSmgr(rel)->smgr_cached_nblocks[forkNum]; +#endif + + if (result != InvalidBlockNumber) + { + nblocks += result; + } + else + { + if (smgrexists(RelationGetSmgr(rel), forkNum)) + { + cached = false; + nblocks += smgrnblocks(RelationGetSmgr(rel), forkNum); + } + } + } + + if (verbose) + ereport(DEBUG2, + (errmsg("%s for %s", + cached ? "Cached size used" : "Fetching actual size", + RelationGetRelationName(rel)))); + + /* convert the size into bytes and return */ + return nblocks * BLCKSZ; +} + +static RelationSize +ts_relation_approximate_size_impl(Oid relid) +{ + RelationSize relsize = { 0 }; + Relation rel; + + DEBUG_WAITPOINT("relation_approximate_size_before_lock"); + /* Open relation earlier to keep a lock during all function calls */ + rel = try_relation_open(relid, AccessShareLock); + + if (!rel) + return relsize; + + /* Get the main heap size */ + relsize.heap_size = ts_try_relation_cached_size(rel, false); + + /* Get the size of the relation's indexes */ + if (rel->rd_rel->relhasindex) + { + List *index_oids = RelationGetIndexList(rel); + ListCell *cell; + + foreach (cell, index_oids) + { + Oid idxOid = lfirst_oid(cell); + Relation idxRel; + + idxRel = relation_open(idxOid, AccessShareLock); + relsize.index_size += ts_try_relation_cached_size(idxRel, false); + relation_close(idxRel, AccessShareLock); + } + } + + /* If there's an associated TOAST table, calculate the total size (including its indexes) */ + if (OidIsValid(rel->rd_rel->reltoastrelid)) + { + Relation toastRel; + List *index_oids; + ListCell *cell; + + toastRel = relation_open(rel->rd_rel->reltoastrelid, AccessShareLock); + relsize.toast_size = ts_try_relation_cached_size(toastRel, false); + + /* Get the indexes size of the TOAST relation */ + index_oids = RelationGetIndexList(toastRel); + foreach (cell, index_oids) + { + Oid idxOid = lfirst_oid(cell); + Relation idxRel; + + idxRel = relation_open(idxOid, AccessShareLock); + relsize.toast_size += ts_try_relation_cached_size(idxRel, false); + relation_close(idxRel, AccessShareLock); + } + + relation_close(toastRel, AccessShareLock); + } + + relation_close(rel, AccessShareLock); + + /* Add up the total size based on the heap size, indexes and toast */ + relsize.total_size = relsize.heap_size + relsize.index_size + relsize.toast_size; + + return relsize; +} + +TS_FUNCTION_INFO_V1(ts_relation_approximate_size); +Datum +ts_relation_approximate_size(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + RelationSize relsize = { 0 }; + TupleDesc tupdesc; + HeapTuple tuple; + Datum values[4] = { 0 }; + bool nulls[4] = { false }; + + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("function returning record called in context " + "that cannot accept type record"))); + + /* check if object exists, return NULL otherwise */ + if (get_rel_name(relid) == NULL) + PG_RETURN_NULL(); + + relsize = ts_relation_approximate_size_impl(relid); + + tupdesc = BlessTupleDesc(tupdesc); + + values[0] = Int64GetDatum(relsize.total_size); + values[1] = Int64GetDatum(relsize.heap_size); + values[2] = Int64GetDatum(relsize.index_size); + values[3] = Int64GetDatum(relsize.toast_size); + + tuple = heap_form_tuple(tupdesc, values, nulls); + + return HeapTupleGetDatum(tuple); +} + +static void +init_scan_by_hypertable_id(ScanIterator *iterator, int32 hypertable_id) +{ + iterator->ctx.index = catalog_get_index(ts_catalog_get(), CHUNK, CHUNK_HYPERTABLE_ID_INDEX); + ts_scan_iterator_scan_key_init(iterator, + Anum_chunk_hypertable_id_idx_hypertable_id, + BTEqualStrategyNumber, + F_INT4EQ, + Int32GetDatum(hypertable_id)); +} + +#define ADD_RELATIONSIZE(total, rel) \ + do \ + { \ + (total).heap_size += (rel).heap_size; \ + (total).toast_size += (rel).toast_size; \ + (total).index_size += (rel).index_size; \ + (total).total_size += (rel).total_size; \ + } while (0) + +TS_FUNCTION_INFO_V1(ts_hypertable_approximate_size); +Datum +ts_hypertable_approximate_size(PG_FUNCTION_ARGS) +{ + Oid relid = PG_ARGISNULL(0) ? InvalidOid : PG_GETARG_OID(0); + RelationSize total_relsize = { 0 }; + TupleDesc tupdesc; + HeapTuple tuple; + Datum values[4] = { 0 }; + bool nulls[4] = { false }; + Cache *hcache; + Hypertable *ht; + ScanIterator iterator = ts_scan_iterator_create(CHUNK, RowExclusiveLock, CurrentMemoryContext); + + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("function returning record called in context " + "that cannot accept type record"))); + + if (!OidIsValid(relid)) + PG_RETURN_NULL(); + + /* go ahead only if this is a hypertable or a CAgg */ + hcache = ts_hypertable_cache_pin(); + ht = ts_resolve_hypertable_from_table_or_cagg(hcache, relid, true); + if (ht == NULL) + { + ts_cache_release(hcache); + PG_RETURN_NULL(); + } + + /* get the main hypertable size */ + total_relsize = ts_relation_approximate_size_impl(relid); + + iterator = ts_scan_iterator_create(CHUNK, RowExclusiveLock, CurrentMemoryContext); + init_scan_by_hypertable_id(&iterator, ht->fd.id); + ts_scanner_foreach(&iterator) + { + bool isnull, dropped, is_osm_chunk; + TupleInfo *ti = ts_scan_iterator_tuple_info(&iterator); + Datum id = slot_getattr(ti->slot, Anum_chunk_id, &isnull); + Datum comp_id = DatumGetInt32(slot_getattr(ti->slot, Anum_chunk_id, &isnull)); + int32 chunk_id, compressed_chunk_id; + Oid chunk_relid, compressed_chunk_relid; + RelationSize chunk_relsize, compressed_chunk_relsize; + + if (isnull) + continue; + + /* only consider chunks that are not dropped */ + dropped = DatumGetBool(slot_getattr(ti->slot, Anum_chunk_dropped, &isnull)); + Assert(!isnull); + if (dropped) + continue; + + chunk_id = DatumGetInt32(id); + + /* avoid if it's an OSM chunk */ + is_osm_chunk = slot_getattr(ti->slot, Anum_chunk_osm_chunk, &isnull); + Assert(!isnull); + if (is_osm_chunk) + continue; + + chunk_relid = ts_chunk_get_relid(chunk_id, false); + chunk_relsize = ts_relation_approximate_size_impl(chunk_relid); + /* add this chunk's size to the total size */ + ADD_RELATIONSIZE(total_relsize, chunk_relsize); + + /* check if the chunk has a compressed counterpart and add if yes */ + comp_id = slot_getattr(ti->slot, Anum_chunk_compressed_chunk_id, &isnull); + if (isnull) + continue; + + compressed_chunk_id = DatumGetInt32(comp_id); + compressed_chunk_relid = ts_chunk_get_relid(compressed_chunk_id, false); + compressed_chunk_relsize = ts_relation_approximate_size_impl(compressed_chunk_relid); + /* add this compressed chunk's size to the total size */ + ADD_RELATIONSIZE(total_relsize, compressed_chunk_relsize); + } + ts_scan_iterator_close(&iterator); + + tupdesc = BlessTupleDesc(tupdesc); + + values[0] = Int64GetDatum(total_relsize.heap_size); + values[1] = Int64GetDatum(total_relsize.index_size); + values[2] = Int64GetDatum(total_relsize.toast_size); + values[3] = Int64GetDatum(total_relsize.total_size); + + tuple = heap_form_tuple(tupdesc, values, nulls); + ts_cache_release(hcache); + + return HeapTupleGetDatum(tuple); +} + #define STR_VALUE(str) #str #define NODE_CASE(name) \ case T_##name: \ diff --git a/test/expected/size_utils.out b/test/expected/size_utils.out index a4ccc02e499..0349a2ef99f 100644 --- a/test/expected/size_utils.out +++ b/test/expected/size_utils.out @@ -583,6 +583,86 @@ SELECT * FROM _timescaledb_functions.relation_size(NULL); | | | (1 row) +-- Test approximate size functions with invalid input +SELECT * FROM hypertable_approximate_size(0); + hypertable_approximate_size +----------------------------- + +(1 row) + +SELECT * FROM hypertable_approximate_detailed_size(0); + table_bytes | index_bytes | toast_bytes | total_bytes +-------------+-------------+-------------+------------- + | | | +(1 row) + +SELECT * FROM _timescaledb_functions.relation_approximate_size(0); + total_size | heap_size | index_size | toast_size +------------+-----------+------------+------------ + | | | +(1 row) + +SELECT * FROM hypertable_approximate_size(NULL); + hypertable_approximate_size +----------------------------- + +(1 row) + +SELECT * FROM hypertable_approximate_detailed_size(NULL); + table_bytes | index_bytes | toast_bytes | total_bytes +-------------+-------------+-------------+------------- + | | | +(1 row) + +SELECT * FROM _timescaledb_functions.relation_approximate_size(NULL); + total_size | heap_size | index_size | toast_size +------------+-----------+------------+------------ +(0 rows) + +-- Test size on view, sequence and composite type +CREATE VIEW view1 as SELECT 1; +SELECT * FROM _timescaledb_functions.relation_approximate_size('view1'); + total_size | heap_size | index_size | toast_size +------------+-----------+------------+------------ + 0 | 0 | 0 | 0 +(1 row) + +SELECT * FROM _timescaledb_functions.relation_size('view1'); + total_size | heap_size | index_size | toast_size +------------+-----------+------------+------------ + 0 | 0 | 0 | 0 +(1 row) + +CREATE SEQUENCE test_id_seq + INCREMENT 1 + START 1 MINVALUE 1 + MAXVALUE 9223372036854775807 + CACHE 1; +SELECT * FROM _timescaledb_functions.relation_approximate_size('test_id_seq'); + total_size | heap_size | index_size | toast_size +------------+-----------+------------+------------ + 8192 | 8192 | 0 | 0 +(1 row) + +SELECT * FROM _timescaledb_functions.relation_size('test_id_seq'); + total_size | heap_size | index_size | toast_size +------------+-----------+------------+------------ + 8192 | 8192 | 0 | 0 +(1 row) + +CREATE TYPE test_type AS (time timestamp, temp float); +SELECT * FROM _timescaledb_functions.relation_approximate_size('test_type'); + total_size | heap_size | index_size | toast_size +------------+-----------+------------+------------ + 0 | 0 | 0 | 0 +(1 row) + +SELECT * FROM _timescaledb_functions.relation_size('test_type'); + total_size | heap_size | index_size | toast_size +------------+-----------+------------+------------ + 0 | 0 | 0 | 0 +(1 row) + -- Test size functions on regular table CREATE TABLE hypersize(time timestamptz, device int); CREATE INDEX hypersize_time_idx ON hypersize (time); @@ -633,6 +713,18 @@ SELECT * FROM hypertable_index_size('hypersize_time_idx'); (1 row) +SELECT * FROM _timescaledb_functions.relation_approximate_size('hypersize'); + total_size | heap_size | index_size | toast_size +------------+-----------+------------+------------ + 8192 | 0 | 8192 | 0 +(1 row) + +SELECT * FROM hypertable_approximate_size('hypersize'); +ERROR: "hypersize" is not a hypertable or a continuous aggregate +HINT: The operation is only possible on a hypertable or continuous aggregate. +SELECT * FROM hypertable_approximate_detailed_size('hypersize'); +ERROR: "hypersize" is not a hypertable or a continuous aggregate +HINT: The operation is only possible on a hypertable or continuous aggregate. \set VERBOSITY terse \set ON_ERROR_STOP 1 -- Test size functions on empty hypertable @@ -688,6 +780,24 @@ SELECT * FROM hypertable_index_size('hypersize_time_idx'); 8192 (1 row) +SELECT * FROM _timescaledb_functions.relation_approximate_size('hypersize'); + total_size | heap_size | index_size | toast_size +------------+-----------+------------+------------ + 8192 | 0 | 8192 | 0 +(1 row) + +SELECT * FROM hypertable_approximate_size('hypersize'); + hypertable_approximate_size +----------------------------- + 8192 +(1 row) + +SELECT * FROM hypertable_approximate_detailed_size('hypersize'); + table_bytes | index_bytes | toast_bytes | total_bytes +-------------+-------------+-------------+------------- + 0 | 8192 | 0 | 8192 +(1 row) + -- Test size functions on non-empty hypertable INSERT INTO hypersize VALUES('2021-02-25', 1); SELECT pg_relation_size('hypersize'), pg_table_size('hypersize'), pg_indexes_size('hypersize'), pg_total_relation_size('hypersize'), pg_relation_size('hypersize_time_idx'); @@ -744,6 +854,72 @@ SELECT * FROM hypertable_index_size('hypersize_time_idx'); 24576 (1 row) +SELECT * FROM _timescaledb_functions.relation_approximate_size('hypersize'); + total_size | heap_size | index_size | toast_size +------------+-----------+------------+------------ + 8192 | 0 | 8192 | 0 +(1 row) + +SELECT * FROM hypertable_approximate_size('hypersize'); + hypertable_approximate_size +----------------------------- + 32768 +(1 row) + +SELECT * FROM hypertable_approximate_detailed_size('hypersize'); + table_bytes | index_bytes | toast_bytes | total_bytes +-------------+-------------+-------------+------------- + 8192 | 24576 | 0 | 32768 +(1 row) + +-- Test approx size functions with toast entries +SELECT * FROM _timescaledb_functions.relation_approximate_size('toast_test'); + total_size | heap_size | index_size | toast_size +------------+-----------+------------+------------ + 16384 | 0 | 8192 | 8192 +(1 row) + +SELECT * FROM hypertable_approximate_size('toast_test'); + hypertable_approximate_size +----------------------------- + 65536 +(1 row) + +SELECT * FROM hypertable_approximate_detailed_size('toast_test'); + table_bytes | index_bytes | toast_bytes | total_bytes +-------------+-------------+-------------+------------- + 8192 | 24576 | 32768 | 65536 +(1 row) + +-- Test approx size function against a regular table +\set ON_ERROR_STOP 0 +CREATE TABLE regular(time TIMESTAMP, value TEXT); +SELECT * FROM hypertable_approximate_size('regular'); +ERROR: "regular" is not a hypertable or a continuous aggregate +\set ON_ERROR_STOP 1 +-- Test approx size functions with dropped chunks +CREATE TABLE drop_chunks_table(time BIGINT NOT NULL, data INTEGER); +SELECT hypertable_id AS drop_chunks_table_id + FROM create_hypertable('drop_chunks_table', 'time', chunk_time_interval => 10) \gset +INSERT INTO drop_chunks_table SELECT i, i FROM generate_series(0, 29) AS i; +SELECT * FROM hypertable_approximate_size('drop_chunks_table'); + hypertable_approximate_size +----------------------------- + 81920 +(1 row) + +SELECT drop_chunks('drop_chunks_table', older_than => 19); + drop_chunks +----------------------------------------- + _timescaledb_internal._hyper_7_12_chunk +(1 row) + +SELECT * FROM hypertable_approximate_size('drop_chunks_table'); + hypertable_approximate_size +----------------------------- + 57344 +(1 row) + -- github issue #4857 -- below procedure should not crash SET client_min_messages = ERROR; diff --git a/test/sql/size_utils.sql b/test/sql/size_utils.sql index dc54b99b2df..c0a8ebf6597 100644 --- a/test/sql/size_utils.sql +++ b/test/sql/size_utils.sql @@ -232,6 +232,29 @@ SELECT * FROM chunk_compression_stats(NULL) ORDER BY node_name; SELECT * FROM hypertable_index_size(NULL); SELECT * FROM _timescaledb_functions.relation_size(NULL); +-- Test approximate size functions with invalid input +SELECT * FROM hypertable_approximate_size(0); +SELECT * FROM hypertable_approximate_detailed_size(0); +SELECT * FROM _timescaledb_functions.relation_approximate_size(0); +SELECT * FROM hypertable_approximate_size(NULL); +SELECT * FROM hypertable_approximate_detailed_size(NULL); +SELECT * FROM _timescaledb_functions.relation_approximate_size(NULL); + +-- Test size on view, sequence and composite type +CREATE VIEW view1 as SELECT 1; +SELECT * FROM _timescaledb_functions.relation_approximate_size('view1'); +SELECT * FROM _timescaledb_functions.relation_size('view1'); +CREATE SEQUENCE test_id_seq + INCREMENT 1 + START 1 MINVALUE 1 + MAXVALUE 9223372036854775807 + CACHE 1; +SELECT * FROM _timescaledb_functions.relation_approximate_size('test_id_seq'); +SELECT * FROM _timescaledb_functions.relation_size('test_id_seq'); +CREATE TYPE test_type AS (time timestamp, temp float); +SELECT * FROM _timescaledb_functions.relation_approximate_size('test_type'); +SELECT * FROM _timescaledb_functions.relation_size('test_type'); + -- Test size functions on regular table CREATE TABLE hypersize(time timestamptz, device int); CREATE INDEX hypersize_time_idx ON hypersize (time); @@ -246,6 +269,9 @@ SELECT * FROM chunks_detailed_size('hypersize') ORDER BY node_name; SELECT * FROM hypertable_compression_stats('hypersize') ORDER BY node_name; SELECT * FROM chunk_compression_stats('hypersize') ORDER BY node_name; SELECT * FROM hypertable_index_size('hypersize_time_idx'); +SELECT * FROM _timescaledb_functions.relation_approximate_size('hypersize'); +SELECT * FROM hypertable_approximate_size('hypersize'); +SELECT * FROM hypertable_approximate_detailed_size('hypersize'); \set VERBOSITY terse \set ON_ERROR_STOP 1 @@ -259,6 +285,9 @@ SELECT * FROM chunks_detailed_size('hypersize') ORDER BY node_name; SELECT * FROM hypertable_compression_stats('hypersize') ORDER BY node_name; SELECT * FROM chunk_compression_stats('hypersize') ORDER BY node_name; SELECT * FROM hypertable_index_size('hypersize_time_idx'); +SELECT * FROM _timescaledb_functions.relation_approximate_size('hypersize'); +SELECT * FROM hypertable_approximate_size('hypersize'); +SELECT * FROM hypertable_approximate_detailed_size('hypersize'); -- Test size functions on non-empty hypertable INSERT INTO hypersize VALUES('2021-02-25', 1); @@ -273,6 +302,26 @@ SELECT * FROM chunks_detailed_size('hypersize') ORDER BY node_name; SELECT * FROM hypertable_compression_stats('hypersize') ORDER BY node_name; SELECT * FROM chunk_compression_stats('hypersize') ORDER BY node_name; SELECT * FROM hypertable_index_size('hypersize_time_idx'); +SELECT * FROM _timescaledb_functions.relation_approximate_size('hypersize'); +SELECT * FROM hypertable_approximate_size('hypersize'); +SELECT * FROM hypertable_approximate_detailed_size('hypersize'); +-- Test approx size functions with toast entries +SELECT * FROM _timescaledb_functions.relation_approximate_size('toast_test'); +SELECT * FROM hypertable_approximate_size('toast_test'); +SELECT * FROM hypertable_approximate_detailed_size('toast_test'); +-- Test approx size function against a regular table +\set ON_ERROR_STOP 0 +CREATE TABLE regular(time TIMESTAMP, value TEXT); +SELECT * FROM hypertable_approximate_size('regular'); +\set ON_ERROR_STOP 1 +-- Test approx size functions with dropped chunks +CREATE TABLE drop_chunks_table(time BIGINT NOT NULL, data INTEGER); +SELECT hypertable_id AS drop_chunks_table_id + FROM create_hypertable('drop_chunks_table', 'time', chunk_time_interval => 10) \gset +INSERT INTO drop_chunks_table SELECT i, i FROM generate_series(0, 29) AS i; +SELECT * FROM hypertable_approximate_size('drop_chunks_table'); +SELECT drop_chunks('drop_chunks_table', older_than => 19); +SELECT * FROM hypertable_approximate_size('drop_chunks_table'); -- github issue #4857 -- below procedure should not crash diff --git a/tsl/test/expected/chunk_utils_compression.out b/tsl/test/expected/chunk_utils_compression.out index 4f79e960704..c561a01f979 100644 --- a/tsl/test/expected/chunk_utils_compression.out +++ b/tsl/test/expected/chunk_utils_compression.out @@ -73,6 +73,20 @@ SELECT compress_chunk(show_chunks('public.table_to_compress')); _timescaledb_internal._hyper_2_9_chunk (3 rows) +-- check that approx size function works. We call VACUUM to ensure all forks exist +VACUUM public.table_to_compress; +SELECT * FROM hypertable_approximate_size('public.table_to_compress'); + hypertable_approximate_size +----------------------------- + 262144 +(1 row) + +SELECT * FROM hypertable_size('public.table_to_compress'); + hypertable_size +----------------- + 262144 +(1 row) + -- and run the queries again to make sure results are the same SELECT show_chunks('public.uncompressed_table'); show_chunks diff --git a/tsl/test/expected/chunk_utils_internal.out b/tsl/test/expected/chunk_utils_internal.out index 3e1d1b1aebe..6536bd44ec7 100644 --- a/tsl/test/expected/chunk_utils_internal.out +++ b/tsl/test/expected/chunk_utils_internal.out @@ -515,6 +515,19 @@ SELECT * FROM child_fdw_table; Wed Jan 01 01:00:00 2020 PST | 100 | 1000 (1 row) +-- test size functions on foreign table +SELECT * FROM _timescaledb_functions.relation_approximate_size('child_fdw_table'); + total_size | heap_size | index_size | toast_size +------------+-----------+------------+------------ + 0 | 0 | 0 | 0 +(1 row) + +SELECT * FROM _timescaledb_functions.relation_size('child_fdw_table'); + total_size | heap_size | index_size | toast_size +------------+-----------+------------+------------ + 0 | 0 | 0 | 0 +(1 row) + -- error should be thrown as the hypertable does not yet have an associated tiered chunk \set ON_ERROR_STOP 0 SELECT _timescaledb_functions.hypertable_osm_range_update('ht_try','2020-01-01 01:00'::timestamptz, '2020-01-01 03:00'); @@ -707,6 +720,13 @@ ERROR: dimension slice range_end cannot be less than range_start SELECT _timescaledb_functions.hypertable_osm_range_update('ht_try', '2022-05-05 01:00'::timestamptz, '2022-05-06'); ERROR: attempting to set overlapping range for tiered chunk of public.ht_try \set ON_ERROR_STOP 1 +-- test that approximate size function works when a osm chunk is present +SELECT * FROM hypertable_approximate_size('ht_try'); + hypertable_approximate_size +----------------------------- + 32768 +(1 row) + --TEST GUC variable to enable/disable OSM chunk SET timescaledb.enable_tiered_reads=false; EXPLAIN (COSTS OFF) SELECT * from ht_try; diff --git a/tsl/test/shared/expected/extension.out b/tsl/test/shared/expected/extension.out index fa6b8e1fa03..b8fca9a6e2b 100644 --- a/tsl/test/shared/expected/extension.out +++ b/tsl/test/shared/expected/extension.out @@ -116,6 +116,7 @@ ORDER BY pronamespace::regnamespace::text COLLATE "C", p.oid::regprocedure::text _timescaledb_functions.process_ddl_event() _timescaledb_functions.range_value_to_pretty(bigint,regtype) _timescaledb_functions.recompress_chunk_segmentwise(regclass,boolean) + _timescaledb_functions.relation_approximate_size(regclass) _timescaledb_functions.relation_size(regclass) _timescaledb_functions.repair_relation_acls() _timescaledb_functions.restart_background_workers() @@ -246,6 +247,8 @@ ORDER BY pronamespace::regnamespace::text COLLATE "C", p.oid::regprocedure::text drop_chunks(regclass,"any","any",boolean,"any","any") first(anyelement,"any") histogram(double precision,double precision,double precision,integer) + hypertable_approximate_detailed_size(regclass) + hypertable_approximate_size(regclass) hypertable_compression_stats(regclass) hypertable_detailed_size(regclass) hypertable_index_size(regclass) diff --git a/tsl/test/sql/chunk_utils_compression.sql b/tsl/test/sql/chunk_utils_compression.sql index d258039debf..113ca95dd80 100644 --- a/tsl/test/sql/chunk_utils_compression.sql +++ b/tsl/test/sql/chunk_utils_compression.sql @@ -32,6 +32,10 @@ SELECT show_chunks('public.table_to_compress', older_than=>'1 day'::interval); SELECT show_chunks('public.table_to_compress', newer_than=>'1 day'::interval); -- compress all chunks of the table: SELECT compress_chunk(show_chunks('public.table_to_compress')); +-- check that approx size function works. We call VACUUM to ensure all forks exist +VACUUM public.table_to_compress; +SELECT * FROM hypertable_approximate_size('public.table_to_compress'); +SELECT * FROM hypertable_size('public.table_to_compress'); -- and run the queries again to make sure results are the same SELECT show_chunks('public.uncompressed_table'); SELECT show_chunks('public.table_to_compress'); diff --git a/tsl/test/sql/chunk_utils_internal.sql b/tsl/test/sql/chunk_utils_internal.sql index 47918882af8..9c2dde243d2 100644 --- a/tsl/test/sql/chunk_utils_internal.sql +++ b/tsl/test/sql/chunk_utils_internal.sql @@ -325,6 +325,9 @@ SELECT create_hypertable('ht_try', 'timec', chunk_time_interval => interval '1 d INSERT INTO ht_try VALUES ('2022-05-05 01:00', 222, 222); SELECT * FROM child_fdw_table; +-- test size functions on foreign table +SELECT * FROM _timescaledb_functions.relation_approximate_size('child_fdw_table'); +SELECT * FROM _timescaledb_functions.relation_size('child_fdw_table'); -- error should be thrown as the hypertable does not yet have an associated tiered chunk \set ON_ERROR_STOP 0 @@ -396,6 +399,9 @@ SELECT _timescaledb_functions.hypertable_osm_range_update('ht_try', '2020-01-02 SELECT _timescaledb_functions.hypertable_osm_range_update('ht_try', '2022-05-05 01:00'::timestamptz, '2022-05-06'); \set ON_ERROR_STOP 1 +-- test that approximate size function works when a osm chunk is present +SELECT * FROM hypertable_approximate_size('ht_try'); + --TEST GUC variable to enable/disable OSM chunk SET timescaledb.enable_tiered_reads=false; EXPLAIN (COSTS OFF) SELECT * from ht_try;