From 97e0eea6a3fb123845ac5650f1aaa1802bf56694 Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas Date: Mon, 8 Apr 2019 15:16:53 +0300 Subject: [PATCH 1/3] Add a toy table AM implementation to play with. It returns a constant data set. No insert/update/delete. But you can create indexes. --- src/test/modules/toytable/Makefile | 25 + .../modules/toytable/expected/toytable.out | 41 ++ src/test/modules/toytable/sql/toytable.sql | 17 + src/test/modules/toytable/toytable--1.0.sql | 12 + src/test/modules/toytable/toytable.control | 4 + src/test/modules/toytable/toytableam.c | 612 ++++++++++++++++++ 6 files changed, 711 insertions(+) create mode 100644 src/test/modules/toytable/Makefile create mode 100644 src/test/modules/toytable/expected/toytable.out create mode 100644 src/test/modules/toytable/sql/toytable.sql create mode 100644 src/test/modules/toytable/toytable--1.0.sql create mode 100644 src/test/modules/toytable/toytable.control create mode 100644 src/test/modules/toytable/toytableam.c diff --git a/src/test/modules/toytable/Makefile b/src/test/modules/toytable/Makefile new file mode 100644 index 00000000000..142ef2d23e6 --- /dev/null +++ b/src/test/modules/toytable/Makefile @@ -0,0 +1,25 @@ +# src/test/modules/toytable/Makefile + +MODULE_big = toytable +OBJS = toytableam.o $(WIN32RES) +PGFILEDESC = "A dummy implementantation of the table AM API" + +EXTENSION = toytable +DATA = toytable--1.0.sql + +REGRESS = toytable + +ifdef USE_PGXS +PG_CONFIG = pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) +else +subdir = src/test/modules/toytable +top_builddir = ../../../.. +include $(top_builddir)/src/Makefile.global +include $(top_srcdir)/contrib/contrib-global.mk +endif + +OBJS = toytableam.o + +include $(top_srcdir)/src/backend/common.mk diff --git a/src/test/modules/toytable/expected/toytable.out b/src/test/modules/toytable/expected/toytable.out new file mode 100644 index 00000000000..3e8598e284c --- /dev/null +++ b/src/test/modules/toytable/expected/toytable.out @@ -0,0 +1,41 @@ +CREATE EXTENSION toytable; +create table toytab (i int4, j int4, k int4) using toytable; +select * from toytab; + i | j | k +----+----+---- + 1 | 1 | 1 + 2 | 2 | 2 + 3 | 3 | 3 + 4 | 4 | 4 + 5 | 5 | 5 + 6 | 6 | 6 + 7 | 7 | 7 + 8 | 8 | 8 + 9 | 9 | 9 + 10 | 10 | 10 +(10 rows) + +create index toyidx on toytab(i); +-- test index scan +set enable_seqscan=off; +set enable_indexscan=on; +select i, j from toytab where i = 4; + i | j +---+--- + 4 | 4 +(1 row) + +-- index only scan +explain (costs off) select i from toytab where i = 4; + QUERY PLAN +---------------------------------------- + Index Only Scan using toyidx on toytab + Index Cond: (i = 4) +(2 rows) + +select i from toytab where i = 4 ; + i +--- + 4 +(1 row) + diff --git a/src/test/modules/toytable/sql/toytable.sql b/src/test/modules/toytable/sql/toytable.sql new file mode 100644 index 00000000000..8d9bac41bbf --- /dev/null +++ b/src/test/modules/toytable/sql/toytable.sql @@ -0,0 +1,17 @@ +CREATE EXTENSION toytable; + +create table toytab (i int4, j int4, k int4) using toytable; + +select * from toytab; + +create index toyidx on toytab(i); + +-- test index scan +set enable_seqscan=off; +set enable_indexscan=on; + +select i, j from toytab where i = 4; + +-- index only scan +explain (costs off) select i from toytab where i = 4; +select i from toytab where i = 4 ; diff --git a/src/test/modules/toytable/toytable--1.0.sql b/src/test/modules/toytable/toytable--1.0.sql new file mode 100644 index 00000000000..52085d27f4a --- /dev/null +++ b/src/test/modules/toytable/toytable--1.0.sql @@ -0,0 +1,12 @@ +/* src/test/modules/toyam/toyam--1.0.sql */ + +-- complain if script is sourced in psql, rather than via CREATE EXTENSION +\echo Use "CREATE EXTENSION toytable" to load this file. \quit + +CREATE FUNCTION toytableam_handler(internal) +RETURNS pg_catalog.table_am_handler STRICT +AS 'MODULE_PATHNAME' LANGUAGE C; + +CREATE ACCESS METHOD toytable TYPE TABLE HANDLER toytableam_handler + + diff --git a/src/test/modules/toytable/toytable.control b/src/test/modules/toytable/toytable.control new file mode 100644 index 00000000000..8f613e58d6e --- /dev/null +++ b/src/test/modules/toytable/toytable.control @@ -0,0 +1,4 @@ +comment = 'Dummy implementation of table AM api' +default_version = '1.0' +module_pathname = '$libdir/toytable' +relocatable = true diff --git a/src/test/modules/toytable/toytableam.c b/src/test/modules/toytable/toytableam.c new file mode 100644 index 00000000000..30b0e74e7f6 --- /dev/null +++ b/src/test/modules/toytable/toytableam.c @@ -0,0 +1,612 @@ +/*------------------------------------------------------------------------- + * + * toyam_handler.c + * a toy table access method code + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/access/toytable/toyam_handler.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include + +#include "miscadmin.h" + +#include "access/multixact.h" +#include "access/relscan.h" +#include "access/tableam.h" +#include "catalog/catalog.h" +#include "catalog/storage.h" +#include "catalog/index.h" +#include "catalog/pg_type.h" +#include "executor/executor.h" +#include "utils/builtins.h" +#include "utils/rel.h" +#include "storage/bufmgr.h" + +PG_MODULE_MAGIC; + +PG_FUNCTION_INFO_V1(toytableam_handler); + +typedef struct +{ + TableScanDescData scan; + + int tupidx; +} ToyScanDescData; +typedef ToyScanDescData *ToyScanDesc; + +static const TupleTableSlotOps * +toyam_slot_callbacks(Relation relation) +{ + return &TTSOpsVirtual; +} + +static TableScanDesc toyam_scan_begin(Relation rel, + Snapshot snapshot, + int nkeys, struct ScanKeyData *key, + ParallelTableScanDesc pscan, + bool allow_strat, + bool allow_sync, + bool allow_pagemode, + bool is_bitmapscan, + bool is_samplescan, + bool temp_snap) +{ + ToyScanDesc tscan; + + tscan = palloc0(sizeof(ToyScanDescData)); + tscan->scan.rs_rd = rel; + tscan->scan.rs_snapshot = snapshot; + tscan->scan.rs_nkeys = nkeys; + tscan->scan.rs_bitmapscan = is_bitmapscan; + tscan->scan.rs_samplescan = is_samplescan; + tscan->scan.rs_allow_strat = allow_strat; + tscan->scan.rs_allow_sync = allow_sync; + tscan->scan.rs_temp_snap = temp_snap; + tscan->scan.rs_parallel = pscan; + + tscan->tupidx = 0; + + return &tscan->scan; +} + +static void +toyam_scan_end(TableScanDesc scan) +{ + pfree(scan); +} + +static void +toyam_scan_rescan(TableScanDesc scan, struct ScanKeyData *key, + bool set_params, bool allow_strat, + bool allow_sync, bool allow_pagemode) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("function %s not implemented yet", __func__))); +} + +static bool +toyam_scan_getnextslot(TableScanDesc scan, + ScanDirection direction, + TupleTableSlot *slot) +{ + ToyScanDesc tscan = (ToyScanDesc) scan; + + slot->tts_nvalid = 0; + slot->tts_flags |= TTS_FLAG_EMPTY; + + /* + * Return a constant 1 rows. Every int4 attribute gets + * a running count, everything else is NULL. + */ + if (tscan->tupidx < 10) + { + TupleDesc desc = RelationGetDescr(tscan->scan.rs_rd); + + tscan->tupidx++; + + for (AttrNumber attno = 1; attno <= desc->natts; attno++) + { + Form_pg_attribute att = &desc->attrs[attno - 1]; + Datum d; + bool isnull; + + if (att->atttypid == INT4OID) + { + d = tscan->tupidx; + isnull = false; + } + else + { + d = (Datum) 0; + isnull = true; + } + + slot->tts_values[attno - 1] = d; + slot->tts_isnull[attno - 1] = isnull; + } + + ItemPointerSet(&slot->tts_tid, 1, tscan->tupidx); + slot->tts_nvalid = slot->tts_tupleDescriptor->natts; + slot->tts_flags &= ~TTS_FLAG_EMPTY; + + return true; + } + else + return false; +} + +static Size +toyam_parallelscan_estimate(Relation rel) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("function %s not implemented yet", __func__))); +} + +static Size +toyam_parallelscan_initialize(Relation rel, + ParallelTableScanDesc pscan) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("function %s not implemented yet", __func__))); +} + +static void +toyam_parallelscan_reinitialize(Relation rel, + ParallelTableScanDesc pscan) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("function %s not implemented yet", __func__))); +} + +static struct IndexFetchTableData * +toyam_index_fetch_begin(Relation rel) +{ + IndexFetchTableData *tfetch = palloc0(sizeof(IndexFetchTableData)); + + tfetch->rel = rel; + + return tfetch; +} + +static void +toyam_index_fetch_reset(struct IndexFetchTableData *data) +{ +} + +static void +toyam_index_fetch_end(struct IndexFetchTableData *data) +{ + pfree(data); +} + +static bool +toyam_index_fetch_tuple(struct IndexFetchTableData *scan, + ItemPointer tid, + Snapshot snapshot, + TupleTableSlot *slot, + bool *call_again, bool *all_dead) +{ + TupleDesc desc = RelationGetDescr(scan->rel); + int tupidx; + + if (ItemPointerGetBlockNumber(tid) != 1) + return false; + + tupidx = ItemPointerGetOffsetNumber(tid); + if (tupidx < 1 || tupidx > 10) + return false; + + slot->tts_nvalid = 0; + slot->tts_flags |= TTS_FLAG_EMPTY; + + /* Return same data as toyam_scan_getnextslot does */ + for (AttrNumber attno = 1; attno <= desc->natts; attno++) + { + Form_pg_attribute att = &desc->attrs[attno - 1]; + Datum d; + bool isnull; + + if (att->atttypid == INT4OID) + { + d = tupidx; + isnull = false; + } + else + { + d = (Datum) 0; + isnull = true; + } + + slot->tts_values[attno - 1] = d; + slot->tts_isnull[attno - 1] = isnull; + } + + ItemPointerSet(&slot->tts_tid, 1, tupidx); + slot->tts_nvalid = slot->tts_tupleDescriptor->natts; + slot->tts_flags &= ~TTS_FLAG_EMPTY; + + return true; +} + +static bool +toyam_tuple_fetch_row_version(Relation rel, + ItemPointer tid, + Snapshot snapshot, + TupleTableSlot *slot) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("function %s not implemented yet", __func__))); +} + +static void +toyam_tuple_get_latest_tid(Relation rel, + Snapshot snapshot, + ItemPointer tid) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("function %s not implemented yet", __func__))); +} + +static bool +toyam_tuple_satisfies_snapshot(Relation rel, + TupleTableSlot *slot, + Snapshot snapshot) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("function %s not implemented yet", __func__))); +} + +static TransactionId +toyam_compute_xid_horizon_for_tuples(Relation rel, + ItemPointerData *items, + int nitems) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("function %s not implemented yet", __func__))); +} + +static void +toyam_tuple_insert(Relation rel, TupleTableSlot *slot, + CommandId cid, int options, + struct BulkInsertStateData *bistate) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("function %s not implemented yet", __func__))); +} + +static void +toyam_tuple_insert_speculative(Relation rel, + TupleTableSlot *slot, + CommandId cid, + int options, + struct BulkInsertStateData *bistate, + uint32 specToken) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("function %s not implemented yet", __func__))); +} + +static void +toyam_tuple_complete_speculative(Relation rel, + TupleTableSlot *slot, + uint32 specToken, + bool succeeded) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("function %s not implemented yet", __func__))); +} + +static TM_Result +toyam_tuple_delete(Relation rel, + ItemPointer tid, + CommandId cid, + Snapshot snapshot, + Snapshot crosscheck, + bool wait, + TM_FailureData *tmfd, + bool changingPart) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("function %s not implemented yet", __func__))); +} + +static void +toyam_multi_insert(Relation rel, TupleTableSlot **slots, int nslots, + CommandId cid, int options, struct BulkInsertStateData *bistate) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("function %s not implemented yet", __func__))); +} + +static TM_Result +toyam_tuple_update(Relation rel, + ItemPointer otid, + TupleTableSlot *slot, + CommandId cid, + Snapshot snapshot, + Snapshot crosscheck, + bool wait, + TM_FailureData *tmfd, + LockTupleMode *lockmode, + bool *update_indexes) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("function %s not implemented yet", __func__))); +} + +static TM_Result +toyam_tuple_lock(Relation rel, + ItemPointer tid, + Snapshot snapshot, + TupleTableSlot *slot, + CommandId cid, + LockTupleMode mode, + LockWaitPolicy wait_policy, + uint8 flags, + TM_FailureData *tmfd) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("function %s not implemented yet", __func__))); +} + +static void +toyam_finish_bulk_insert(Relation rel, int options) +{ + return; +} + +static void +toyam_relation_set_new_filenode(Relation rel, + char persistence, + TransactionId *freezeXid, + MultiXactId *minmulti) +{ + *freezeXid = InvalidTransactionId; + *minmulti = InvalidMultiXactId; + + /* + * FIXME: We don't need this for anything. But index build calls + * RelationGetNumberOfBlocks, from index_update_stats(), and that + * fails if the underlying file doesn't exist. + */ + RelationCreateStorage(rel->rd_node, persistence); +} + +static void +toyam_relation_nontransactional_truncate(Relation rel) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("function %s not implemented yet", __func__))); +} + +static void +toyam_relation_copy_data(Relation rel, RelFileNode newrnode) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("function %s not implemented yet", __func__))); +} + +static void +toyam_relation_copy_for_cluster(Relation NewHeap, + Relation OldHeap, + Relation OldIndex, + bool use_sort, + TransactionId OldestXmin, + TransactionId FreezeXid, + MultiXactId MultiXactCutoff, + double *num_tuples, + double *tups_vacuumed, + double *tups_recently_dead) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("function %s not implemented yet", __func__))); +} + +static void +toyam_relation_vacuum(Relation onerel, + struct VacuumParams *params, + BufferAccessStrategy bstrategy) +{ + /* we've got nothing to do */ +} + +static bool +toyam_scan_analyze_next_block(TableScanDesc scan, + BlockNumber blockno, + BufferAccessStrategy bstrategy) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("function %s not implemented yet", __func__))); +} + +static bool +toyam_scan_analyze_next_tuple(TableScanDesc scan, + TransactionId OldestXmin, + double *liverows, + double *deadrows, + TupleTableSlot *slot) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("function %s not implemented yet", __func__))); +} + +static double +toyam_index_build_range_scan(Relation heap_rel, + Relation index_rel, + struct IndexInfo *index_nfo, + bool allow_sync, + bool anyvisible, + bool progress, + BlockNumber start_blockno, + BlockNumber end_blockno, + IndexBuildCallback callback, + void *callback_state, + TableScanDesc scan) +{ + TupleTableSlot *slot; + EState *estate; + + estate = CreateExecutorState(); + slot = table_slot_create(heap_rel, NULL); + + if (!scan) + scan = toyam_scan_begin(heap_rel, + SnapshotAny, + 0, NULL, + NULL, + false, + false, + false, + false, + false, + false); + + while (toyam_scan_getnextslot(scan, ForwardScanDirection, slot)) + { + Datum values[INDEX_MAX_KEYS]; + bool isnull[INDEX_MAX_KEYS]; + HeapTuple heapTuple; + + FormIndexDatum(index_nfo, slot, estate, values, isnull); + + /* Call the AM's callback routine to process the tuple */ + heapTuple = ExecCopySlotHeapTuple(slot); + heapTuple->t_self = slot->tts_tid; + callback(heap_rel, heapTuple, values, isnull, true, + callback_state); + pfree(heapTuple); + } + + toyam_scan_end(scan); + ExecDropSingleTupleTableSlot(slot); + FreeExecutorState(estate); + + return 10; +} + +static void +toyam_index_validate_scan(Relation heap_rel, + Relation index_rel, + struct IndexInfo *index_info, + Snapshot snapshot, + struct ValidateIndexState *state) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("function %s not implemented yet", __func__))); +} + +static void +toyam_relation_estimate_size(Relation rel, int32 *attr_widths, + BlockNumber *pages, double *tuples, + double *allvisfrac) +{ + *pages = 1; + *tuples = 1; + *allvisfrac = 1.0; +} + +static bool +toyam_scan_sample_next_block(TableScanDesc scan, + struct SampleScanState *scanstate) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("function %s not implemented yet", __func__))); +} + +static bool +toyam_scan_sample_next_tuple(TableScanDesc scan, + struct SampleScanState *scanstate, + TupleTableSlot *slot) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("function %s not implemented yet", __func__))); +} + +static const TableAmRoutine toyam_methods = { + .type = T_TableAmRoutine, + + .slot_callbacks = toyam_slot_callbacks, + + .scan_begin = toyam_scan_begin, + .scan_end = toyam_scan_end, + .scan_rescan = toyam_scan_rescan, + .scan_getnextslot = toyam_scan_getnextslot, + + .parallelscan_estimate = toyam_parallelscan_estimate, + .parallelscan_initialize = toyam_parallelscan_initialize, + .parallelscan_reinitialize = toyam_parallelscan_reinitialize, + + .index_fetch_begin = toyam_index_fetch_begin, + .index_fetch_reset = toyam_index_fetch_reset, + .index_fetch_end = toyam_index_fetch_end, + .index_fetch_tuple = toyam_index_fetch_tuple, + + .tuple_fetch_row_version = toyam_tuple_fetch_row_version, + .tuple_get_latest_tid = toyam_tuple_get_latest_tid, + .tuple_satisfies_snapshot = toyam_tuple_satisfies_snapshot, + .compute_xid_horizon_for_tuples = toyam_compute_xid_horizon_for_tuples, + + .tuple_insert = toyam_tuple_insert, + .tuple_insert_speculative = toyam_tuple_insert_speculative, + .tuple_complete_speculative = toyam_tuple_complete_speculative, + .multi_insert = toyam_multi_insert, + .tuple_delete = toyam_tuple_delete, + .tuple_update = toyam_tuple_update, + .tuple_lock = toyam_tuple_lock, + .finish_bulk_insert = toyam_finish_bulk_insert, + + .relation_set_new_filenode = toyam_relation_set_new_filenode, + .relation_nontransactional_truncate = toyam_relation_nontransactional_truncate, + .relation_copy_data = toyam_relation_copy_data, + .relation_copy_for_cluster = toyam_relation_copy_for_cluster, + .relation_vacuum = toyam_relation_vacuum, + + .scan_analyze_next_block = toyam_scan_analyze_next_block, + .scan_analyze_next_tuple = toyam_scan_analyze_next_tuple, + .index_build_range_scan = toyam_index_build_range_scan, + .index_validate_scan = toyam_index_validate_scan, + + .relation_estimate_size = toyam_relation_estimate_size, + + .scan_bitmap_next_block = NULL, + .scan_bitmap_next_tuple = NULL, + .scan_sample_next_block = toyam_scan_sample_next_block, + .scan_sample_next_tuple = toyam_scan_sample_next_tuple, +}; + +Datum +toytableam_handler(PG_FUNCTION_ARGS) +{ + PG_RETURN_POINTER(&toyam_methods); +} -- 2.20.1