diff --git a/columnar/src/backend/columnar/columnar.control b/columnar/src/backend/columnar/columnar.control index 4cc0010d..0438faff 100644 --- a/columnar/src/backend/columnar/columnar.control +++ b/columnar/src/backend/columnar/columnar.control @@ -1,4 +1,4 @@ comment = 'Hydra Columnar extension' -default_version = '11.1-5' +default_version = '11.1-6' module_pathname = '$libdir/columnar' relocatable = false diff --git a/columnar/src/backend/columnar/columnar_metadata.c b/columnar/src/backend/columnar/columnar_metadata.c index 7b8a0428..d90e2b31 100644 --- a/columnar/src/backend/columnar/columnar_metadata.c +++ b/columnar/src/backend/columnar/columnar_metadata.c @@ -26,6 +26,7 @@ #include "citus_version.h" #include "columnar/columnar.h" +#include "columnar/columnar_metadata.h" #include "columnar/columnar_storage.h" #include "columnar/columnar_version_compat.h" #include "columnar/utils/listutils.h" @@ -2520,3 +2521,32 @@ GetHighestUsedRowNumber(uint64 storageId) return highestRowNumber; } + +/* + * RewriteMetadataRowWithNewValues rewrites a given metadata entry + * for a storageId and a stripeId in place with a new offset, + * rowCount, sizeBytes, and chunkCount. + * + * This is used in the vacuum UDF to fill any existing holes + * if possible. + */ +StripeMetadata * +RewriteStripeMetadataRowWithNewValues(Relation rel, uint64 stripeId, + uint64 sizeBytes, uint64 fileOffset, uint64 rowCount, uint64 chunkCount) +{ + uint64 storageId = ColumnarStorageGetStorageId(rel, false); + + bool update[Natts_columnar_stripe] = { false }; + update[Anum_columnar_stripe_file_offset - 1] = true; + update[Anum_columnar_stripe_data_length - 1] = true; + update[Anum_columnar_stripe_row_count - 1] = true; + update[Anum_columnar_stripe_chunk_count - 1] = true; + + Datum newValues[Natts_columnar_stripe] = { 0 }; + newValues[Anum_columnar_stripe_file_offset - 1] = Int64GetDatum(fileOffset); + newValues[Anum_columnar_stripe_data_length - 1] = Int64GetDatum(sizeBytes); + newValues[Anum_columnar_stripe_row_count - 1] = UInt64GetDatum(rowCount); + newValues[Anum_columnar_stripe_chunk_count - 1] = Int32GetDatum(chunkCount); + + return UpdateStripeMetadataRow(storageId, stripeId, update, newValues); +} diff --git a/columnar/src/backend/columnar/columnar_reader.c b/columnar/src/backend/columnar/columnar_reader.c index 71c99e8d..7a60b856 100644 --- a/columnar/src/backend/columnar/columnar_reader.c +++ b/columnar/src/backend/columnar/columnar_reader.c @@ -706,7 +706,9 @@ ColumnarEndRead(ColumnarReadState *readState) pfree(readState->currentStripeMetadata); } - pfree(readState); + if (readState) { + pfree(readState); + } } diff --git a/columnar/src/backend/columnar/columnar_tableam.c b/columnar/src/backend/columnar/columnar_tableam.c index 60009f4d..e467bd79 100644 --- a/columnar/src/backend/columnar/columnar_tableam.c +++ b/columnar/src/backend/columnar/columnar_tableam.c @@ -1,6 +1,7 @@ #include "citus_version.h" #include "postgres.h" +#include "fmgr.h" #include @@ -12,6 +13,8 @@ #include "access/rewriteheap.h" #include "access/tableam.h" #include "access/tsmapi.h" +#include "storage/lockdefs.h" +#include "utils/palloc.h" #if PG_VERSION_NUM >= 130000 #include "access/detoast.h" #else @@ -31,7 +34,9 @@ #include "commands/vacuum.h" #include "commands/extension.h" #include "executor/executor.h" +#include "funcapi.h" #include "nodes/makefuncs.h" +#include "nodes/pg_list.h" #include "optimizer/plancat.h" #include "pgstat.h" #include "safe_lib.h" @@ -54,12 +59,14 @@ #include "utils/syscache.h" #include "columnar/columnar.h" #include "columnar/columnar_customscan.h" +#include "columnar/columnar_metadata.h" #include "columnar/columnar_storage.h" #include "columnar/columnar_tableam.h" #include "columnar/columnar_version_compat.h" #include "columnar/utils/listutils.h" #include "columnar/vectorization/columnar_vector_types.h" +#include "columnar/columnar_metadata.h" /* * Timing parameters for truncate locking heuristics. @@ -136,7 +143,8 @@ static void ColumnarProcessUtility(PlannedStmt *pstmt, DestReceiver *dest, QueryCompletionCompat *completionTag); static bool ConditionalLockRelationWithTimeout(Relation rel, LOCKMODE lockMode, - int timeout, int retryInterval); + int timeout, int retryInterval, + bool acquire); static List * NeededColumnsList(TupleDesc tupdesc, Bitmapset *attr_needed); static void LogRelationStats(Relation rel, int elevel); static void TruncateColumnar(Relation rel, int elevel); @@ -1464,7 +1472,8 @@ TruncateColumnar(Relation rel, int elevel) */ if (!ConditionalLockRelationWithTimeout(rel, AccessExclusiveLock, VACUUM_TRUNCATE_LOCK_TIMEOUT, - VACUUM_TRUNCATE_LOCK_WAIT_INTERVAL)) + VACUUM_TRUNCATE_LOCK_WAIT_INTERVAL, + false)) { /* * We failed to establish the lock in the specified number of @@ -1532,7 +1541,7 @@ TruncateColumnar(Relation rel, int elevel) */ static bool ConditionalLockRelationWithTimeout(Relation rel, LOCKMODE lockMode, int timeout, - int retryInterval) + int retryInterval, bool acquire) { int lock_retry = 0; @@ -1548,7 +1557,7 @@ ConditionalLockRelationWithTimeout(Relation rel, LOCKMODE lockMode, int timeout, */ CHECK_FOR_INTERRUPTS(); - if (++lock_retry > (timeout / retryInterval)) + if (!acquire && (++lock_retry > (timeout / retryInterval))) { return false; } @@ -2842,4 +2851,406 @@ downgrade_columnar_storage(PG_FUNCTION_ARGS) PG_RETURN_VOID(); } +typedef struct StripeHole +{ + uint64 fileOffset; + uint64 dataLength; +} StripeHole; + +/* + * HolesForRelation returns a list of holes in the Relation id in the current + * MemoryContext. + */ +static List *HolesForRelation(Relation rel) +{ + List *holes = NIL; + + /* Get current columnar options */ + ColumnarOptions columnarOptions = { 0 }; + ReadColumnarOptions(rel->rd_id, &columnarOptions); + + ListCell *lc = NULL; + + List *stripeMetadataList = StripesForRelfilenode(rel->rd_node, ForwardScanDirection); + uint64 lastMinimalOffset = ColumnarFirstLogicalOffset; + + foreach(lc, stripeMetadataList) + { + StripeMetadata * stripeMetadata = lfirst(lc); + + /* Arbitrary check to see if the offset will be large enough, 10000 bytes is what was chosen. */ + if (stripeMetadata->fileOffset == lastMinimalOffset || stripeMetadata->fileOffset - lastMinimalOffset < 10000) { + lastMinimalOffset = stripeMetadata->fileOffset + stripeMetadata->dataLength; + continue; + } else { + StripeHole * hole = palloc(sizeof(StripeHole)); + hole->fileOffset = lastMinimalOffset; + hole->dataLength = stripeMetadata->fileOffset + stripeMetadata->dataLength - lastMinimalOffset; + lastMinimalOffset = stripeMetadata->fileOffset + stripeMetadata->dataLength; + + holes = lappend(holes, hole); + } + } + + return holes; +} + +/* + * vacuum_columnar_table + */ +typedef struct StripeVacuumCandidate +{ + uint64 stripeId; + int32 stripeMetadataIndex; + uint32 candidateTotalSize; + uint32 activeRows; + StripeMetadata *stripeMetadata; +} StripeVacuumCandidate; + +PG_FUNCTION_INFO_V1(vacuum_columnar_table); +Datum +vacuum_columnar_table(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + Relation rel = RelationIdGetRelation(relid); + TupleDesc tupleDesc = RelationGetDescr(rel); + MemoryContext oldcontext = CurrentMemoryContext; + MemoryContext vacuum_context = AllocSetContextCreate(CurrentMemoryContext, + "Columnar Vacuum Context", + ALLOCSET_SMALL_SIZES); + + oldcontext = MemoryContextSwitchTo(vacuum_context); + + if (tupleDesc->natts == 0) + { + ereport(INFO, + (errmsg("\"%s\": stopping vacuum due to zero column table", + RelationGetRelationName(rel)))); + + MemoryContextSwitchTo(oldcontext); + + PG_RETURN_VOID(); + } + + /* Get current columnar options */ + ColumnarOptions columnarOptions = { 0 }; + ReadColumnarOptions(rel->rd_id, &columnarOptions); + + /* Get all stripes in order. */ + List *stripeMetadataList = StripesForRelfilenode(rel->rd_node, ForwardScanDirection); + List *vacuumCandidatesStripeList = NIL; + + /* Empty table nothing to do */ + if (stripeMetadataList == NIL) + { + ereport(INFO, + (errmsg("\"%s\": stopping vacuum due to empty table", + RelationGetRelationName(rel)))); + + /* Close the relation as we are done with it */ + RelationClose(rel); + + MemoryContextSwitchTo(oldcontext); + + PG_RETURN_VOID(); + } + + ListCell *lc = NULL; + + int stripeMetadataIndex = 0; + + /* + * Get a list of all stripes that can be combined into larger stripes. + */ + foreach(lc, stripeMetadataList) + { + StripeMetadata * stripeMetadata = lfirst(lc); + uint32 stripeDeletedRows = DeletedRowsForStripe(rel->rd_node, + stripeMetadata->chunkCount, + stripeMetadata->id); + + float percentageOfDeleteRows = + (float)stripeDeletedRows / (float)(stripeMetadata->rowCount); + + /* + * If inspected stripe has less than 0.5 percent of maximum strip row size + * or percentage of deleted rows is less than 20% we will skip this stripe + * for vacuum. + */ + if ((stripeMetadata->rowCount > columnarOptions.stripeRowCount * 0.5) && + percentageOfDeleteRows <= 0.2f) + { + continue; + } + + StripeVacuumCandidate *vacuumCandidate = palloc(sizeof(StripeVacuumCandidate)); + vacuumCandidate->stripeMetadataIndex = stripeMetadataIndex; + vacuumCandidate->candidateTotalSize = stripeMetadata->rowCount - stripeDeletedRows; + vacuumCandidate->stripeMetadata = stripeMetadata; + vacuumCandidate->activeRows = stripeMetadata->rowCount - stripeDeletedRows; + + vacuumCandidatesStripeList = lappend(vacuumCandidatesStripeList, vacuumCandidate); + } + + /* we need all columns */ + int natts = rel->rd_att->natts; + Bitmapset *attr_needed = bms_add_range(NULL, 0, natts - 1); + + /* No quals for table rewrite */ + List *scanQual = NIL; + + /* Use SnapshotAny when re-writing table as heapAM does */ + Snapshot snapshot = SnapshotAny; + + MemoryContext scanContext = CreateColumnarScanMemoryContext(); + bool randomAccess = true; + + ConditionalLockRelationWithTimeout(rel, RowExclusiveLock, + VACUUM_TRUNCATE_LOCK_TIMEOUT, + VACUUM_TRUNCATE_LOCK_WAIT_INTERVAL, + true); + + ColumnarWriteState *writeState = ColumnarBeginWrite(rel->rd_node, + columnarOptions, + tupleDesc); + + /* + * Combine the vacuum candidates into their own stripes appended to the rel, this + * should clear out any space from partial stripes to make space to move stripes into. + */ + foreach(lc, vacuumCandidatesStripeList) + { + StripeVacuumCandidate *vacuumCandidate = lfirst(lc); + + ColumnarReadState *readState = init_columnar_read_state(rel, tupleDesc, + attr_needed, scanQual, + scanContext, snapshot, + randomAccess, + NULL); + + + ColumnarSetStripeReadState(readState, + vacuumCandidate->stripeMetadata); + + Datum *values = palloc0(tupleDesc->natts * sizeof(Datum)); + bool *nulls = palloc0(tupleDesc->natts * sizeof(bool)); + + int32 rowCount = 0; + /* we don't need to know rowNumber here */ + while (rowCount < vacuumCandidate->activeRows && ColumnarReadNextRow(readState, values, nulls, NULL)) + { + ColumnarWriteRow(writeState, values, nulls); + rowCount++; + } + + DeleteMetadataRowsForStripeId(rel->rd_node, vacuumCandidate->stripeMetadata->id); + ColumnarEndRead(readState); + + pfree(values); + pfree(nulls); + } + + /* + * We have finished the first route of writes, let's drop our lock until we are + * ready for the next stage: compaction. + */ + ColumnarEndWrite(writeState); + UnlockRelation(rel, RowExclusiveLock); + + /* Lock the relation. */ + ConditionalLockRelationWithTimeout(rel, RowExclusiveLock, + VACUUM_TRUNCATE_LOCK_TIMEOUT, + VACUUM_TRUNCATE_LOCK_WAIT_INTERVAL, + true); + + /* + * Get a list of all stripes in order. + */ + stripeMetadataList = StripesForRelfilenode(rel->rd_node, ForwardScanDirection); + + /* + * Continually iterate through the holes, finding where we can place + * old stripes. + */ + bool done = false; + while (!done) + { + /* + * Get a List of empty spaces to fill with later stripes. + */ + List *holes = HolesForRelation(rel); + + if (list_length(holes) == 0) + { + done = true; + continue; + } + + stripeMetadataList = StripesForRelfilenode(rel->rd_node, ForwardScanDirection); + + int relocationCount = 0; + /* + * Iterate through the holes, moving later slices into the holes. + */ + foreach(lc, holes) + { + StripeHole *hole = lfirst(lc); + + ListCell *stripeLc = NULL; + + foreach(stripeLc, stripeMetadataList) + { + StripeMetadata *stripe = lfirst(stripeLc); + + /* Find one that will fit, and move it. */ + if (stripe->dataLength <= hole->dataLength && stripe->fileOffset > hole->fileOffset) + { + /* Read a copy of the old row. */ + char * data = palloc(stripe->dataLength); + ColumnarStorageRead(rel, stripe->fileOffset, data, stripe->dataLength); + + /* Write the data to the new offset. */ + ColumnarStorageWrite(rel, hole->fileOffset, data, stripe->dataLength); + + /* Update the stripe metadata for the moved stripe. */ + StripeMetadata *newStripe = RewriteStripeMetadataRowWithNewValues(rel, stripe->id, stripe->dataLength, hole->fileOffset, stripe->rowCount, stripe->chunkCount); + + relocationCount++; + + hole->fileOffset += newStripe->dataLength; + hole->dataLength -= newStripe->dataLength; + + pfree(data); + } + + if (relocationCount == 0) + { + done = true; + } + } + + holes = HolesForRelation(rel); + } + } + + UnlockRelation(rel, RowExclusiveLock); + + relation_close(rel, NoLock); + + MemoryContextSwitchTo(oldcontext); + + PG_RETURN_VOID(); +} + +/* + * Data storage for columnar stats. + */ +typedef struct ColumnarStats +{ + uint64 stripeId; + uint64 fileOffset; + uint32 rowCount; + uint32 deletedRows; + uint32 chunkCount; + uint32 dataLength; +} ColumnarStats; + +/* We return 6 columns. */ +#define STATS_INFO_NATTS 6 + +PG_FUNCTION_INFO_V1(columnar_stats); +Datum +columnar_stats(PG_FUNCTION_ARGS) +{ + FuncCallContext *funcctx; + int call_cntr; + int max_calls; + TupleDesc tupdesc; + AttInMetadata *attinmeta; + + + /* If this is the first call in, set up the data. */ + if (SRF_IS_FIRSTCALL()) + { + MemoryContext oldcontext; + + Oid relid = PG_GETARG_OID(0); + Relation rel = RelationIdGetRelation(relid); + + /* Function context for persistance. */ + funcctx = SRF_FIRSTCALL_INIT(); + + /* Use the SRF memory context */ + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + + /* Retrieve the stripe metadata. */ + List *stripeMetadataList = StripesForRelfilenode(rel->rd_node, ForwardScanDirection); + + 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"))); + + attinmeta = TupleDescGetAttInMetadata(tupdesc); + funcctx->attinmeta = attinmeta; + + /* Set up the stats. */ + ColumnarStats *stats = palloc(sizeof(ColumnarStats) * list_length(stripeMetadataList)); + funcctx->max_calls = list_length(stripeMetadataList); + + /* Iterate through the stripes to get a copy of the important data. */ + for (int i = 0; i < funcctx->max_calls; i++) + { + StripeMetadata *data = list_nth(stripeMetadataList, i); + + stats[i].stripeId = data->id; + stats[i].fileOffset = data->fileOffset; + stats[i].rowCount = data->rowCount; + stats[i].chunkCount = data->chunkCount; + stats[i].dataLength = data->dataLength; + stats[i].deletedRows = DeletedRowsForStripe(rel->rd_node, + data->chunkCount, + data->id); + } + + funcctx->user_fctx = stats; + + table_close(rel, NoLock); + + MemoryContextSwitchTo(oldcontext); + } + + funcctx = SRF_PERCALL_SETUP(); + + call_cntr = funcctx->call_cntr; + max_calls = funcctx->max_calls; + ColumnarStats *stats = funcctx->user_fctx; + + if (call_cntr < max_calls) + { + Datum result; + + Datum values[STATS_INFO_NATTS] = { 0 }; + bool nulls[STATS_INFO_NATTS] = { 0 }; + + get_call_result_type(fcinfo, NULL, &tupdesc); + + values[0] = Int64GetDatum(stats[call_cntr].stripeId); + values[1] = Int64GetDatum(stats[call_cntr].fileOffset); + values[2] = Int32GetDatum(stats[call_cntr].rowCount); + values[3] = Int32GetDatum(stats[call_cntr].deletedRows); + values[4] = Int32GetDatum(stats[call_cntr].chunkCount); + values[5] = Int32GetDatum(stats[call_cntr].dataLength); + + HeapTuple tuple = heap_form_tuple(tupdesc, values, nulls); + result = HeapTupleGetDatum(tuple); + + SRF_RETURN_NEXT(funcctx, result); + } + else + { + SRF_RETURN_DONE(funcctx); + } +} \ No newline at end of file diff --git a/columnar/src/backend/columnar/sql/columnar--11.1-5--11.1-6.sql b/columnar/src/backend/columnar/sql/columnar--11.1-5--11.1-6.sql new file mode 100644 index 00000000..ff1027d0 --- /dev/null +++ b/columnar/src/backend/columnar/sql/columnar--11.1-5--11.1-6.sql @@ -0,0 +1,3 @@ +-- columnar--11.1-4--11.1-5.sql + +#include "udfs/vacuum/11.1-6.sql" \ No newline at end of file diff --git a/columnar/src/backend/columnar/sql/udfs/vacuum/11.1-6.sql b/columnar/src/backend/columnar/sql/udfs/vacuum/11.1-6.sql new file mode 100644 index 00000000..ec710551 --- /dev/null +++ b/columnar/src/backend/columnar/sql/udfs/vacuum/11.1-6.sql @@ -0,0 +1,19 @@ +CREATE OR REPLACE FUNCTION columnar.vacuum(rel regclass) + RETURNS VOID + STRICT + LANGUAGE c AS 'MODULE_PATHNAME', $$vacuum_columnar_table$$; + +COMMENT ON FUNCTION columnar.vacuum(regclass) + IS 'vacuum columnar table'; + +CREATE OR REPLACE FUNCTION columnar.stats( + IN regclass, + OUT stripeId bigint, + OUT fileOffset bigint, + OUT rowCount integer, + OUT deletedRows integer, + OUT chunkCount integer, + OUT dataLength integer +) RETURNS SETOF record +LANGUAGE c +AS 'MODULE_PATHNAME', $$columnar_stats$$; diff --git a/columnar/src/backend/columnar/sql/udfs/vacuum/latest.sql b/columnar/src/backend/columnar/sql/udfs/vacuum/latest.sql new file mode 100644 index 00000000..ec710551 --- /dev/null +++ b/columnar/src/backend/columnar/sql/udfs/vacuum/latest.sql @@ -0,0 +1,19 @@ +CREATE OR REPLACE FUNCTION columnar.vacuum(rel regclass) + RETURNS VOID + STRICT + LANGUAGE c AS 'MODULE_PATHNAME', $$vacuum_columnar_table$$; + +COMMENT ON FUNCTION columnar.vacuum(regclass) + IS 'vacuum columnar table'; + +CREATE OR REPLACE FUNCTION columnar.stats( + IN regclass, + OUT stripeId bigint, + OUT fileOffset bigint, + OUT rowCount integer, + OUT deletedRows integer, + OUT chunkCount integer, + OUT dataLength integer +) RETURNS SETOF record +LANGUAGE c +AS 'MODULE_PATHNAME', $$columnar_stats$$; diff --git a/columnar/src/include/columnar/columnar_metadata.h b/columnar/src/include/columnar/columnar_metadata.h index 6405cfdd..16d9641d 100644 --- a/columnar/src/include/columnar/columnar_metadata.h +++ b/columnar/src/include/columnar/columnar_metadata.h @@ -56,5 +56,7 @@ extern uint32 DeletedRowsForStripe(RelFileNode relfilenode, uint32 chunkCount, uint64 stripeId); extern void ColumnarStorageUpdateIfNeeded(Relation rel, bool isUpgrade); +extern StripeMetadata * RewriteStripeMetadataRowWithNewValues(Relation rel, uint64 stripeId, + uint64 sizeBytes, uint64 fileOffset, uint64 rowCount, uint64 chunkCount); #endif /* COLUMNAR_METADATA_H */ diff --git a/columnar/src/test/regress/Makefile b/columnar/src/test/regress/Makefile index 121b5908..2e3d219e 100644 --- a/columnar/src/test/regress/Makefile +++ b/columnar/src/test/regress/Makefile @@ -13,6 +13,9 @@ output_files := $(patsubst $(citus_abs_srcdir)/output/%.source,expected/%.out, $ check-all: check-regression-columnar check-regression-columnar: + echo "======================================================" + echo $(pg_regress_check) + echo "======================================================" $(pg_regress_check) \ --temp-config columnar_regression.conf \ --load-extension=columnar \ diff --git a/columnar/src/test/regress/columnar_schedule b/columnar/src/test/regress/columnar_schedule index 98e12ae0..c860f21a 100644 --- a/columnar/src/test/regress/columnar_schedule +++ b/columnar/src/test/regress/columnar_schedule @@ -21,6 +21,7 @@ test: columnar_lz4 columnar_zstd test: columnar_rollback test: columnar_truncate test: columnar_vacuum +test: columnar_vacuum_udf test: columnar_clean test: columnar_types_without_comparison #test: columnar_chunk_filtering diff --git a/columnar/src/test/regress/expected/columnar_vacuum_udf.out b/columnar/src/test/regress/expected/columnar_vacuum_udf.out new file mode 100644 index 00000000..742f2497 --- /dev/null +++ b/columnar/src/test/regress/expected/columnar_vacuum_udf.out @@ -0,0 +1,245 @@ +CREATE TABLE t1(a int, b int) USING columnar; +CREATE TABLE t2(a int, b int) USING columnar; +INSERT INTO t1 SELECT generate_series(1, 1000000, 1) AS a, generate_series(2, 2000000, 2) AS b; +INSERT INTO t2 SELECT generate_series(1, 2000000, 1) AS a, generate_series(2, 4000000, 2) AS b; +SELECT * FROM columnar.stats('t1'::regclass); + stripeid | fileoffset | rowcount | deletedrows | chunkcount | datalength +----------+------------+----------+-------------+------------+------------ + 1 | 16336 | 150000 | 0 | 15 | 788928 + 2 | 808632 | 150000 | 0 | 15 | 815091 + 3 | 1625432 | 150000 | 0 | 15 | 814184 + 4 | 2442232 | 150000 | 0 | 15 | 813607 + 5 | 3259032 | 150000 | 0 | 15 | 813550 + 6 | 4075832 | 150000 | 0 | 15 | 815135 + 7 | 4892632 | 100000 | 0 | 10 | 543291 +(7 rows) + +SELECT * FROM columnar.stats('t2'::regclass); + stripeid | fileoffset | rowcount | deletedrows | chunkcount | datalength +----------+------------+----------+-------------+------------+------------ + 1 | 16336 | 150000 | 0 | 15 | 788928 + 2 | 808632 | 150000 | 0 | 15 | 815091 + 3 | 1625432 | 150000 | 0 | 15 | 814184 + 4 | 2442232 | 150000 | 0 | 15 | 813607 + 5 | 3259032 | 150000 | 0 | 15 | 813550 + 6 | 4075832 | 150000 | 0 | 15 | 815135 + 7 | 4892632 | 150000 | 0 | 15 | 815582 + 8 | 5709432 | 150000 | 0 | 15 | 812780 + 9 | 6526232 | 150000 | 0 | 15 | 814376 + 10 | 7343032 | 150000 | 0 | 15 | 814484 + 11 | 8159832 | 150000 | 0 | 15 | 815362 + 12 | 8976632 | 150000 | 0 | 15 | 813439 + 13 | 9793432 | 150000 | 0 | 15 | 814608 + 14 | 10610232 | 50000 | 0 | 5 | 272361 +(14 rows) + +SELECT * FROM columnar.vacuum('t1'::regclass); + vacuum +-------- + +(1 row) + +SELECT * FROM columnar.stats('t1'::regclass); + stripeid | fileoffset | rowcount | deletedrows | chunkcount | datalength +----------+------------+----------+-------------+------------+------------ + 1 | 16336 | 150000 | 0 | 15 | 788928 + 2 | 808632 | 150000 | 0 | 15 | 815091 + 3 | 1625432 | 150000 | 0 | 15 | 814184 + 4 | 2442232 | 150000 | 0 | 15 | 813607 + 5 | 3259032 | 150000 | 0 | 15 | 813550 + 6 | 4075832 | 150000 | 0 | 15 | 815135 + 7 | 4892632 | 100000 | 0 | 10 | 543291 +(7 rows) + +SELECT * FROM columnar.stats('t2'::regclass); + stripeid | fileoffset | rowcount | deletedrows | chunkcount | datalength +----------+------------+----------+-------------+------------+------------ + 1 | 16336 | 150000 | 0 | 15 | 788928 + 2 | 808632 | 150000 | 0 | 15 | 815091 + 3 | 1625432 | 150000 | 0 | 15 | 814184 + 4 | 2442232 | 150000 | 0 | 15 | 813607 + 5 | 3259032 | 150000 | 0 | 15 | 813550 + 6 | 4075832 | 150000 | 0 | 15 | 815135 + 7 | 4892632 | 150000 | 0 | 15 | 815582 + 8 | 5709432 | 150000 | 0 | 15 | 812780 + 9 | 6526232 | 150000 | 0 | 15 | 814376 + 10 | 7343032 | 150000 | 0 | 15 | 814484 + 11 | 8159832 | 150000 | 0 | 15 | 815362 + 12 | 8976632 | 150000 | 0 | 15 | 813439 + 13 | 9793432 | 150000 | 0 | 15 | 814608 + 14 | 10610232 | 50000 | 0 | 5 | 272361 +(14 rows) + +SELECT * FROM columnar.vacuum('t2'::regclass); + vacuum +-------- + +(1 row) + +SELECT * FROM columnar.stats('t1'::regclass); + stripeid | fileoffset | rowcount | deletedrows | chunkcount | datalength +----------+------------+----------+-------------+------------+------------ + 1 | 16336 | 150000 | 0 | 15 | 788928 + 2 | 808632 | 150000 | 0 | 15 | 815091 + 3 | 1625432 | 150000 | 0 | 15 | 814184 + 4 | 2442232 | 150000 | 0 | 15 | 813607 + 5 | 3259032 | 150000 | 0 | 15 | 813550 + 6 | 4075832 | 150000 | 0 | 15 | 815135 + 7 | 4892632 | 100000 | 0 | 10 | 543291 +(7 rows) + +SELECT * FROM columnar.stats('t2'::regclass); + stripeid | fileoffset | rowcount | deletedrows | chunkcount | datalength +----------+------------+----------+-------------+------------+------------ + 1 | 16336 | 150000 | 0 | 15 | 788928 + 2 | 808632 | 150000 | 0 | 15 | 815091 + 3 | 1625432 | 150000 | 0 | 15 | 814184 + 4 | 2442232 | 150000 | 0 | 15 | 813607 + 5 | 3259032 | 150000 | 0 | 15 | 813550 + 6 | 4075832 | 150000 | 0 | 15 | 815135 + 7 | 4892632 | 150000 | 0 | 15 | 815582 + 8 | 5709432 | 150000 | 0 | 15 | 812780 + 9 | 6526232 | 150000 | 0 | 15 | 814376 + 10 | 7343032 | 150000 | 0 | 15 | 814484 + 11 | 8159832 | 150000 | 0 | 15 | 815362 + 12 | 8976632 | 150000 | 0 | 15 | 813439 + 13 | 9793432 | 150000 | 0 | 15 | 814608 + 15 | 10608040 | 50000 | 0 | 5 | 272361 +(14 rows) + +DELETE FROM t1 WHERE a % 2 = 0; +DELETE FROM t1 WHERE a % 100 = 0; +SELECT * FROM columnar.stats('t1'::regclass); + stripeid | fileoffset | rowcount | deletedrows | chunkcount | datalength +----------+------------+----------+-------------+------------+------------ + 1 | 16336 | 150000 | 75000 | 15 | 788928 + 2 | 808632 | 150000 | 75000 | 15 | 815091 + 3 | 1625432 | 150000 | 75000 | 15 | 814184 + 4 | 2442232 | 150000 | 75000 | 15 | 813607 + 5 | 3259032 | 150000 | 75000 | 15 | 813550 + 6 | 4075832 | 150000 | 75000 | 15 | 815135 + 7 | 4892632 | 100000 | 50000 | 10 | 543291 +(7 rows) + +SELECT * FROM columnar.stats('t2'::regclass); + stripeid | fileoffset | rowcount | deletedrows | chunkcount | datalength +----------+------------+----------+-------------+------------+------------ + 1 | 16336 | 150000 | 0 | 15 | 788928 + 2 | 808632 | 150000 | 0 | 15 | 815091 + 3 | 1625432 | 150000 | 0 | 15 | 814184 + 4 | 2442232 | 150000 | 0 | 15 | 813607 + 5 | 3259032 | 150000 | 0 | 15 | 813550 + 6 | 4075832 | 150000 | 0 | 15 | 815135 + 7 | 4892632 | 150000 | 0 | 15 | 815582 + 8 | 5709432 | 150000 | 0 | 15 | 812780 + 9 | 6526232 | 150000 | 0 | 15 | 814376 + 10 | 7343032 | 150000 | 0 | 15 | 814484 + 11 | 8159832 | 150000 | 0 | 15 | 815362 + 12 | 8976632 | 150000 | 0 | 15 | 813439 + 13 | 9793432 | 150000 | 0 | 15 | 814608 + 15 | 10608040 | 50000 | 0 | 5 | 272361 +(14 rows) + +SELECT * FROM columnar.vacuum('t1'::regclass); + vacuum +-------- + +(1 row) + +SELECT * FROM columnar.vacuum('t2'::regclass); + vacuum +-------- + +(1 row) + +SELECT * FROM columnar.stats('t1'::regclass); + stripeid | fileoffset | rowcount | deletedrows | chunkcount | datalength +----------+------------+----------+-------------+------------+------------ + 8 | 16336 | 150000 | 0 | 15 | 808748 + 9 | 825084 | 150000 | 0 | 15 | 822409 + 10 | 1647493 | 150000 | 0 | 15 | 821020 + 11 | 2468513 | 50000 | 0 | 5 | 273902 +(4 rows) + +SELECT * FROM columnar.stats('t2'::regclass); + stripeid | fileoffset | rowcount | deletedrows | chunkcount | datalength +----------+------------+----------+-------------+------------+------------ + 1 | 16336 | 150000 | 0 | 15 | 788928 + 2 | 808632 | 150000 | 0 | 15 | 815091 + 3 | 1625432 | 150000 | 0 | 15 | 814184 + 4 | 2442232 | 150000 | 0 | 15 | 813607 + 5 | 3259032 | 150000 | 0 | 15 | 813550 + 6 | 4075832 | 150000 | 0 | 15 | 815135 + 7 | 4892632 | 150000 | 0 | 15 | 815582 + 8 | 5709432 | 150000 | 0 | 15 | 812780 + 9 | 6526232 | 150000 | 0 | 15 | 814376 + 10 | 7343032 | 150000 | 0 | 15 | 814484 + 11 | 8159832 | 150000 | 0 | 15 | 815362 + 12 | 8976632 | 150000 | 0 | 15 | 813439 + 13 | 9793432 | 150000 | 0 | 15 | 814608 + 16 | 10608040 | 50000 | 0 | 5 | 272361 +(14 rows) + +SELECT COUNT(*) FROM t1; + count +-------- + 500000 +(1 row) + +SELECT COUNT(*) FROM t2; + count +--------- + 2000000 +(1 row) + +SELECT SUM(a), SUM(b) FROM t1; + sum | sum +--------------+-------------- + 250000000000 | 500000000000 +(1 row) + +SELECT SUM(a), SUM(b) FROM t2; + sum | sum +---------------+--------------- + 2000001000000 | 4000002000000 +(1 row) + +INSERT INTO t1 SELECT generate_series(1000001, 2000000, 1) AS a, generate_series(2000002, 4000000, 2) AS b; +-- should show no holes between the previous insert and this insert +SELECT * FROM columnar.stats('t1'::regclass); + stripeid | fileoffset | rowcount | deletedrows | chunkcount | datalength +----------+------------+----------+-------------+------------+------------ + 8 | 16336 | 150000 | 0 | 15 | 808748 + 9 | 825084 | 150000 | 0 | 15 | 822409 + 10 | 1647493 | 150000 | 0 | 15 | 821020 + 11 | 2468513 | 50000 | 0 | 5 | 273902 + 12 | 8184336 | 150000 | 0 | 15 | 815176 + 13 | 9001136 | 150000 | 0 | 15 | 813187 + 14 | 9817936 | 150000 | 0 | 15 | 814516 + 15 | 10634736 | 150000 | 0 | 15 | 814283 + 16 | 11451536 | 150000 | 0 | 15 | 815497 + 17 | 12268336 | 150000 | 0 | 15 | 813704 + 18 | 13085136 | 100000 | 0 | 10 | 543338 +(11 rows) + +DELETE FROM t1 WHERE a BETWEEN 500000 AND 1500000; +SELECT * FROM columnar.vacuum('t1'::regclass); + vacuum +-------- + +(1 row) + +-- should show no holes +SELECT * FROM columnar.stats('t1'::regclass); + stripeid | fileoffset | rowcount | deletedrows | chunkcount | datalength +----------+------------+----------+-------------+------------+------------ + 8 | 16336 | 150000 | 0 | 15 | 808748 + 16 | 825084 | 150000 | 0 | 15 | 815497 + 17 | 1640581 | 150000 | 0 | 15 | 813704 + 18 | 2454285 | 100000 | 0 | 10 | 543338 + 19 | 2997623 | 150000 | 0 | 15 | 819245 + 20 | 3816868 | 50000 | 0 | 5 | 271619 +(6 rows) + +DROP TABLE t1; +DROP TABLE t2; diff --git a/columnar/src/test/regress/expected/columnar_vacuum_udf_1.out b/columnar/src/test/regress/expected/columnar_vacuum_udf_1.out new file mode 100644 index 00000000..55254d5d --- /dev/null +++ b/columnar/src/test/regress/expected/columnar_vacuum_udf_1.out @@ -0,0 +1,245 @@ +CREATE TABLE t1(a int, b int) USING columnar; +CREATE TABLE t2(a int, b int) USING columnar; +INSERT INTO t1 SELECT generate_series(1, 1000000, 1) AS a, generate_series(2, 2000000, 2) AS b; +INSERT INTO t2 SELECT generate_series(1, 2000000, 1) AS a, generate_series(2, 4000000, 2) AS b; +SELECT * FROM columnar.stats('t1'::regclass); + stripeid | fileoffset | rowcount | deletedrows | chunkcount | datalength +----------+------------+----------+-------------+------------+------------ + 1 | 16336 | 150000 | 0 | 15 | 789006 + 2 | 808632 | 150000 | 0 | 15 | 815093 + 3 | 1625432 | 150000 | 0 | 15 | 814187 + 4 | 2442232 | 150000 | 0 | 15 | 813607 + 5 | 3259032 | 150000 | 0 | 15 | 813556 + 6 | 4075832 | 150000 | 0 | 15 | 815135 + 7 | 4892632 | 100000 | 0 | 10 | 543291 +(7 rows) + +SELECT * FROM columnar.stats('t2'::regclass); + stripeid | fileoffset | rowcount | deletedrows | chunkcount | datalength +----------+------------+----------+-------------+------------+------------ + 1 | 16336 | 150000 | 0 | 15 | 789006 + 2 | 808632 | 150000 | 0 | 15 | 815093 + 3 | 1625432 | 150000 | 0 | 15 | 814187 + 4 | 2442232 | 150000 | 0 | 15 | 813607 + 5 | 3259032 | 150000 | 0 | 15 | 813556 + 6 | 4075832 | 150000 | 0 | 15 | 815135 + 7 | 4892632 | 150000 | 0 | 15 | 815582 + 8 | 5709432 | 150000 | 0 | 15 | 812781 + 9 | 6526232 | 150000 | 0 | 15 | 814379 + 10 | 7343032 | 150000 | 0 | 15 | 814489 + 11 | 8159832 | 150000 | 0 | 15 | 815362 + 12 | 8976632 | 150000 | 0 | 15 | 813441 + 13 | 9793432 | 150000 | 0 | 15 | 814604 + 14 | 10610232 | 50000 | 0 | 5 | 272361 +(14 rows) + +SELECT * FROM columnar.vacuum('t1'::regclass); + vacuum +-------- + +(1 row) + +SELECT * FROM columnar.stats('t1'::regclass); + stripeid | fileoffset | rowcount | deletedrows | chunkcount | datalength +----------+------------+----------+-------------+------------+------------ + 1 | 16336 | 150000 | 0 | 15 | 789006 + 2 | 808632 | 150000 | 0 | 15 | 815093 + 3 | 1625432 | 150000 | 0 | 15 | 814187 + 4 | 2442232 | 150000 | 0 | 15 | 813607 + 5 | 3259032 | 150000 | 0 | 15 | 813556 + 6 | 4075832 | 150000 | 0 | 15 | 815135 + 7 | 4892632 | 100000 | 0 | 10 | 543291 +(7 rows) + +SELECT * FROM columnar.stats('t2'::regclass); + stripeid | fileoffset | rowcount | deletedrows | chunkcount | datalength +----------+------------+----------+-------------+------------+------------ + 1 | 16336 | 150000 | 0 | 15 | 789006 + 2 | 808632 | 150000 | 0 | 15 | 815093 + 3 | 1625432 | 150000 | 0 | 15 | 814187 + 4 | 2442232 | 150000 | 0 | 15 | 813607 + 5 | 3259032 | 150000 | 0 | 15 | 813556 + 6 | 4075832 | 150000 | 0 | 15 | 815135 + 7 | 4892632 | 150000 | 0 | 15 | 815582 + 8 | 5709432 | 150000 | 0 | 15 | 812781 + 9 | 6526232 | 150000 | 0 | 15 | 814379 + 10 | 7343032 | 150000 | 0 | 15 | 814489 + 11 | 8159832 | 150000 | 0 | 15 | 815362 + 12 | 8976632 | 150000 | 0 | 15 | 813441 + 13 | 9793432 | 150000 | 0 | 15 | 814604 + 14 | 10610232 | 50000 | 0 | 5 | 272361 +(14 rows) + +SELECT * FROM columnar.vacuum('t2'::regclass); + vacuum +-------- + +(1 row) + +SELECT * FROM columnar.stats('t1'::regclass); + stripeid | fileoffset | rowcount | deletedrows | chunkcount | datalength +----------+------------+----------+-------------+------------+------------ + 1 | 16336 | 150000 | 0 | 15 | 789006 + 2 | 808632 | 150000 | 0 | 15 | 815093 + 3 | 1625432 | 150000 | 0 | 15 | 814187 + 4 | 2442232 | 150000 | 0 | 15 | 813607 + 5 | 3259032 | 150000 | 0 | 15 | 813556 + 6 | 4075832 | 150000 | 0 | 15 | 815135 + 7 | 4892632 | 100000 | 0 | 10 | 543291 +(7 rows) + +SELECT * FROM columnar.stats('t2'::regclass); + stripeid | fileoffset | rowcount | deletedrows | chunkcount | datalength +----------+------------+----------+-------------+------------+------------ + 1 | 16336 | 150000 | 0 | 15 | 789006 + 2 | 808632 | 150000 | 0 | 15 | 815093 + 3 | 1625432 | 150000 | 0 | 15 | 814187 + 4 | 2442232 | 150000 | 0 | 15 | 813607 + 5 | 3259032 | 150000 | 0 | 15 | 813556 + 6 | 4075832 | 150000 | 0 | 15 | 815135 + 7 | 4892632 | 150000 | 0 | 15 | 815582 + 8 | 5709432 | 150000 | 0 | 15 | 812781 + 9 | 6526232 | 150000 | 0 | 15 | 814379 + 10 | 7343032 | 150000 | 0 | 15 | 814489 + 11 | 8159832 | 150000 | 0 | 15 | 815362 + 12 | 8976632 | 150000 | 0 | 15 | 813441 + 13 | 9793432 | 150000 | 0 | 15 | 814604 + 15 | 10608036 | 50000 | 0 | 5 | 272361 +(14 rows) + +DELETE FROM t1 WHERE a % 2 = 0; +DELETE FROM t1 WHERE a % 100 = 0; +SELECT * FROM columnar.stats('t1'::regclass); + stripeid | fileoffset | rowcount | deletedrows | chunkcount | datalength +----------+------------+----------+-------------+------------+------------ + 1 | 16336 | 150000 | 75000 | 15 | 789006 + 2 | 808632 | 150000 | 75000 | 15 | 815093 + 3 | 1625432 | 150000 | 75000 | 15 | 814187 + 4 | 2442232 | 150000 | 75000 | 15 | 813607 + 5 | 3259032 | 150000 | 75000 | 15 | 813556 + 6 | 4075832 | 150000 | 75000 | 15 | 815135 + 7 | 4892632 | 100000 | 50000 | 10 | 543291 +(7 rows) + +SELECT * FROM columnar.stats('t2'::regclass); + stripeid | fileoffset | rowcount | deletedrows | chunkcount | datalength +----------+------------+----------+-------------+------------+------------ + 1 | 16336 | 150000 | 0 | 15 | 789006 + 2 | 808632 | 150000 | 0 | 15 | 815093 + 3 | 1625432 | 150000 | 0 | 15 | 814187 + 4 | 2442232 | 150000 | 0 | 15 | 813607 + 5 | 3259032 | 150000 | 0 | 15 | 813556 + 6 | 4075832 | 150000 | 0 | 15 | 815135 + 7 | 4892632 | 150000 | 0 | 15 | 815582 + 8 | 5709432 | 150000 | 0 | 15 | 812781 + 9 | 6526232 | 150000 | 0 | 15 | 814379 + 10 | 7343032 | 150000 | 0 | 15 | 814489 + 11 | 8159832 | 150000 | 0 | 15 | 815362 + 12 | 8976632 | 150000 | 0 | 15 | 813441 + 13 | 9793432 | 150000 | 0 | 15 | 814604 + 15 | 10608036 | 50000 | 0 | 5 | 272361 +(14 rows) + +SELECT * FROM columnar.vacuum('t1'::regclass); + vacuum +-------- + +(1 row) + +SELECT * FROM columnar.vacuum('t2'::regclass); + vacuum +-------- + +(1 row) + +SELECT * FROM columnar.stats('t1'::regclass); + stripeid | fileoffset | rowcount | deletedrows | chunkcount | datalength +----------+------------+----------+-------------+------------+------------ + 8 | 16336 | 150000 | 0 | 15 | 808747 + 9 | 825083 | 150000 | 0 | 15 | 822408 + 10 | 1647491 | 150000 | 0 | 15 | 821023 + 11 | 2468514 | 50000 | 0 | 5 | 273901 +(4 rows) + +SELECT * FROM columnar.stats('t2'::regclass); + stripeid | fileoffset | rowcount | deletedrows | chunkcount | datalength +----------+------------+----------+-------------+------------+------------ + 1 | 16336 | 150000 | 0 | 15 | 789006 + 2 | 808632 | 150000 | 0 | 15 | 815093 + 3 | 1625432 | 150000 | 0 | 15 | 814187 + 4 | 2442232 | 150000 | 0 | 15 | 813607 + 5 | 3259032 | 150000 | 0 | 15 | 813556 + 6 | 4075832 | 150000 | 0 | 15 | 815135 + 7 | 4892632 | 150000 | 0 | 15 | 815582 + 8 | 5709432 | 150000 | 0 | 15 | 812781 + 9 | 6526232 | 150000 | 0 | 15 | 814379 + 10 | 7343032 | 150000 | 0 | 15 | 814489 + 11 | 8159832 | 150000 | 0 | 15 | 815362 + 12 | 8976632 | 150000 | 0 | 15 | 813441 + 13 | 9793432 | 150000 | 0 | 15 | 814604 + 16 | 10608036 | 50000 | 0 | 5 | 272361 +(14 rows) + +SELECT COUNT(*) FROM t1; + count +-------- + 500000 +(1 row) + +SELECT COUNT(*) FROM t2; + count +--------- + 2000000 +(1 row) + +SELECT SUM(a), SUM(b) FROM t1; + sum | sum +--------------+-------------- + 250000000000 | 500000000000 +(1 row) + +SELECT SUM(a), SUM(b) FROM t2; + sum | sum +---------------+--------------- + 2000001000000 | 4000002000000 +(1 row) + +INSERT INTO t1 SELECT generate_series(1000001, 2000000, 1) AS a, generate_series(2000002, 4000000, 2) AS b; +-- should show no holes between the previous insert and this insert +SELECT * FROM columnar.stats('t1'::regclass); + stripeid | fileoffset | rowcount | deletedrows | chunkcount | datalength +----------+------------+----------+-------------+------------+------------ + 8 | 16336 | 150000 | 0 | 15 | 808747 + 9 | 825083 | 150000 | 0 | 15 | 822408 + 10 | 1647491 | 150000 | 0 | 15 | 821023 + 11 | 2468514 | 50000 | 0 | 5 | 273901 + 12 | 8184336 | 150000 | 0 | 15 | 815179 + 13 | 9001136 | 150000 | 0 | 15 | 813188 + 14 | 9817936 | 150000 | 0 | 15 | 814519 + 15 | 10634736 | 150000 | 0 | 15 | 814286 + 16 | 11451536 | 150000 | 0 | 15 | 815497 + 17 | 12268336 | 150000 | 0 | 15 | 813703 + 18 | 13085136 | 100000 | 0 | 10 | 543336 +(11 rows) + +DELETE FROM t1 WHERE a BETWEEN 500000 AND 1500000; +SELECT * FROM columnar.vacuum('t1'::regclass); + vacuum +-------- + +(1 row) + +-- should show no holes +SELECT * FROM columnar.stats('t1'::regclass); + stripeid | fileoffset | rowcount | deletedrows | chunkcount | datalength +----------+------------+----------+-------------+------------+------------ + 8 | 16336 | 150000 | 0 | 15 | 808747 + 16 | 825083 | 150000 | 0 | 15 | 815497 + 17 | 1640580 | 150000 | 0 | 15 | 813703 + 18 | 2454283 | 100000 | 0 | 10 | 543336 + 19 | 2997619 | 150000 | 0 | 15 | 819249 + 20 | 3816868 | 50000 | 0 | 5 | 271618 +(6 rows) + +DROP TABLE t1; +DROP TABLE t2; diff --git a/columnar/src/test/regress/sql/columnar_vacuum_udf.sql b/columnar/src/test/regress/sql/columnar_vacuum_udf.sql new file mode 100644 index 00000000..ecdb90b4 --- /dev/null +++ b/columnar/src/test/regress/sql/columnar_vacuum_udf.sql @@ -0,0 +1,50 @@ +CREATE TABLE t1(a int, b int) USING columnar; + +CREATE TABLE t2(a int, b int) USING columnar; + +INSERT INTO t1 SELECT generate_series(1, 1000000, 1) AS a, generate_series(2, 2000000, 2) AS b; +INSERT INTO t2 SELECT generate_series(1, 2000000, 1) AS a, generate_series(2, 4000000, 2) AS b; + +SELECT * FROM columnar.stats('t1'::regclass); +SELECT * FROM columnar.stats('t2'::regclass); + +SELECT * FROM columnar.vacuum('t1'::regclass); + +SELECT * FROM columnar.stats('t1'::regclass); +SELECT * FROM columnar.stats('t2'::regclass); + +SELECT * FROM columnar.vacuum('t2'::regclass); + +SELECT * FROM columnar.stats('t1'::regclass); +SELECT * FROM columnar.stats('t2'::regclass); + +DELETE FROM t1 WHERE a % 2 = 0; +DELETE FROM t1 WHERE a % 100 = 0; + +SELECT * FROM columnar.stats('t1'::regclass); +SELECT * FROM columnar.stats('t2'::regclass); + +SELECT * FROM columnar.vacuum('t1'::regclass); +SELECT * FROM columnar.vacuum('t2'::regclass); + +SELECT * FROM columnar.stats('t1'::regclass); +SELECT * FROM columnar.stats('t2'::regclass); + +SELECT COUNT(*) FROM t1; +SELECT COUNT(*) FROM t2; + +SELECT SUM(a), SUM(b) FROM t1; +SELECT SUM(a), SUM(b) FROM t2; + +INSERT INTO t1 SELECT generate_series(1000001, 2000000, 1) AS a, generate_series(2000002, 4000000, 2) AS b; +-- should show no holes between the previous insert and this insert +SELECT * FROM columnar.stats('t1'::regclass); + +DELETE FROM t1 WHERE a BETWEEN 500000 AND 1500000; + +SELECT * FROM columnar.vacuum('t1'::regclass); +-- should show no holes +SELECT * FROM columnar.stats('t1'::regclass); + +DROP TABLE t1; +DROP TABLE t2;