/* * Performs a compaction of an append-only relation. * * In non-utility mode, all compaction segment files should be * marked as in-use/in-compaction in the appendonlywriter.c code. If * set, the insert_segno should also be marked as in-use. * When the insert segno is negative, only truncate to eof operations * can be executed. * * The caller is required to hold either an AccessExclusiveLock (vacuum full) * or a ShareLock on the relation. */ void AppendOnlyCompact(Relation aorel, List* compaction_segno, int insert_segno, bool isFull) { const char* relname; int total_segfiles; FileSegInfo** segfile_array; AppendOnlyInsertDesc insertDesc = NULL; int i, segno; FileSegInfo* fsinfo; Assert (Gp_role == GP_ROLE_EXECUTE || Gp_role == GP_ROLE_UTILITY); Assert(insert_segno >= 0); relname = RelationGetRelationName(aorel); AppendOnlyEntry *aoEntry = GetAppendOnlyEntry(RelationGetRelid(aorel), SnapshotNow); elogif (Debug_appendonly_print_compaction, LOG, "Compact AO relation %s", relname); /* Get information about all the file segments we need to scan */ segfile_array = GetAllFileSegInfo(aorel, aoEntry, SnapshotNow, &total_segfiles); insertDesc = appendonly_insert_init(aorel, SnapshotNow, insert_segno, false); for(i = 0 ; i < total_segfiles ; i++) { segno = segfile_array[i]->segno; if (list_find_int(compaction_segno, segno) < 0) { continue; } if (segno == insert_segno) { /* We cannot compact the segment file we are inserting to. */ continue; } /* * Try to get the transaction write-lock for the Append-Only segment file. * * NOTE: This is a transaction scope lock that must be held until commit / abort. */ LockRelationAppendOnlySegmentFile( &aorel->rd_node, segfile_array[i]->segno, AccessExclusiveLock, false); /* Re-fetch under the write lock to get latest committed eof. */ fsinfo = GetFileSegInfo(aorel, aoEntry, SnapshotNow, segno); /* * This should not occur since this segfile info was found by the * "all" method, but better to catch for trouble shooting * (possibly index corruption?) */ if (fsinfo == NULL) elog(ERROR, "file seginfo for AO relation %s %u/%u/%u (segno=%u) is missing", relname, aorel->rd_node.spcNode, aorel->rd_node.dbNode, aorel->rd_node.relNode, segno); if (AppendOnlyCompaction_ShouldCompact(aorel, aoEntry, fsinfo->segno, fsinfo->total_tupcount, isFull)) { AppendOnlySegmentFileFullCompaction(aorel, aoEntry, insertDesc, fsinfo); } pfree(fsinfo); } appendonly_insert_finish(insertDesc); pfree(aoEntry); if (segfile_array) { FreeAllSegFileInfo(segfile_array, total_segfiles); pfree(segfile_array); } }
/* * Performs a compaction of an append-only relation. * * In non-utility mode, all compaction segment files should be * marked as in-use/in-compaction in the appendonlywriter.c code. * */ void AppendOnlyDrop(Relation aorel, List* compaction_segno) { const char* relname; int total_segfiles; FileSegInfo** segfile_array; int i, segno; FileSegInfo* fsinfo; Assert (Gp_role == GP_ROLE_EXECUTE || Gp_role == GP_ROLE_UTILITY); Assert (RelationIsAoRows(aorel)); relname = RelationGetRelationName(aorel); AppendOnlyEntry *aoEntry = GetAppendOnlyEntry(RelationGetRelid(aorel), SnapshotNow); elogif (Debug_appendonly_print_compaction, LOG, "Drop AO relation %s", relname); /* Get information about all the file segments we need to scan */ segfile_array = GetAllFileSegInfo(aorel, aoEntry, SnapshotNow, &total_segfiles); for(i = 0 ; i < total_segfiles ; i++) { segno = segfile_array[i]->segno; if (list_find_int(compaction_segno, segno) < 0) { continue; } /* * Try to get the transaction write-lock for the Append-Only segment file. * * NOTE: This is a transaction scope lock that must be held until commit / abort. */ LockRelationAppendOnlySegmentFile( &aorel->rd_node, segfile_array[i]->segno, AccessExclusiveLock, false); /* Re-fetch under the write lock to get latest committed eof. */ fsinfo = GetFileSegInfo(aorel, aoEntry, SnapshotNow, segno); if (fsinfo->state == AOSEG_STATE_AWAITING_DROP) { Assert(HasLockForSegmentFileDrop(aorel)); Assert(!HasSerializableBackends(false)); AppendOnlyCompaction_DropSegmentFile(aorel, segno); ClearFileSegInfo(aorel, aoEntry, segno, AOSEG_STATE_DEFAULT); } pfree(fsinfo); } pfree(aoEntry); if (segfile_array) { FreeAllSegFileInfo(segfile_array, total_segfiles); pfree(segfile_array); } }
/* * Truncates each segment file to the AO relation to its EOF. * If we cannot get a lock on the segment file (because e.g. a concurrent insert) * the segment file is skipped. */ void AppendOnlyTruncateToEOF(Relation aorel) { const char* relname; int total_segfiles; FileSegInfo** segfile_array; int i, segno; LockAcquireResult acquireResult; FileSegInfo* fsinfo; Assert (RelationIsAoRows(aorel)); relname = RelationGetRelationName(aorel); AppendOnlyEntry *aoEntry = GetAppendOnlyEntry(RelationGetRelid(aorel), SnapshotNow); elogif (Debug_appendonly_print_compaction, LOG, "Compact AO relation %s", relname); /* Get information about all the file segments we need to scan */ segfile_array = GetAllFileSegInfo(aorel, aoEntry, SnapshotNow, &total_segfiles); for(i = 0 ; i < total_segfiles ; i++) { segno = segfile_array[i]->segno; /* * Try to get the transaction write-lock for the Append-Only segment file. * * NOTE: This is a transaction scope lock that must be held until commit / abort. */ acquireResult = LockRelationAppendOnlySegmentFile( &aorel->rd_node, segfile_array[i]->segno, AccessExclusiveLock, /* dontWait */ true); if (acquireResult == LOCKACQUIRE_NOT_AVAIL) { elog(DEBUG5, "truncate skips AO segfile %d, " "relation %s", segfile_array[i]->segno, relname); continue; } /* Re-fetch under the write lock to get latest committed eof. */ fsinfo = GetFileSegInfo(aorel, aoEntry, SnapshotNow, segno); /* * This should not occur since this segfile info was found by the * "all" method, but better to catch for trouble shooting * (possibly index corruption?) */ if (fsinfo == NULL) elog(ERROR, "file seginfo for AO relation %s %u/%u/%u (segno=%u) is missing", relname, aorel->rd_node.spcNode, aorel->rd_node.dbNode, aorel->rd_node.relNode, segno); AppendOnlySegmentFileTruncateToEOF(aorel, fsinfo); pfree(fsinfo); } pfree(aoEntry); if (segfile_array) { FreeAllSegFileInfo(segfile_array, total_segfiles); pfree(segfile_array); } }
static Datum gp_aovisimap_hidden_info_internal(PG_FUNCTION_ARGS, Oid aoRelOid) { Datum values[3]; bool nulls[3]; HeapTuple tuple; Datum result; typedef struct Context { AppendOnlyVisimap visiMap; Relation parentRelation; FileSegInfo **appendonlySegfileInfo; AOCSFileSegInfo **aocsSegfileInfo; int segfile_info_total; int i; } Context; FuncCallContext *funcctx; Context *context; if (SRF_IS_FIRSTCALL()) { TupleDesc tupdesc; MemoryContext oldcontext; /* create a function context for cross-call persistence */ funcctx = SRF_FIRSTCALL_INIT(); /* * switch to memory context appropriate for multiple function * calls */ oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); /* build tupdesc for result tuples */ tupdesc = CreateTemplateTupleDesc(3, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "segno", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "hidden_tupcount", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 3, "total_tupcount", INT8OID, -1, 0); funcctx->tuple_desc = BlessTupleDesc(tupdesc); /* * Collect all the locking information that we will format and send * out as a result set. */ context = (Context *) palloc0(sizeof(Context)); context->parentRelation = heap_open(aoRelOid, AccessShareLock); if (!(RelationIsAoRows(context->parentRelation) || RelationIsAoCols(context->parentRelation))) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Function not supported on relation"))); } if (RelationIsAoRows(context->parentRelation)) { context->appendonlySegfileInfo = GetAllFileSegInfo( context->parentRelation, SnapshotNow, &context->segfile_info_total); } else { Assert(RelationIsAoCols(context->parentRelation)); context->aocsSegfileInfo = GetAllAOCSFileSegInfo(context->parentRelation, SnapshotNow, &context->segfile_info_total); } context->i = 0; AppendOnlyVisimap_Init(&context->visiMap, context->parentRelation->rd_appendonly->visimaprelid, context->parentRelation->rd_appendonly->visimapidxid, AccessShareLock, SnapshotNow); funcctx->user_fctx = (void *) context; MemoryContextSwitchTo(oldcontext); } funcctx = SRF_PERCALL_SETUP(); context = (Context *) funcctx->user_fctx; while (context->i < context->segfile_info_total) { int64 tupcount; int segno; if (context->appendonlySegfileInfo) { FileSegInfo *fsinfo = context->appendonlySegfileInfo[context->i]; tupcount = fsinfo->total_tupcount; segno = fsinfo->segno; } else if (context->aocsSegfileInfo) { AOCSFileSegInfo *fsinfo = context->aocsSegfileInfo[context->i]; tupcount = fsinfo->total_tupcount; segno = fsinfo->segno; } else { Insist(false); } MemSet(values, 0, sizeof(values)); MemSet(nulls, false, sizeof(nulls)); values[0] = Int32GetDatum(segno); values[1] = Int64GetDatum(AppendOnlyVisimap_GetSegmentFileHiddenTupleCount( &context->visiMap, segno)); values[2] = Int64GetDatum(tupcount); tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls); result = HeapTupleGetDatum(tuple); context->i++; SRF_RETURN_NEXT(funcctx, result); } AppendOnlyVisimap_Finish(&context->visiMap, AccessShareLock); if (context->appendonlySegfileInfo) { FreeAllSegFileInfo(context->appendonlySegfileInfo, context->segfile_info_total); pfree(context->appendonlySegfileInfo); context->appendonlySegfileInfo = NULL; } if (context->aocsSegfileInfo) { FreeAllAOCSSegFileInfo(context->aocsSegfileInfo, context->segfile_info_total); pfree(context->aocsSegfileInfo); context->aocsSegfileInfo = NULL; } heap_close(context->parentRelation, AccessShareLock); pfree(context); funcctx->user_fctx = NULL; SRF_RETURN_DONE(funcctx); }