On Sat, Jun 3, 2023 at 7:42 PM Fabrízio de Royes Mello < fabriziome...@gmail.com> wrote: > > > Hi all, > > During the PGCon Unconference session about Table Access Method one missing item pointed out is that currently we lack documentation and examples of TAM. > > So in order to improve things a bit in this area I'm proposing to add a test module for Table Access Method similar what we already have for Index Access Method. > > This code is based on the "blackhole_am" implemented by Michael Paquier: https://github.com/michaelpq/pg_plugins/tree/main/blackhole_am >
Just added some more tests, ran pgindent and also organized a bit some comments and README.txt. Regards, -- Fabrízio de Royes Mello
From 31d6bf00fbee6c229382db0760ba602e4d41c917 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabr=C3=ADzio=20de=20Royes=20Mello?= <fabriziome...@gmail.com> Date: Sat, 3 Jun 2023 17:23:05 -0400 Subject: [PATCH v2] Add test module for Table Access Method --- src/test/modules/Makefile | 1 + src/test/modules/dummy_table_am/.gitignore | 3 + src/test/modules/dummy_table_am/Makefile | 20 + src/test/modules/dummy_table_am/README | 5 + .../dummy_table_am/dummy_table_am--1.0.sql | 14 + .../modules/dummy_table_am/dummy_table_am.c | 500 ++++++++++++++++++ .../dummy_table_am/dummy_table_am.control | 5 + .../expected/dummy_table_am.out | 207 ++++++++ src/test/modules/dummy_table_am/meson.build | 33 ++ .../dummy_table_am/sql/dummy_table_am.sql | 55 ++ src/test/modules/meson.build | 1 + 11 files changed, 844 insertions(+) create mode 100644 src/test/modules/dummy_table_am/.gitignore create mode 100644 src/test/modules/dummy_table_am/Makefile create mode 100644 src/test/modules/dummy_table_am/README create mode 100644 src/test/modules/dummy_table_am/dummy_table_am--1.0.sql create mode 100644 src/test/modules/dummy_table_am/dummy_table_am.c create mode 100644 src/test/modules/dummy_table_am/dummy_table_am.control create mode 100644 src/test/modules/dummy_table_am/expected/dummy_table_am.out create mode 100644 src/test/modules/dummy_table_am/meson.build create mode 100644 src/test/modules/dummy_table_am/sql/dummy_table_am.sql diff --git a/src/test/modules/Makefile b/src/test/modules/Makefile index 6331c976dc..ce982b0e46 100644 --- a/src/test/modules/Makefile +++ b/src/test/modules/Makefile @@ -10,6 +10,7 @@ SUBDIRS = \ delay_execution \ dummy_index_am \ dummy_seclabel \ + dummy_table_am \ libpq_pipeline \ plsample \ snapshot_too_old \ diff --git a/src/test/modules/dummy_table_am/.gitignore b/src/test/modules/dummy_table_am/.gitignore new file mode 100644 index 0000000000..44d119cfcc --- /dev/null +++ b/src/test/modules/dummy_table_am/.gitignore @@ -0,0 +1,3 @@ +# Generated subdirectories +/log/ +/results/ diff --git a/src/test/modules/dummy_table_am/Makefile b/src/test/modules/dummy_table_am/Makefile new file mode 100644 index 0000000000..9ea4a590c6 --- /dev/null +++ b/src/test/modules/dummy_table_am/Makefile @@ -0,0 +1,20 @@ +# src/test/modules/dummy_table_am/Makefile + +MODULES = dummy_table_am + +EXTENSION = dummy_table_am +DATA = dummy_table_am--1.0.sql +PGFILEDESC = "dummy_table_am - table access method template" + +REGRESS = dummy_table_am + +ifdef USE_PGXS +PG_CONFIG = pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) +else +subdir = src/test/modules/dummy_table_am +top_builddir = ../../../.. +include $(top_builddir)/src/Makefile.global +include $(top_srcdir)/contrib/contrib-global.mk +endif diff --git a/src/test/modules/dummy_table_am/README b/src/test/modules/dummy_table_am/README new file mode 100644 index 0000000000..35211554b0 --- /dev/null +++ b/src/test/modules/dummy_table_am/README @@ -0,0 +1,5 @@ +Dummy Table AM +============== + +Dummy table AM is a module for testing any facility usable by an table +access method, whose code is kept a maximum simple. diff --git a/src/test/modules/dummy_table_am/dummy_table_am--1.0.sql b/src/test/modules/dummy_table_am/dummy_table_am--1.0.sql new file mode 100644 index 0000000000..aa0fd82e61 --- /dev/null +++ b/src/test/modules/dummy_table_am/dummy_table_am--1.0.sql @@ -0,0 +1,14 @@ +/* src/test/modules/dummy_table_am/dummy_table_am--1.0.sql */ + +-- complain if script is sourced in psql, rather than via CREATE EXTENSION +\echo Use "CREATE EXTENSION dummy_table_am" to load this file. \quit + +CREATE FUNCTION dummy_table_am_handler(internal) +RETURNS table_am_handler +AS 'MODULE_PATHNAME' +LANGUAGE C; + +-- Access method +CREATE ACCESS METHOD dummy_table_am TYPE TABLE HANDLER dummy_table_am_handler; +COMMENT ON ACCESS METHOD dummy_table_am IS 'dummy table access method'; + diff --git a/src/test/modules/dummy_table_am/dummy_table_am.c b/src/test/modules/dummy_table_am/dummy_table_am.c new file mode 100644 index 0000000000..132f7b18cd --- /dev/null +++ b/src/test/modules/dummy_table_am/dummy_table_am.c @@ -0,0 +1,500 @@ +/*------------------------------------------------------------------------- + * + * dummy_table_am.c + * Index AM template main file. + * + * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/test/modules/dummy_table_am/dummy_table_am.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/heapam.h" +#include "access/tableam.h" +#include "catalog/index.h" +#include "commands/vacuum.h" + +PG_MODULE_MAGIC; + +/* Handler for table AM */ +PG_FUNCTION_INFO_V1(dummy_table_am_handler); + +/* Base structures for scans */ +typedef struct DummyScanDescData +{ + TableScanDescData rs_base; +} DummyScanDescData; + +typedef struct DummyScanDescData *DummyScanDesc; + +/* + * Slot related callbacks for Dummy Table Access Method + */ +static const TupleTableSlotOps * +dummy_table_am_slot_callbacks(Relation relation) +{ + elog(INFO, "%s", __func__); + return &TTSOpsMinimalTuple; +} + +/* + * Table Scan Callbacks for dummy_table_am AM + */ +static TableScanDesc +dummy_table_am_scan_begin(Relation relation, Snapshot snapshot, + int nkeys, ScanKey key, + ParallelTableScanDesc parallel_scan, + uint32 flags) +{ + DummyScanDesc scan; + + scan = (DummyScanDesc) palloc(sizeof(DummyScanDescData)); + + scan->rs_base.rs_rd = relation; + scan->rs_base.rs_snapshot = snapshot; + scan->rs_base.rs_nkeys = nkeys; + scan->rs_base.rs_flags = flags; + scan->rs_base.rs_parallel = parallel_scan; + + elog(INFO, "%s", __func__); + + return (TableScanDesc) scan; +} + +static void +dummy_table_am_scan_end(TableScanDesc sscan) +{ + DummyScanDesc scan = (DummyScanDesc) sscan; + + elog(INFO, "%s", __func__); + + pfree(scan); +} + +static void +dummy_table_am_scan_rescan(TableScanDesc sscan, ScanKey key, bool set_params, + bool allow_strat, bool allow_sync, bool allow_pagemode) +{ + elog(INFO, "%s", __func__); +} + +static bool +dummy_table_am_scan_getnextslot(TableScanDesc sscan, ScanDirection direction, + TupleTableSlot *slot) +{ + elog(INFO, "%s", __func__); + return false; +} + +/* + * Index Scan Callbacks for dummy_table_am AM + */ +static IndexFetchTableData * +dummy_table_am_index_fetch_begin(Relation rel) +{ + elog(INFO, "%s", __func__); + return NULL; +} + +static void +dummy_table_am_index_fetch_reset(IndexFetchTableData *scan) +{ + elog(INFO, "%s", __func__); +} + +static void +dummy_table_am_index_fetch_end(IndexFetchTableData *scan) +{ + elog(INFO, "%s", __func__); +} + +static bool +dummy_table_am_index_fetch_tuple(struct IndexFetchTableData *scan, + ItemPointer tid, + Snapshot snapshot, + TupleTableSlot *slot, + bool *call_again, bool *all_dead) +{ + elog(INFO, "%s", __func__); + return 0; +} + +/* + * Callbacks for non-modifying operations on individual tuples for + * dummy_table_am AM. + */ +static bool +dummy_table_am_fetch_row_version(Relation relation, + ItemPointer tid, + Snapshot snapshot, + TupleTableSlot *slot) +{ + elog(INFO, "%s", __func__); + return false; +} + +static void +dummy_table_am_get_latest_tid(TableScanDesc sscan, + ItemPointer tid) +{ + elog(INFO, "%s", __func__); +} + +static bool +dummy_table_am_tuple_tid_valid(TableScanDesc scan, ItemPointer tid) +{ + elog(INFO, "%s", __func__); + return false; +} + +static bool +dummy_table_am_tuple_satisfies_snapshot(Relation rel, TupleTableSlot *slot, + Snapshot snapshot) +{ + elog(INFO, "%s", __func__); + return false; +} + +static TransactionId +dummy_table_am_index_delete_tuples(Relation rel, + TM_IndexDeleteOp *delstate) +{ + elog(INFO, "%s", __func__); + return InvalidTransactionId; +} + +/* + * Functions for manipulations of physical tuples for dummy_table_am AM. + */ +static void +dummy_table_am_tuple_insert(Relation relation, TupleTableSlot *slot, + CommandId cid, int options, BulkInsertState bistate) +{ + elog(INFO, "%s", __func__); +} + +static void +dummy_table_am_tuple_insert_speculative(Relation relation, TupleTableSlot *slot, + CommandId cid, int options, + BulkInsertState bistate, + uint32 specToken) +{ + elog(INFO, "%s", __func__); +} + +static void +dummy_table_am_tuple_complete_speculative(Relation relation, TupleTableSlot *slot, + uint32 spekToken, bool succeeded) +{ + elog(INFO, "%s", __func__); +} + +static void +dummy_table_am_multi_insert(Relation relation, TupleTableSlot **slots, + int ntuples, CommandId cid, int options, + BulkInsertState bistate) +{ + elog(INFO, "%s", __func__); +} + +static TM_Result +dummy_table_am_tuple_delete(Relation relation, ItemPointer tid, CommandId cid, + Snapshot snapshot, Snapshot crosscheck, bool wait, + TM_FailureData *tmfd, bool changingPart) +{ + elog(INFO, "%s", __func__); + + /* nothing to do, so it is always OK */ + return TM_Ok; +} + +static TM_Result +dummy_table_am_tuple_update(Relation relation, ItemPointer otid, + TupleTableSlot *slot, CommandId cid, + Snapshot snapshot, Snapshot crosscheck, + bool wait, TM_FailureData *tmfd, + LockTupleMode *lockmode, + TU_UpdateIndexes *update_indexes) +{ + elog(INFO, "%s", __func__); + + /* nothing to do, so it is always OK */ + return TM_Ok; +} + +static TM_Result +dummy_table_am_tuple_lock(Relation relation, ItemPointer tid, Snapshot snapshot, + TupleTableSlot *slot, CommandId cid, LockTupleMode mode, + LockWaitPolicy wait_policy, uint8 flags, + TM_FailureData *tmfd) +{ + elog(INFO, "%s", __func__); + + /* nothing to do, so it is always OK */ + return TM_Ok; +} + +static void +dummy_table_am_finish_bulk_insert(Relation relation, int options) +{ + elog(INFO, "%s", __func__); +} + +/* + * DDL related callbacks for dummy_table_am AM. + */ +static void +dummy_table_am_relation_set_new_filelocator(Relation rel, + const RelFileLocator *newrnode, + char persistence, + TransactionId *freezeXid, + MultiXactId *minmulti) +{ + elog(INFO, "%s", __func__); +} + +static void +dummy_table_am_relation_nontransactional_truncate(Relation rel) +{ + elog(INFO, "%s", __func__); +} + +static void +dummy_table_am_copy_data(Relation rel, const RelFileLocator *newrnode) +{ + elog(INFO, "%s", __func__); +} + +static void +dummy_table_am_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(INFO, "%s", __func__); +} + +static void +dummy_table_am_vacuum(Relation onerel, VacuumParams *params, + BufferAccessStrategy bstrategy) +{ + elog(INFO, "%s", __func__); +} + +static bool +dummy_table_am_scan_analyze_next_block(TableScanDesc scan, BlockNumber blockno, + BufferAccessStrategy bstrategy) +{ + elog(INFO, "%s", __func__); + + /* no data, so no point to analyze next block */ + return false; +} + +static bool +dummy_table_am_scan_analyze_next_tuple(TableScanDesc scan, TransactionId OldestXmin, + double *liverows, double *deadrows, + TupleTableSlot *slot) +{ + elog(INFO, "%s", __func__); + + /* no data, so no point to analyze next tuple */ + return false; +} + +static double +dummy_table_am_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, "%s", __func__); + + /* no data, so no tuples */ + return 0; +} + +static void +dummy_table_am_index_validate_scan(Relation tableRelation, + Relation indexRelation, + IndexInfo *indexInfo, + Snapshot snapshot, + ValidateIndexState *state) +{ + elog(INFO, "%s", __func__); +} + +/* + * Miscellaneous callbacks for the dummy_table_am AM + */ +static uint64 +dummy_table_am_relation_size(Relation rel, ForkNumber forkNumber) +{ + elog(INFO, "%s", __func__); + + /* there is nothing */ + return 0; +} + +/* + * Check to see whether the table needs a TOAST table. + */ +static bool +dummy_table_am_relation_needs_toast_table(Relation rel) +{ + elog(INFO, "%s", __func__); + + /* no data, so no toast table needed */ + return false; +} + +/* + * Planner related callbacks for the dummy_table_am AM + */ +static void +dummy_table_am_estimate_rel_size(Relation rel, int32 *attr_widths, + BlockNumber *pages, double *tuples, + double *allvisfrac) +{ + /* no data available */ + if (attr_widths) + *attr_widths = 0; + if (pages) + *pages = 0; + if (tuples) + *tuples = 0; + if (allvisfrac) + *allvisfrac = 0; + + elog(INFO, "%s", __func__); +} + + +/* + * Executor related callbacks for the dummy_table_am AM + */ +static bool +dummy_table_am_scan_bitmap_next_block(TableScanDesc scan, + TBMIterateResult *tbmres) +{ + elog(INFO, "%s", __func__); + + /* no data, so no point to scan next block */ + return false; +} + +static bool +dummy_table_am_scan_bitmap_next_tuple(TableScanDesc scan, + TBMIterateResult *tbmres, + TupleTableSlot *slot) +{ + elog(INFO, "%s", __func__); + + /* no data, so no point to scan next tuple */ + return false; +} + +static bool +dummy_table_am_scan_sample_next_block(TableScanDesc scan, + SampleScanState *scanstate) +{ + elog(INFO, "%s", __func__); + + /* no data, so no point to scan next block for sampling */ + return false; +} + +static bool +dummy_table_am_scan_sample_next_tuple(TableScanDesc scan, + SampleScanState *scanstate, + TupleTableSlot *slot) +{ + elog(INFO, "%s", __func__); + + /* no data, so no point to scan next tuple for sampling */ + return false; +} + +/* + * Definition of the dummy_table_am table access method. + */ +static const TableAmRoutine dummy_table_am_methods = { + .type = T_TableAmRoutine, + + .slot_callbacks = dummy_table_am_slot_callbacks, + + .scan_begin = dummy_table_am_scan_begin, + .scan_end = dummy_table_am_scan_end, + .scan_rescan = dummy_table_am_scan_rescan, + .scan_getnextslot = dummy_table_am_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 = dummy_table_am_index_fetch_begin, + .index_fetch_reset = dummy_table_am_index_fetch_reset, + .index_fetch_end = dummy_table_am_index_fetch_end, + .index_fetch_tuple = dummy_table_am_index_fetch_tuple, + + .tuple_insert = dummy_table_am_tuple_insert, + .tuple_insert_speculative = dummy_table_am_tuple_insert_speculative, + .tuple_complete_speculative = dummy_table_am_tuple_complete_speculative, + .multi_insert = dummy_table_am_multi_insert, + .tuple_delete = dummy_table_am_tuple_delete, + .tuple_update = dummy_table_am_tuple_update, + .tuple_lock = dummy_table_am_tuple_lock, + .finish_bulk_insert = dummy_table_am_finish_bulk_insert, + + .tuple_fetch_row_version = dummy_table_am_fetch_row_version, + .tuple_get_latest_tid = dummy_table_am_get_latest_tid, + .tuple_tid_valid = dummy_table_am_tuple_tid_valid, + .tuple_satisfies_snapshot = dummy_table_am_tuple_satisfies_snapshot, + .index_delete_tuples = dummy_table_am_index_delete_tuples, + + .relation_set_new_filelocator = dummy_table_am_relation_set_new_filelocator, + .relation_nontransactional_truncate = dummy_table_am_relation_nontransactional_truncate, + .relation_copy_data = dummy_table_am_copy_data, + .relation_copy_for_cluster = dummy_table_am_copy_for_cluster, + .relation_vacuum = dummy_table_am_vacuum, + .scan_analyze_next_block = dummy_table_am_scan_analyze_next_block, + .scan_analyze_next_tuple = dummy_table_am_scan_analyze_next_tuple, + .index_build_range_scan = dummy_table_am_index_build_range_scan, + .index_validate_scan = dummy_table_am_index_validate_scan, + + .relation_size = dummy_table_am_relation_size, + .relation_needs_toast_table = dummy_table_am_relation_needs_toast_table, + + .relation_estimate_size = dummy_table_am_estimate_rel_size, + + .scan_bitmap_next_block = dummy_table_am_scan_bitmap_next_block, + .scan_bitmap_next_tuple = dummy_table_am_scan_bitmap_next_tuple, + .scan_sample_next_block = dummy_table_am_scan_sample_next_block, + .scan_sample_next_tuple = dummy_table_am_scan_sample_next_tuple +}; + +/* + * Table AM handler function: returns TableAmRoutine with access method + * parameters and callbacks. + */ +Datum +dummy_table_am_handler(PG_FUNCTION_ARGS) +{ + PG_RETURN_POINTER(&dummy_table_am_methods); +} diff --git a/src/test/modules/dummy_table_am/dummy_table_am.control b/src/test/modules/dummy_table_am/dummy_table_am.control new file mode 100644 index 0000000000..08f2f868d4 --- /dev/null +++ b/src/test/modules/dummy_table_am/dummy_table_am.control @@ -0,0 +1,5 @@ +# dummy_table_am extension +comment = 'dummy_table_am - table access method template' +default_version = '1.0' +module_pathname = '$libdir/dummy_table_am' +relocatable = true diff --git a/src/test/modules/dummy_table_am/expected/dummy_table_am.out b/src/test/modules/dummy_table_am/expected/dummy_table_am.out new file mode 100644 index 0000000000..76029a30ae --- /dev/null +++ b/src/test/modules/dummy_table_am/expected/dummy_table_am.out @@ -0,0 +1,207 @@ +-- Tests for dummy table access method +CREATE EXTENSION dummy_table_am; +CREATE TABLE dummy_table (a int, b text) USING dummy_table_am; +INFO: dummy_table_am_relation_set_new_filelocator +INFO: dummy_table_am_relation_needs_toast_table +-- Will error out +CREATE INDEX dummy_table_idx ON dummy_table (a); +INFO: dummy_table_am_estimate_rel_size +ERROR: dummy_table_am_index_build_range_scan +SELECT * FROM dummy_table; +INFO: dummy_table_am_estimate_rel_size +INFO: dummy_table_am_slot_callbacks +INFO: dummy_table_am_scan_begin +INFO: dummy_table_am_scan_getnextslot +INFO: dummy_table_am_scan_end + a | b +---+--- +(0 rows) + +INSERT INTO dummy_table VALUES (1, 'dummy'); +INFO: dummy_table_am_slot_callbacks +INFO: dummy_table_am_tuple_insert +SELECT * FROM dummy_table; +INFO: dummy_table_am_estimate_rel_size +INFO: dummy_table_am_slot_callbacks +INFO: dummy_table_am_scan_begin +INFO: dummy_table_am_scan_getnextslot +INFO: dummy_table_am_scan_end + a | b +---+--- +(0 rows) + +INSERT INTO dummy_table VALUES (1, 'dummy'), (2, 'dummy'); +INFO: dummy_table_am_slot_callbacks +INFO: dummy_table_am_tuple_insert +INFO: dummy_table_am_tuple_insert +SELECT * FROM dummy_table; +INFO: dummy_table_am_estimate_rel_size +INFO: dummy_table_am_slot_callbacks +INFO: dummy_table_am_scan_begin +INFO: dummy_table_am_scan_getnextslot +INFO: dummy_table_am_scan_end + a | b +---+--- +(0 rows) + +UPDATE dummy_table SET a = 0 WHERE a = 1; +INFO: dummy_table_am_estimate_rel_size +INFO: dummy_table_am_slot_callbacks +INFO: dummy_table_am_scan_begin +INFO: dummy_table_am_scan_getnextslot +INFO: dummy_table_am_scan_end +SELECT * FROM dummy_table; +INFO: dummy_table_am_estimate_rel_size +INFO: dummy_table_am_slot_callbacks +INFO: dummy_table_am_scan_begin +INFO: dummy_table_am_scan_getnextslot +INFO: dummy_table_am_scan_end + a | b +---+--- +(0 rows) + +-- Update without WHERE +UPDATE dummy_table SET a = 0, b = NULL; +INFO: dummy_table_am_estimate_rel_size +INFO: dummy_table_am_slot_callbacks +INFO: dummy_table_am_scan_begin +INFO: dummy_table_am_scan_getnextslot +INFO: dummy_table_am_scan_end +SELECT * FROM dummy_table; +INFO: dummy_table_am_estimate_rel_size +INFO: dummy_table_am_slot_callbacks +INFO: dummy_table_am_scan_begin +INFO: dummy_table_am_scan_getnextslot +INFO: dummy_table_am_scan_end + a | b +---+--- +(0 rows) + +DELETE FROM dummy_table WHERE a = 1; +INFO: dummy_table_am_estimate_rel_size +INFO: dummy_table_am_slot_callbacks +INFO: dummy_table_am_scan_begin +INFO: dummy_table_am_scan_getnextslot +INFO: dummy_table_am_scan_end +SELECT * FROM dummy_table; +INFO: dummy_table_am_estimate_rel_size +INFO: dummy_table_am_slot_callbacks +INFO: dummy_table_am_scan_begin +INFO: dummy_table_am_scan_getnextslot +INFO: dummy_table_am_scan_end + a | b +---+--- +(0 rows) + +TRUNCATE dummy_table; +INFO: dummy_table_am_relation_set_new_filelocator +SELECT * FROM dummy_table; +INFO: dummy_table_am_estimate_rel_size +INFO: dummy_table_am_slot_callbacks +INFO: dummy_table_am_scan_begin +INFO: dummy_table_am_scan_getnextslot +INFO: dummy_table_am_scan_end + a | b +---+--- +(0 rows) + +VACUUM dummy_table; +INFO: dummy_table_am_vacuum +ANALYZE dummy_table; +INFO: dummy_table_am_relation_size +INFO: dummy_table_am_relation_size +INFO: dummy_table_am_scan_begin +INFO: dummy_table_am_slot_callbacks +INFO: dummy_table_am_scan_end +VACUUM (FULL) dummy_table; +INFO: dummy_table_am_relation_set_new_filelocator +INFO: dummy_table_am_copy_for_cluster +INFO: dummy_table_am_relation_size +SELECT * FROM dummy_table; +INFO: dummy_table_am_estimate_rel_size +INFO: dummy_table_am_slot_callbacks +INFO: dummy_table_am_scan_begin +INFO: dummy_table_am_scan_getnextslot +INFO: dummy_table_am_scan_end + a | b +---+--- +(0 rows) + +COPY dummy_table TO STDOUT; +INFO: dummy_table_am_scan_begin +INFO: dummy_table_am_slot_callbacks +INFO: dummy_table_am_scan_getnextslot +INFO: dummy_table_am_scan_end +COPY dummy_table (a, b) FROM STDIN; +INFO: dummy_table_am_slot_callbacks +INFO: dummy_table_am_slot_callbacks +INFO: dummy_table_am_multi_insert +INFO: dummy_table_am_finish_bulk_insert +SELECT * FROM dummy_table; +INFO: dummy_table_am_estimate_rel_size +INFO: dummy_table_am_slot_callbacks +INFO: dummy_table_am_scan_begin +INFO: dummy_table_am_scan_getnextslot +INFO: dummy_table_am_scan_end + a | b +---+--- +(0 rows) + +ALTER TABLE dummy_table ADD COLUMN c BOOLEAN DEFAULT true; +INFO: dummy_table_am_relation_needs_toast_table +INSERT INTO dummy_table VALUES (1, 'dummy'); +INFO: dummy_table_am_slot_callbacks +INFO: dummy_table_am_tuple_insert +SELECT * FROM dummy_table; +INFO: dummy_table_am_estimate_rel_size +INFO: dummy_table_am_slot_callbacks +INFO: dummy_table_am_scan_begin +INFO: dummy_table_am_scan_getnextslot +INFO: dummy_table_am_scan_end + a | b | c +---+---+--- +(0 rows) + +-- ALTER TABLE SET ACCESS METHOD +ALTER TABLE dummy_table SET ACCESS METHOD heap; +INFO: dummy_table_am_relation_needs_toast_table +INFO: dummy_table_am_slot_callbacks +INFO: dummy_table_am_scan_begin +INFO: dummy_table_am_scan_getnextslot +INFO: dummy_table_am_scan_end +CREATE INDEX dummy_table_idx ON dummy_table (a); +INSERT INTO dummy_table VALUES (1, 'heap', false); +SELECT * FROM dummy_table; + a | b | c +---+------+--- + 1 | heap | f +(1 row) + +-- Will error out, the index must be dropped +ALTER TABLE dummy_table SET ACCESS METHOD dummy_table_am; +INFO: dummy_table_am_relation_set_new_filelocator +INFO: dummy_table_am_relation_needs_toast_table +INFO: dummy_table_am_slot_callbacks +INFO: dummy_table_am_tuple_insert +INFO: dummy_table_am_finish_bulk_insert +INFO: dummy_table_am_estimate_rel_size +ERROR: dummy_table_am_index_build_range_scan +DROP INDEX dummy_table_idx; +ALTER TABLE dummy_table SET ACCESS METHOD dummy_table_am; +INFO: dummy_table_am_relation_set_new_filelocator +INFO: dummy_table_am_relation_needs_toast_table +INFO: dummy_table_am_slot_callbacks +INFO: dummy_table_am_tuple_insert +INFO: dummy_table_am_finish_bulk_insert +SELECT * FROM dummy_table; +INFO: dummy_table_am_estimate_rel_size +INFO: dummy_table_am_slot_callbacks +INFO: dummy_table_am_scan_begin +INFO: dummy_table_am_scan_getnextslot +INFO: dummy_table_am_scan_end + a | b | c +---+---+--- +(0 rows) + +-- Clean up +DROP TABLE dummy_table; diff --git a/src/test/modules/dummy_table_am/meson.build b/src/test/modules/dummy_table_am/meson.build new file mode 100644 index 0000000000..93f9108f29 --- /dev/null +++ b/src/test/modules/dummy_table_am/meson.build @@ -0,0 +1,33 @@ +# Copyright (c) 2022-2023, PostgreSQL Global Development Group + +dummy_table_am_sources = files( + 'dummy_table_am.c', +) + +if host_system == 'windows' + dummy_table_am_sources += rc_lib_gen.process(win32ver_rc, extra_args: [ + '--NAME', 'dummy_table_am', + '--FILEDESC', 'dummy_table_am - table access method template',]) +endif + +dummy_table_am = shared_module('dummy_table_am', + dummy_table_am_sources, + kwargs: pg_test_mod_args, +) +test_install_libs += dummy_table_am + +test_install_data += files( + 'dummy_table_am.control', + 'dummy_table_am--1.0.sql', +) + +tests += { + 'name': 'dummy_table_am', + 'sd': meson.current_source_dir(), + 'bd': meson.current_build_dir(), + 'regress': { + 'sql': [ + 'dummy_table_am', + ], + }, +} diff --git a/src/test/modules/dummy_table_am/sql/dummy_table_am.sql b/src/test/modules/dummy_table_am/sql/dummy_table_am.sql new file mode 100644 index 0000000000..f17c169255 --- /dev/null +++ b/src/test/modules/dummy_table_am/sql/dummy_table_am.sql @@ -0,0 +1,55 @@ +-- Tests for dummy table access method +CREATE EXTENSION dummy_table_am; +CREATE TABLE dummy_table (a int, b text) USING dummy_table_am; +-- Will error out +CREATE INDEX dummy_table_idx ON dummy_table (a); +SELECT * FROM dummy_table; + +INSERT INTO dummy_table VALUES (1, 'dummy'); +SELECT * FROM dummy_table; + +INSERT INTO dummy_table VALUES (1, 'dummy'), (2, 'dummy'); +SELECT * FROM dummy_table; + +UPDATE dummy_table SET a = 0 WHERE a = 1; +SELECT * FROM dummy_table; + +-- Update without WHERE +UPDATE dummy_table SET a = 0, b = NULL; +SELECT * FROM dummy_table; + +DELETE FROM dummy_table WHERE a = 1; +SELECT * FROM dummy_table; + +TRUNCATE dummy_table; +SELECT * FROM dummy_table; + +VACUUM dummy_table; +ANALYZE dummy_table; +VACUUM (FULL) dummy_table; +SELECT * FROM dummy_table; + +COPY dummy_table TO STDOUT; +COPY dummy_table (a, b) FROM STDIN; +1 dummy +\. +SELECT * FROM dummy_table; + +ALTER TABLE dummy_table ADD COLUMN c BOOLEAN DEFAULT true; +INSERT INTO dummy_table VALUES (1, 'dummy'); +SELECT * FROM dummy_table; + +-- ALTER TABLE SET ACCESS METHOD +ALTER TABLE dummy_table SET ACCESS METHOD heap; +CREATE INDEX dummy_table_idx ON dummy_table (a); +INSERT INTO dummy_table VALUES (1, 'heap', false); +SELECT * FROM dummy_table; + +-- Will error out, the index must be dropped +ALTER TABLE dummy_table SET ACCESS METHOD dummy_table_am; +DROP INDEX dummy_table_idx; +ALTER TABLE dummy_table SET ACCESS METHOD dummy_table_am; +SELECT * FROM dummy_table; + +-- Clean up +DROP TABLE dummy_table; diff --git a/src/test/modules/meson.build b/src/test/modules/meson.build index 17d369e378..84460f27b4 100644 --- a/src/test/modules/meson.build +++ b/src/test/modules/meson.build @@ -5,6 +5,7 @@ subdir('commit_ts') subdir('delay_execution') subdir('dummy_index_am') subdir('dummy_seclabel') +subdir('dummy_table_am') subdir('ldap_password_func') subdir('libpq_pipeline') subdir('plsample') -- 2.34.1