Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ensure reltuples are preserved during compression #2539

Merged
merged 1 commit into from Oct 19, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
19 changes: 18 additions & 1 deletion src/planner.c
Expand Up @@ -872,15 +872,32 @@ timescaledb_get_relation_info_hook(PlannerInfo *root, Oid relation_objectid, boo

if (chunk->fd.compressed_chunk_id > 0)
{
Relation uncompressed_chunk = table_open(relation_objectid, NoLock);

ts_get_private_reloptinfo(rel)->compressed = true;

/* Planning indexes are expensive, and if this is a compressed chunk, we
* know we'll never need to us indexes on the uncompressed version, since
* know we'll never need to use indexes on the uncompressed version, since
* all the data is in the compressed chunk anyway. Therefore, it is much
* faster if we simply trash the indexlist here and never plan any useless
* IndexPaths at all
*/
rel->indexlist = NIL;

/* Relation size estimates are messed up on compressed chunks due to there
* being no actual pages for the table in the storage manager.
*/
rel->pages = (BlockNumber) uncompressed_chunk->rd_rel->relpages;
rel->tuples = (double) uncompressed_chunk->rd_rel->reltuples;
if (rel->pages == 0)
rel->allvisfrac = 0.0;
else if (uncompressed_chunk->rd_rel->relallvisible >= rel->pages)
rel->allvisfrac = 1.0;
else
rel->allvisfrac =
(double) uncompressed_chunk->rd_rel->relallvisible / rel->pages;

table_close(uncompressed_chunk, NoLock);
}
}
break;
Expand Down
50 changes: 50 additions & 0 deletions tsl/src/compression/compression.c
Expand Up @@ -157,6 +157,49 @@ static void row_compressor_finish(RowCompressor *row_compressor);
** compress_chunk **
********************/

static void
capture_pgclass_stats(Oid table_oid, int *out_pages, int *out_visible, float *out_tuples)
{
Relation pg_class = table_open(RelationRelationId, RowExclusiveLock);
HeapTuple tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(table_oid));
Form_pg_class classform;

if (!HeapTupleIsValid(tuple))
elog(ERROR, "could not find tuple for relation %u", table_oid);

classform = (Form_pg_class) GETSTRUCT(tuple);

*out_pages = classform->relpages;
*out_visible = classform->relallvisible;
*out_tuples = classform->reltuples;

heap_freetuple(tuple);
table_close(pg_class, RowExclusiveLock);
}

static void
restore_pgclass_stats(Oid table_oid, int pages, int visible, float tuples)
{
Relation pg_class;
HeapTuple tuple;
Form_pg_class classform;

pg_class = table_open(RelationRelationId, RowExclusiveLock);
tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(table_oid));
if (!HeapTupleIsValid(tuple))
elog(ERROR, "could not find tuple for relation %u", table_oid);
classform = (Form_pg_class) GETSTRUCT(tuple);

classform->relpages = pages;
classform->relallvisible = visible;
classform->reltuples = tuples;

CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);

heap_freetuple(tuple);
table_close(pg_class, RowExclusiveLock);
}

/* Truncate the relation WITHOUT applying triggers. This is the
* main difference with ExecuteTruncate. Triggers aren't applied
* because the data remains, just in compressed form. Also don't
Expand All @@ -173,6 +216,8 @@ truncate_relation(Oid table_oid)
MultiXactId minmulti;
#endif
Oid toast_relid;
int pages, visible;
float tuples;

/* Chunks should never have fks into them, but double check */
if (fks != NIL)
Expand All @@ -183,6 +228,7 @@ truncate_relation(Oid table_oid)
minmulti = GetOldestMultiXactId();
#endif

capture_pgclass_stats(table_oid, &pages, &visible, &tuples);
RelationSetNewRelfilenode(rel,
rel->rd_rel->relpersistence
#if PG12_LT
Expand Down Expand Up @@ -212,6 +258,10 @@ truncate_relation(Oid table_oid)
}

reindex_relation(table_oid, REINDEX_REL_PROCESS_TOAST, 0);
rel = table_open(table_oid, AccessExclusiveLock);
restore_pgclass_stats(table_oid, pages, visible, tuples);
CommandCounterIncrement();
table_close(rel, NoLock);
}

CompressionStats
Expand Down
15 changes: 14 additions & 1 deletion tsl/test/expected/compression.out
Expand Up @@ -1233,7 +1233,8 @@ ERROR: cannot update/delete rows from chunk "_hyper_25_51_chunk" as it is compr
\set ON_ERROR_STOP 1
DROP TABLE compressed_ht;
DROP TABLE uncompressed_ht;
-- Test that pg_stats for uncompressed chunks are frozen at compression time
-- Test that pg_stats and pg_class stats for uncompressed chunks are frozen at compression time
-- Note that approximate_row_count pulls from pg_class
CREATE TABLE stattest(time TIMESTAMPTZ NOT NULL, c1 int);
SELECT create_hypertable('stattest', 'time');
create_hypertable
Expand All @@ -1250,12 +1251,24 @@ SELECT * FROM pg_stats WHERE tablename = :statchunk;
(0 rows)

ALTER TABLE stattest SET (timescaledb.compress);
SELECT approximate_row_count('stattest');
approximate_row_count
-----------------------
0
(1 row)

SELECT compress_chunk(c) FROM show_chunks('stattest') c;
compress_chunk
------------------------------------------
_timescaledb_internal._hyper_27_55_chunk
(1 row)

SELECT approximate_row_count('stattest');
approximate_row_count
-----------------------
26
(1 row)

SELECT histogram_bounds FROM pg_stats WHERE tablename = :statchunk AND attname = 'c1';
histogram_bounds
-------------------------------------------------------------------------------------------------------------------------------
Expand Down