/*------------------------------------------------------------------------- * * fake_am.c * fake table access method code * * Copyright (c) Citus Data, Inc. * * IDENTIFICATION * Based on https://github.com/michaelpq/pg_plugins/blob/master/blackhole_am/blackhole_am.c * * * NOTES * This file introduces the table access method "fake", which delegates * bare minimum functionality for testing to heapam to provide an append * only access method, and doesn't implement rest of the functionality. * *------------------------------------------------------------------------- */ #include "postgres.h" #include "distributed/pg_version_constants.h" #include "access/amapi.h" #include "access/heapam.h" #include "access/tableam.h" #include "access/multixact.h" #include "access/xact.h" #include "catalog/index.h" #include "catalog/storage.h" #include "catalog/storage_xlog.h" #include "commands/vacuum.h" #include "executor/tuptable.h" #include "storage/smgr.h" #include "utils/snapmgr.h" PG_FUNCTION_INFO_V1(fake_am_handler); static const TableAmRoutine fake_methods; /* ------------------------------------------------------------------------ * Slot related callbacks for fake AM * ------------------------------------------------------------------------ */ static const TupleTableSlotOps * fake_slot_callbacks(Relation relation) { return &TTSOpsBufferHeapTuple; } /* ------------------------------------------------------------------------ * Table Scan Callbacks for fake AM * ------------------------------------------------------------------------ */ static TableScanDesc fake_scan_begin(Relation relation, Snapshot snapshot, int nkeys, ScanKey key, ParallelTableScanDesc parallel_scan, uint32 flags) { return heap_beginscan(relation, snapshot, nkeys, key, parallel_scan, flags); } static void fake_scan_end(TableScanDesc sscan) { heap_endscan(sscan); } static void fake_scan_rescan(TableScanDesc sscan, ScanKey key, bool set_params, bool allow_strat, bool allow_sync, bool allow_pagemode) { heap_rescan(sscan, key, set_params, allow_strat, allow_sync, allow_pagemode); } static bool fake_scan_getnextslot(TableScanDesc sscan, ScanDirection direction, TupleTableSlot *slot) { ereport(WARNING, (errmsg("fake_scan_getnextslot"))); return heap_getnextslot(sscan, direction, slot); } /* ------------------------------------------------------------------------ * Index Scan Callbacks for fake AM * ------------------------------------------------------------------------ */ static IndexFetchTableData * fake_index_fetch_begin(Relation rel) { elog(ERROR, "fake_index_fetch_begin not implemented"); return NULL; } static void fake_index_fetch_reset(IndexFetchTableData *scan) { elog(ERROR, "fake_index_fetch_reset not implemented"); } static void fake_index_fetch_end(IndexFetchTableData *scan) { elog(ERROR, "fake_index_fetch_end not implemented"); } static bool fake_index_fetch_tuple(struct IndexFetchTableData *scan, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot, bool *call_again, bool *all_dead) { elog(ERROR, "fake_index_fetch_tuple not implemented"); return false; } /* ------------------------------------------------------------------------ * Callbacks for non-modifying operations on individual tuples for * fake AM. * ------------------------------------------------------------------------ */ static bool fake_fetch_row_version(Relation relation, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot) { elog(ERROR, "fake_fetch_row_version not implemented"); return false; } static void fake_get_latest_tid(TableScanDesc sscan, ItemPointer tid) { elog(ERROR, "fake_get_latest_tid not implemented"); } static bool fake_tuple_tid_valid(TableScanDesc scan, ItemPointer tid) { elog(ERROR, "fake_tuple_tid_valid not implemented"); return false; } static bool fake_tuple_satisfies_snapshot(Relation rel, TupleTableSlot *slot, Snapshot snapshot) { elog(ERROR, "fake_tuple_satisfies_snapshot not implemented"); return false; } #if PG_VERSION_NUM >= PG_VERSION_14 static TransactionId fake_index_delete_tuples(Relation rel, TM_IndexDeleteOp *delstate) { elog(ERROR, "fake_index_delete_tuples not implemented"); return InvalidTransactionId; } #else static TransactionId fake_compute_xid_horizon_for_tuples(Relation rel, ItemPointerData *tids, int nitems) { elog(ERROR, "fake_compute_xid_horizon_for_tuples not implemented"); return InvalidTransactionId; } #endif /* ---------------------------------------------------------------------------- * Functions for manipulations of physical tuples for fake AM. * ---------------------------------------------------------------------------- */ static void fake_tuple_insert(Relation relation, TupleTableSlot *slot, CommandId cid, int options, BulkInsertState bistate) { ereport(WARNING, (errmsg("fake_tuple_insert"))); /* * Code below this point is from heapam_tuple_insert from * heapam_handler.c */ bool shouldFree = true; HeapTuple tuple = ExecFetchSlotHeapTuple(slot, true, &shouldFree); /* Update the tuple with table oid */ slot->tts_tableOid = RelationGetRelid(relation); tuple->t_tableOid = slot->tts_tableOid; /* Perform the insertion, and copy the resulting ItemPointer */ heap_insert(relation, tuple, cid, options, bistate); ItemPointerCopy(&tuple->t_self, &slot->tts_tid); if (shouldFree) { pfree(tuple); } } static void fake_tuple_insert_speculative(Relation relation, TupleTableSlot *slot, CommandId cid, int options, BulkInsertState bistate, uint32 specToken) { elog(ERROR, "fake_tuple_insert_speculative not implemented"); } static void fake_tuple_complete_speculative(Relation relation, TupleTableSlot *slot, uint32 spekToken, bool succeeded) { elog(ERROR, "fake_tuple_complete_speculative not implemented"); } static void fake_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples, CommandId cid, int options, BulkInsertState bistate) { ereport(WARNING, (errmsg("fake_multi_insert"))); heap_multi_insert(relation, slots, ntuples, cid, options, bistate); } static TM_Result fake_tuple_delete(Relation relation, ItemPointer tid, CommandId cid, Snapshot snapshot, Snapshot crosscheck, bool wait, TM_FailureData *tmfd, bool changingPart) { elog(ERROR, "fake_tuple_delete not implemented"); } static TM_Result fake_tuple_update(Relation relation, ItemPointer otid, TupleTableSlot *slot, CommandId cid, Snapshot snapshot, Snapshot crosscheck, bool wait, TM_FailureData *tmfd, LockTupleMode *lockmode, bool *update_indexes) { elog(ERROR, "fake_tuple_update not implemented"); } static TM_Result fake_tuple_lock(Relation relation, ItemPointer tid, Snapshot snapshot, TupleTableSlot *slot, CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy, uint8 flags, TM_FailureData *tmfd) { elog(ERROR, "fake_tuple_lock not implemented"); } static void fake_finish_bulk_insert(Relation relation, int options) { /* nothing to do here */ } /* ------------------------------------------------------------------------ * DDL related callbacks for fake AM. * ------------------------------------------------------------------------ */ static void fake_relation_set_new_filenode(Relation rel, const RelFileNode *newrnode, char persistence, TransactionId *freezeXid, MultiXactId *minmulti) { /* * Code below is copied from heapam_relation_set_new_filenode in * heapam_handler.c. */ /* * Initialize to the minimum XID that could put tuples in the table. We * know that no xacts older than RecentXmin are still running, so that * will do. */ *freezeXid = RecentXmin; /* * Similarly, initialize the minimum Multixact to the first value that * could possibly be stored in tuples in the table. Running transactions * could reuse values from their local cache, so we are careful to * consider all currently running multis. * * XXX this could be refined further, but is it worth the hassle? */ *minmulti = GetOldestMultiXactId(); SMgrRelation srel = RelationCreateStorage(*newrnode, persistence); /* * If required, set up an init fork for an unlogged table so that it can * be correctly reinitialized on restart. An immediate sync is required * even if the page has been logged, because the write did not go through * shared_buffers and therefore a concurrent checkpoint may have moved the * redo pointer past our xlog record. Recovery may as well remove it * while replaying, for example, XLOG_DBASE_CREATE or XLOG_TBLSPC_CREATE * record. Therefore, logging is necessary even if wal_level=minimal. */ if (persistence == RELPERSISTENCE_UNLOGGED) { Assert(rel->rd_rel->relkind == RELKIND_RELATION || rel->rd_rel->relkind == RELKIND_MATVIEW || rel->rd_rel->relkind == RELKIND_TOASTVALUE); smgrcreate(srel, INIT_FORKNUM, false); log_smgrcreate(newrnode, INIT_FORKNUM); smgrimmedsync(srel, INIT_FORKNUM); } smgrclose(srel); } static void fake_relation_nontransactional_truncate(Relation rel) { elog(ERROR, "fake_relation_nontransactional_truncate not implemented"); } static void fake_copy_data(Relation rel, const RelFileNode *newrnode) { elog(ERROR, "fake_copy_data not implemented"); } static void fake_copy_for_cluster(Relation OldTable, Relation NewTable, Relation OldIndex, bool use_sort, TransactionId OldestXmin, TransactionId *xid_cutoff, MultiXactId *multi_cutoff, double *num_tuples, double *tups_vacuumed, double *tups_recently_dead) { elog(ERROR, "fake_copy_for_cluster not implemented"); } static void fake_vacuum(Relation onerel, VacuumParams *params, BufferAccessStrategy bstrategy) { elog(WARNING, "fake_copy_for_cluster not implemented"); } static bool fake_scan_analyze_next_block(TableScanDesc scan, BlockNumber blockno, BufferAccessStrategy bstrategy) { /* we don't support analyze, so return false */ return false; } static bool fake_scan_analyze_next_tuple(TableScanDesc scan, TransactionId OldestXmin, double *liverows, double *deadrows, TupleTableSlot *slot) { elog(ERROR, "fake_scan_analyze_next_tuple not implemented"); } static double fake_index_build_range_scan(Relation tableRelation, Relation indexRelation, IndexInfo *indexInfo, bool allow_sync, bool anyvisible, bool progress, BlockNumber start_blockno, BlockNumber numblocks, IndexBuildCallback callback, void *callback_state, TableScanDesc scan) { elog(ERROR, "fake_index_build_range_scan not implemented"); } static void fake_index_validate_scan(Relation tableRelation, Relation indexRelation, IndexInfo *indexInfo, Snapshot snapshot, ValidateIndexState *state) { elog(ERROR, "fake_index_build_range_scan not implemented"); } /* ------------------------------------------------------------------------ * Miscellaneous callbacks for the fake AM * ------------------------------------------------------------------------ */ static uint64 fake_relation_size(Relation rel, ForkNumber forkNumber) { /* * Code below is copied from heapam_relation_size from * heapam_handler.c. */ uint64 nblocks = 0; /* Open it at the smgr level if not already done */ RelationOpenSmgr(rel); /* InvalidForkNumber indicates returning the size for all forks */ if (forkNumber == InvalidForkNumber) { for (int i = 0; i < MAX_FORKNUM; i++) { nblocks += smgrnblocks(rel->rd_smgr, i); } } else { nblocks = smgrnblocks(rel->rd_smgr, forkNumber); } return nblocks * BLCKSZ; } /* * Check to see whether the table needs a TOAST table. */ static bool fake_relation_needs_toast_table(Relation rel) { /* we don't test toastable data with this, so no toast table needed */ return false; } /* ------------------------------------------------------------------------ * Planner related callbacks for the fake AM * ------------------------------------------------------------------------ */ static void fake_estimate_rel_size(Relation rel, int32 *attr_widths, BlockNumber *pages, double *tuples, double *allvisfrac) { /* no data available */ *attr_widths = 0; *tuples = 0; *allvisfrac = 0; *pages = 0; } /* ------------------------------------------------------------------------ * Executor related callbacks for the fake AM * ------------------------------------------------------------------------ */ static bool fake_scan_bitmap_next_block(TableScanDesc scan, TBMIterateResult *tbmres) { elog(ERROR, "fake_scan_bitmap_next_block not implemented"); } static bool fake_scan_bitmap_next_tuple(TableScanDesc scan, TBMIterateResult *tbmres, TupleTableSlot *slot) { elog(ERROR, "fake_scan_bitmap_next_tuple not implemented"); } static bool fake_scan_sample_next_block(TableScanDesc scan, SampleScanState *scanstate) { elog(ERROR, "fake_scan_sample_next_block not implemented"); } static bool fake_scan_sample_next_tuple(TableScanDesc scan, SampleScanState *scanstate, TupleTableSlot *slot) { elog(ERROR, "fake_scan_sample_next_tuple not implemented"); } /* ------------------------------------------------------------------------ * Definition of the fake table access method. * ------------------------------------------------------------------------ */ static const TableAmRoutine fake_methods = { .type = T_TableAmRoutine, .slot_callbacks = fake_slot_callbacks, .scan_begin = fake_scan_begin, .scan_end = fake_scan_end, .scan_rescan = fake_scan_rescan, .scan_getnextslot = fake_scan_getnextslot, /* these are common helper functions */ .parallelscan_estimate = table_block_parallelscan_estimate, .parallelscan_initialize = table_block_parallelscan_initialize, .parallelscan_reinitialize = table_block_parallelscan_reinitialize, .index_fetch_begin = fake_index_fetch_begin, .index_fetch_reset = fake_index_fetch_reset, .index_fetch_end = fake_index_fetch_end, .index_fetch_tuple = fake_index_fetch_tuple, .tuple_insert = fake_tuple_insert, .tuple_insert_speculative = fake_tuple_insert_speculative, .tuple_complete_speculative = fake_tuple_complete_speculative, .multi_insert = fake_multi_insert, .tuple_delete = fake_tuple_delete, .tuple_update = fake_tuple_update, .tuple_lock = fake_tuple_lock, .finish_bulk_insert = fake_finish_bulk_insert, .tuple_fetch_row_version = fake_fetch_row_version, .tuple_get_latest_tid = fake_get_latest_tid, .tuple_tid_valid = fake_tuple_tid_valid, .tuple_satisfies_snapshot = fake_tuple_satisfies_snapshot, #if PG_VERSION_NUM >= PG_VERSION_14 .index_delete_tuples = fake_index_delete_tuples, #else .compute_xid_horizon_for_tuples = fake_compute_xid_horizon_for_tuples, #endif .relation_set_new_filenode = fake_relation_set_new_filenode, .relation_nontransactional_truncate = fake_relation_nontransactional_truncate, .relation_copy_data = fake_copy_data, .relation_copy_for_cluster = fake_copy_for_cluster, .relation_vacuum = fake_vacuum, .scan_analyze_next_block = fake_scan_analyze_next_block, .scan_analyze_next_tuple = fake_scan_analyze_next_tuple, .index_build_range_scan = fake_index_build_range_scan, .index_validate_scan = fake_index_validate_scan, .relation_size = fake_relation_size, .relation_needs_toast_table = fake_relation_needs_toast_table, .relation_estimate_size = fake_estimate_rel_size, .scan_bitmap_next_block = fake_scan_bitmap_next_block, .scan_bitmap_next_tuple = fake_scan_bitmap_next_tuple, .scan_sample_next_block = fake_scan_sample_next_block, .scan_sample_next_tuple = fake_scan_sample_next_tuple }; Datum fake_am_handler(PG_FUNCTION_ARGS) { PG_RETURN_POINTER(&fake_methods); }