From 78bc650cb1f63c81db57958d809ce7fbc006e6ae Mon Sep 17 00:00:00 2001 From: Michael Paquier Date: Wed, 4 Dec 2024 08:09:46 +0900 Subject: [PATCH v13 4/7] Sequence access methods - backend support The "seqlocal" sequence AM is now plugged in as a handler in the relcache, and a set of callbacks in sequenceam.h. --- src/include/access/seqlocalam.h | 15 -- src/include/access/sequenceam.h | 181 ++++++++++++++++++ src/include/catalog/pg_am.dat | 3 + src/include/catalog/pg_am.h | 1 + src/include/catalog/pg_class.h | 6 + src/include/catalog/pg_proc.dat | 13 ++ src/include/catalog/pg_type.dat | 6 + src/include/commands/defrem.h | 1 + src/include/commands/sequence.h | 34 ---- src/include/nodes/meson.build | 1 + src/include/nodes/parsenodes.h | 1 + src/include/utils/guc_hooks.h | 2 + src/include/utils/rel.h | 5 + src/backend/access/sequence/Makefile | 2 +- src/backend/access/sequence/meson.build | 1 + src/backend/access/sequence/seqlocalam.c | 41 +++- src/backend/access/sequence/sequence.c | 3 +- src/backend/access/sequence/sequenceamapi.c | 145 ++++++++++++++ src/backend/catalog/heap.c | 6 +- src/backend/commands/amcmds.c | 16 ++ src/backend/commands/sequence.c | 23 +-- src/backend/commands/tablecmds.c | 17 +- src/backend/nodes/Makefile | 1 + src/backend/nodes/gen_node_support.pl | 2 + src/backend/parser/gram.y | 12 +- src/backend/parser/parse_utilcmd.c | 2 + src/backend/utils/adt/pseudotypes.c | 1 + src/backend/utils/cache/relcache.c | 91 +++++++-- src/backend/utils/misc/guc_tables.c | 12 ++ src/backend/utils/misc/postgresql.conf.sample | 1 + src/bin/psql/describe.c | 2 + src/bin/psql/tab-complete.in.c | 4 +- src/test/regress/expected/create_am.out | 55 ++++-- src/test/regress/expected/opr_sanity.out | 12 ++ src/test/regress/expected/psql.out | 96 +++++----- src/test/regress/expected/type_sanity.out | 12 +- src/test/regress/sql/create_am.sql | 24 ++- src/test/regress/sql/opr_sanity.sql | 10 + src/test/regress/sql/type_sanity.sql | 12 +- src/tools/pgindent/typedefs.list | 5 +- 40 files changed, 695 insertions(+), 182 deletions(-) create mode 100644 src/include/access/sequenceam.h create mode 100644 src/backend/access/sequence/sequenceamapi.c diff --git a/src/include/access/seqlocalam.h b/src/include/access/seqlocalam.h index 225fb9a2cbeb..21936511ac2b 100644 --- a/src/include/access/seqlocalam.h +++ b/src/include/access/seqlocalam.h @@ -15,7 +15,6 @@ #include "access/xlogreader.h" #include "storage/relfilelocator.h" -#include "utils/rel.h" /* XLOG stuff */ #define XLOG_SEQ_LOCAL_LOG 0x00 @@ -41,18 +40,4 @@ extern void seq_local_desc(StringInfo buf, XLogReaderState *record); extern const char *seq_local_identify(uint8 info); extern void seq_local_mask(char *page, BlockNumber blkno); -/* access routines */ -extern int64 seq_local_nextval(Relation rel, int64 incby, int64 maxv, - int64 minv, int64 cache, bool cycle, - int64 *last); -extern const char *seq_local_get_table_am(void); -extern void seq_local_init(Relation rel, int64 last_value, bool is_called); -extern void seq_local_setval(Relation rel, int64 next, bool iscalled); -extern void seq_local_reset(Relation rel, int64 startv, bool is_called, - bool reset_state); -extern void seq_local_get_state(Relation rel, int64 *last_value, - bool *is_called); -extern void seq_local_change_persistence(Relation rel, - char newrelpersistence); - #endif /* SEQLOCALAM_H */ diff --git a/src/include/access/sequenceam.h b/src/include/access/sequenceam.h new file mode 100644 index 000000000000..ac48c8b468be --- /dev/null +++ b/src/include/access/sequenceam.h @@ -0,0 +1,181 @@ +/*------------------------------------------------------------------------- + * + * sequenceam.h + * POSTGRES sequence access method definitions. + * + * + * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/access/sequenceam.h + * + * NOTES + * See sequenceam.sgml for higher level documentation. + * + *------------------------------------------------------------------------- + */ +#ifndef SEQUENCEAM_H +#define SEQUENCEAM_H + +#include "utils/rel.h" + +#define DEFAULT_SEQUENCE_ACCESS_METHOD "seqlocal" + +/* GUCs */ +extern PGDLLIMPORT char *default_sequence_access_method; + +/* + * API struct for a sequence AM. Note this must be allocated in a + * server-lifetime manner, typically as a static const struct, which then gets + * returned by FormData_pg_am.amhandler. + * + * In most cases it's not appropriate to call the callbacks directly, use the + * sequence_* wrapper functions instead. + * + * GetSequenceAmRoutine() asserts that required callbacks are filled in, + * remember to update when adding a callback. + */ +typedef struct SequenceAmRoutine +{ + /* this must be set to T_SequenceAmRoutine */ + NodeTag type; + + /* + * Retrieve table access method used by a sequence to store its metadata. + */ + const char *(*get_table_am) (void); + + /* + * Initialize sequence after creating a sequence Relation in pg_class, + * setting up the sequence for use. "last_value" and "is_called" are + * guessed from the options set for the sequence in CREATE SEQUENCE, based + * on the configuration of pg_sequence. + */ + void (*init) (Relation rel, int64 last_value, bool is_called); + + /* + * Retrieve a result for nextval(), based on the options retrieved from + * the sequence's options in pg_sequence. "last" is the last value + * calculated stored in the session's local cache, for lastval(). + */ + int64 (*nextval) (Relation rel, int64 incby, int64 maxv, + int64 minv, int64 cache, bool cycle, + int64 *last); + + /* + * Callback to set the state of a sequence, based on the input arguments + * from setval(). + */ + void (*setval) (Relation rel, int64 next, bool iscalled); + + /* + * Reset a sequence to its initial value. "reset_state", if set to true, + * means that the sequence parameters have changed, hence its internal + * state may need to be reset as well. "startv" and "is_called" are + * values guessed from the configuration of the sequence, based on the + * contents of pg_sequence. + */ + void (*reset) (Relation rel, int64 startv, bool is_called, + bool reset_state); + + /* + * Returns the current state of a sequence, returning data for + * pg_sequence_last_value() and related DDLs like ALTER SEQUENCE. + * "last_value" and "is_called" should be assigned to the values retrieved + * from the sequence Relation. + */ + void (*get_state) (Relation rel, int64 *last_value, bool *is_called); + + /* + * Callback used when switching persistence of a sequence Relation, to + * reset the sequence based on its new persistence "newrelpersistence". + */ + void (*change_persistence) (Relation rel, char newrelpersistence); + +} SequenceAmRoutine; + + +/* --------------------------------------------------------------------------- + * Wrapper functions for each callback. + * --------------------------------------------------------------------------- + */ + +/* + * Returns the name of the table access method used by this sequence. + */ +static inline const char * +sequence_get_table_am(Relation rel) +{ + return rel->rd_sequenceam->get_table_am(); +} + +/* + * Insert tuple data based on the information guessed from the contents + * of pg_sequence. + */ +static inline void +sequence_init(Relation rel, int64 last_value, bool is_called) +{ + rel->rd_sequenceam->init(rel, last_value, is_called); +} + +/* + * Allocate a set of values for the given sequence. "last" is the last value + * allocated. The result returned is the next value of the sequence computed. + */ +static inline int64 +sequence_nextval(Relation rel, int64 incby, int64 maxv, + int64 minv, int64 cache, bool cycle, + int64 *last) +{ + return rel->rd_sequenceam->nextval(rel, incby, maxv, minv, cache, + cycle, last); +} + +/* + * Callback to set the state of a sequence, based on the input arguments + * from setval(). + */ +static inline void +sequence_setval(Relation rel, int64 next, bool iscalled) +{ + rel->rd_sequenceam->setval(rel, next, iscalled); +} + +/* + * Reset a sequence to its initial state. + */ +static inline void +sequence_reset(Relation rel, int64 startv, bool is_called, + bool reset_state) +{ + rel->rd_sequenceam->reset(rel, startv, is_called, reset_state); +} + +/* + * Retrieve sequence metadata. + */ +static inline void +sequence_get_state(Relation rel, int64 *last_value, bool *is_called) +{ + rel->rd_sequenceam->get_state(rel, last_value, is_called); +} + +/* + * Callback to change the persistence of a sequence Relation. + */ +static inline void +sequence_change_persistence(Relation rel, char newrelpersistence) +{ + rel->rd_sequenceam->change_persistence(rel, newrelpersistence); +} + +/* ---------------------------------------------------------------------------- + * Functions in sequenceamapi.c + * ---------------------------------------------------------------------------- + */ + +extern const SequenceAmRoutine *GetSequenceAmRoutine(Oid amhandler); +extern Oid GetSequenceAmRoutineId(Oid amoid); + +#endif /* SEQUENCEAM_H */ diff --git a/src/include/catalog/pg_am.dat b/src/include/catalog/pg_am.dat index 26d15928a155..8f076fcec958 100644 --- a/src/include/catalog/pg_am.dat +++ b/src/include/catalog/pg_am.dat @@ -15,6 +15,9 @@ { oid => '2', oid_symbol => 'HEAP_TABLE_AM_OID', descr => 'heap table access method', amname => 'heap', amhandler => 'heap_tableam_handler', amtype => 't' }, +{ oid => '8051', oid_symbol => 'LOCAL_SEQUENCE_AM_OID', + descr => 'local sequence access method', + amname => 'seqlocal', amhandler => 'seq_local_sequenceam_handler', amtype => 's' }, { oid => '403', oid_symbol => 'BTREE_AM_OID', descr => 'b-tree index access method', amname => 'btree', amhandler => 'bthandler', amtype => 'i' }, diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h index 6e98a0930c27..080bea5031a9 100644 --- a/src/include/catalog/pg_am.h +++ b/src/include/catalog/pg_am.h @@ -59,6 +59,7 @@ MAKE_SYSCACHE(AMOID, pg_am_oid_index, 4); * Allowed values for amtype */ #define AMTYPE_INDEX 'i' /* index access method */ +#define AMTYPE_SEQUENCE 's' /* sequence access method */ #define AMTYPE_TABLE 't' /* table access method */ #endif /* EXPOSE_TO_CLIENT_CODE */ diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h index 07d182da796a..012a2863c037 100644 --- a/src/include/catalog/pg_class.h +++ b/src/include/catalog/pg_class.h @@ -231,6 +231,12 @@ MAKE_SYSCACHE(RELNAMENSP, pg_class_relname_nsp_index, 128); (relkind) == RELKIND_TOASTVALUE || \ (relkind) == RELKIND_MATVIEW) +/* + * Relation kinds with a sequence access method (rd_sequenceam). + */ +#define RELKIND_HAS_SEQUENCE_AM(relkind) \ + ((relkind) == RELKIND_SEQUENCE) + #endif /* EXPOSE_TO_CLIENT_CODE */ extern int errdetail_relkind_not_supported(char relkind); diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 8b68b16d79da..0da1d882467b 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -913,6 +913,12 @@ prorettype => 'table_am_handler', proargtypes => 'internal', prosrc => 'heap_tableam_handler' }, +# Sequence access method handlers +{ oid => '8209', descr => 'local sequence access method handler', + proname => 'seq_local_sequenceam_handler', provolatile => 'v', + prorettype => 'sequence_am_handler', proargtypes => 'internal', + prosrc => 'seq_local_sequenceam_handler' }, + # Index access method handlers { oid => '330', descr => 'btree index access method handler', proname => 'bthandler', provolatile => 'v', prorettype => 'index_am_handler', @@ -7846,6 +7852,13 @@ { oid => '327', descr => 'I/O', proname => 'index_am_handler_out', prorettype => 'cstring', proargtypes => 'index_am_handler', prosrc => 'index_am_handler_out' }, +{ oid => '8207', descr => 'I/O', + proname => 'sequence_am_handler_in', proisstrict => 'f', + prorettype => 'sequence_am_handler', proargtypes => 'cstring', + prosrc => 'sequence_am_handler_in' }, +{ oid => '8208', descr => 'I/O', + proname => 'sequence_am_handler_out', prorettype => 'cstring', + proargtypes => 'sequence_am_handler', prosrc => 'sequence_am_handler_out' }, { oid => '3311', descr => 'I/O', proname => 'tsm_handler_in', proisstrict => 'f', prorettype => 'tsm_handler', proargtypes => 'cstring', prosrc => 'tsm_handler_in' }, diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat index 6dca77e0a22f..c42531b25533 100644 --- a/src/include/catalog/pg_type.dat +++ b/src/include/catalog/pg_type.dat @@ -627,6 +627,12 @@ typcategory => 'P', typinput => 'index_am_handler_in', typoutput => 'index_am_handler_out', typreceive => '-', typsend => '-', typalign => 'i' }, +{ oid => '8210', + descr => 'pseudo-type for the result of a sequence AM handler function', + typname => 'sequence_am_handler', typlen => '4', typbyval => 't', + typtype => 'p', typcategory => 'P', typinput => 'sequence_am_handler_in', + typoutput => 'sequence_am_handler_out', typreceive => '-', typsend => '-', + typalign => 'i' }, { oid => '3310', descr => 'pseudo-type for the result of a tablesample method function', typname => 'tsm_handler', typlen => '4', typbyval => 't', typtype => 'p', diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h index dd22b5efdfd9..6790728aced3 100644 --- a/src/include/commands/defrem.h +++ b/src/include/commands/defrem.h @@ -145,6 +145,7 @@ extern Datum transformGenericOptions(Oid catalogId, extern ObjectAddress CreateAccessMethod(CreateAmStmt *stmt); extern Oid get_index_am_oid(const char *amname, bool missing_ok); extern Oid get_table_am_oid(const char *amname, bool missing_ok); +extern Oid get_sequence_am_oid(const char *amname, bool missing_ok); extern Oid get_am_oid(const char *amname, bool missing_ok); extern char *get_am_name(Oid amOid); diff --git a/src/include/commands/sequence.h b/src/include/commands/sequence.h index 9ac0b67683d3..7693e9941fc9 100644 --- a/src/include/commands/sequence.h +++ b/src/include/commands/sequence.h @@ -22,35 +22,6 @@ #include "storage/relfilelocator.h" -typedef struct FormData_pg_sequence_data -{ - int64 last_value; - int64 log_cnt; - bool is_called; -} FormData_pg_sequence_data; - -typedef FormData_pg_sequence_data *Form_pg_sequence_data; - -/* - * Columns of a sequence relation - */ - -#define SEQ_COL_LASTVAL 1 -#define SEQ_COL_LOG 2 -#define SEQ_COL_CALLED 3 - -#define SEQ_COL_FIRSTCOL SEQ_COL_LASTVAL -#define SEQ_COL_LASTCOL SEQ_COL_CALLED - -/* XLOG stuff */ -#define XLOG_SEQ_LOG 0x00 - -typedef struct xl_seq_rec -{ - RelFileLocator locator; - /* SEQUENCE TUPLE DATA FOLLOWS AT THE END */ -} xl_seq_rec; - extern int64 nextval_internal(Oid relid, bool check_permissions); extern Datum nextval(PG_FUNCTION_ARGS); extern List *sequence_options(Oid relid); @@ -62,9 +33,4 @@ extern void DeleteSequenceTuple(Oid relid); extern void ResetSequence(Oid seq_relid); extern void ResetSequenceCaches(void); -extern void seq_redo(XLogReaderState *record); -extern void seq_desc(StringInfo buf, XLogReaderState *record); -extern const char *seq_identify(uint8 info); -extern void seq_mask(char *page, BlockNumber blkno); - #endif /* SEQUENCE_H */ diff --git a/src/include/nodes/meson.build b/src/include/nodes/meson.build index d1ca24dd32f0..b1c4155c9a91 100644 --- a/src/include/nodes/meson.build +++ b/src/include/nodes/meson.build @@ -10,6 +10,7 @@ node_support_input_i = [ 'access/amapi.h', 'access/cmptype.h', 'access/sdir.h', + 'access/sequenceam.h', 'access/tableam.h', 'access/tsmapi.h', 'commands/event_trigger.h', diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index d637a2381c12..6f48d9eec2e8 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3214,6 +3214,7 @@ typedef struct CreateSeqStmt List *options; Oid ownerId; /* ID of owner, or InvalidOid for default */ bool for_identity; + char *accessMethod; /* USING name of access method (eg. local) */ bool if_not_exists; /* just do nothing if it already exists? */ } CreateSeqStmt; diff --git a/src/include/utils/guc_hooks.h b/src/include/utils/guc_hooks.h index 0f1e74f96c9b..7aba166746ac 100644 --- a/src/include/utils/guc_hooks.h +++ b/src/include/utils/guc_hooks.h @@ -55,6 +55,8 @@ extern bool check_log_connections(char **newval, void **extra, GucSource source) extern void assign_log_connections(const char *newval, void *extra); extern bool check_default_table_access_method(char **newval, void **extra, GucSource source); +extern bool check_default_sequence_access_method(char **newval, void **extra, + GucSource source); extern bool check_default_tablespace(char **newval, void **extra, GucSource source); extern bool check_default_text_search_config(char **newval, void **extra, GucSource source); diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h index d94fddd7cef7..6a008d348568 100644 --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -188,6 +188,11 @@ typedef struct RelationData */ const struct TableAmRoutine *rd_tableam; + /* + * Sequence access method. + */ + const struct SequenceAmRoutine *rd_sequenceam; + /* These are non-NULL only for an index relation: */ Form_pg_index rd_index; /* pg_index tuple describing this index */ /* use "struct" here to avoid needing to include htup.h: */ diff --git a/src/backend/access/sequence/Makefile b/src/backend/access/sequence/Makefile index a15ceec1c0a0..62006165a15f 100644 --- a/src/backend/access/sequence/Makefile +++ b/src/backend/access/sequence/Makefile @@ -12,6 +12,6 @@ subdir = src/backend/access/sequence top_builddir = ../../../.. include $(top_builddir)/src/Makefile.global -OBJS = seqlocalam.o seqlocalxlog.o sequence.o +OBJS = seqlocalam.o seqlocalxlog.o sequence.o sequenceamapi.o include $(top_srcdir)/src/backend/common.mk diff --git a/src/backend/access/sequence/meson.build b/src/backend/access/sequence/meson.build index 8bc0e95e68c0..d82af34d538c 100644 --- a/src/backend/access/sequence/meson.build +++ b/src/backend/access/sequence/meson.build @@ -4,4 +4,5 @@ backend_sources += files( 'seqlocalam.c', 'seqlocalxlog.c', 'sequence.c', + 'sequenceamapi.c', ) diff --git a/src/backend/access/sequence/seqlocalam.c b/src/backend/access/sequence/seqlocalam.c index bc735ebb0838..946c7f87a686 100644 --- a/src/backend/access/sequence/seqlocalam.c +++ b/src/backend/access/sequence/seqlocalam.c @@ -17,6 +17,7 @@ #include "access/multixact.h" #include "access/seqlocalam.h" +#include "access/sequenceam.h" #include "access/xact.h" #include "access/xloginsert.h" #include "access/xlogutils.h" @@ -24,6 +25,7 @@ #include "commands/tablecmds.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "utils/builtins.h" /* @@ -230,10 +232,10 @@ fill_seq_fork_with_data(Relation rel, HeapTuple tuple, ForkNumber forkNum) * Allocate a new value for a local sequence, based on the sequence * configuration. */ -int64 +static int64 seq_local_nextval(Relation rel, int64 incby, int64 maxv, - int64 minv, int64 cache, bool cycle, - int64 *last) + int64 minv, int64 cache, bool cycle, + int64 *last) { int64 result; int64 fetch; @@ -417,7 +419,7 @@ seq_local_nextval(Relation rel, int64 incby, int64 maxv, * * Return the table access method used by this sequence. */ -const char * +static const char * seq_local_get_table_am(void) { return "heap"; @@ -432,7 +434,7 @@ seq_local_get_table_am(void) * inserted after the relation has been created, filling in its heap * table. */ -void +static void seq_local_init(Relation rel, int64 last_value, bool is_called) { Datum value[SEQ_LOCAL_COL_LASTCOL]; @@ -499,7 +501,7 @@ seq_local_init(Relation rel, int64 last_value, bool is_called) * * Callback for setval(). */ -void +static void seq_local_setval(Relation rel, int64 next, bool iscalled) { Buffer buf; @@ -547,7 +549,7 @@ seq_local_setval(Relation rel, int64 next, bool iscalled) * Perform a hard reset on the local sequence, rewriting its heap data * entirely. */ -void +static void seq_local_reset(Relation rel, int64 startv, bool is_called, bool reset_state) { Form_pg_seq_local_data seq; @@ -600,7 +602,7 @@ seq_local_reset(Relation rel, int64 startv, bool is_called, bool reset_state) * * Retrieve the state of a local sequence. */ -void +static void seq_local_get_state(Relation rel, int64 *last_value, bool *is_called) { Buffer buf; @@ -621,7 +623,7 @@ seq_local_get_state(Relation rel, int64 *last_value, bool *is_called) * * Persistence change for the local sequence Relation. */ -void +static void seq_local_change_persistence(Relation rel, char newrelpersistence) { Buffer buf; @@ -632,3 +634,24 @@ seq_local_change_persistence(Relation rel, char newrelpersistence) fill_seq_with_data(rel, &seqdatatuple); UnlockReleaseBuffer(buf); } + +/* ------------------------------------------------------------------------ + * Definition of the local sequence access method. + * ------------------------------------------------------------------------ + */ +static const SequenceAmRoutine seq_local_methods = { + .type = T_SequenceAmRoutine, + .get_table_am = seq_local_get_table_am, + .init = seq_local_init, + .nextval = seq_local_nextval, + .setval = seq_local_setval, + .reset = seq_local_reset, + .get_state = seq_local_get_state, + .change_persistence = seq_local_change_persistence +}; + +Datum +seq_local_sequenceam_handler(PG_FUNCTION_ARGS) +{ + PG_RETURN_POINTER(&seq_local_methods); +} diff --git a/src/backend/access/sequence/sequence.c b/src/backend/access/sequence/sequence.c index 8b5303553702..f30317d1feae 100644 --- a/src/backend/access/sequence/sequence.c +++ b/src/backend/access/sequence/sequence.c @@ -13,7 +13,8 @@ * * NOTES * This file contains sequence_ routines that implement access to sequences - * (in contrast to other relation types like indexes). + * (in contrast to other relation types like indexes) that are independent + * of individual sequence access methods. * *------------------------------------------------------------------------- */ diff --git a/src/backend/access/sequence/sequenceamapi.c b/src/backend/access/sequence/sequenceamapi.c new file mode 100644 index 000000000000..dd1a60d827a4 --- /dev/null +++ b/src/backend/access/sequence/sequenceamapi.c @@ -0,0 +1,145 @@ +/*------------------------------------------------------------------------- + * + * sequenceamapi.c + * general sequence access method routines + * + * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/access/sequence/sequenceamapi.c + * + * + * Sequence access method allows the SQL Standard Sequence objects to be + * managed according to either the default access method or a pluggable + * replacement. Each sequence can only use one access method at a time, + * though different sequence access methods can be in use by different + * sequences at the same time. + * + * ------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "access/xact.h" +#include "access/sequenceam.h" +#include "catalog/pg_am.h" +#include "commands/defrem.h" +#include "miscadmin.h" +#include "utils/guc_hooks.h" +#include "utils/syscache.h" + + +/* GUC */ +char *default_sequence_access_method = DEFAULT_SEQUENCE_ACCESS_METHOD; + +/* + * GetSequenceAmRoutine + * Call the specified access method handler routine to get its + * SequenceAmRoutine struct, which will be palloc'd in the caller's + * memory context. + */ +const SequenceAmRoutine * +GetSequenceAmRoutine(Oid amhandler) +{ + Datum datum; + SequenceAmRoutine *routine; + + datum = OidFunctionCall0(amhandler); + routine = (SequenceAmRoutine *) DatumGetPointer(datum); + + if (routine == NULL || !IsA(routine, SequenceAmRoutine)) + elog(ERROR, "sequence access method handler %u did not return a SequenceAmRoutine struct", + amhandler); + + /* + * Assert that all required callbacks are present. That makes it a bit + * easier to keep AMs up to date, e.g. when forward porting them to a new + * major version. + */ + Assert(routine->get_table_am != NULL); + Assert(routine->init != NULL); + Assert(routine->nextval != NULL); + Assert(routine->setval != NULL); + Assert(routine->reset != NULL); + Assert(routine->get_state != NULL); + Assert(routine->change_persistence != NULL); + + return routine; +} + +/* + * GetSequenceAmRoutineId + * Call pg_am and retrieve the OID of the access method handler. + */ +Oid +GetSequenceAmRoutineId(Oid amoid) +{ + Oid amhandleroid; + HeapTuple tuple; + Form_pg_am aform; + + tuple = SearchSysCache1(AMOID, + ObjectIdGetDatum(amoid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for access method %u", amoid); + aform = (Form_pg_am) GETSTRUCT(tuple); + Assert(aform->amtype == AMTYPE_SEQUENCE); + amhandleroid = aform->amhandler; + ReleaseSysCache(tuple); + + return amhandleroid; +} + +/* check_hook: validate new default_sequence_access_method */ +bool +check_default_sequence_access_method(char **newval, void **extra, + GucSource source) +{ + if (**newval == '\0') + { + GUC_check_errdetail("%s cannot be empty.", + "default_sequence_access_method"); + return false; + } + + if (strlen(*newval) >= NAMEDATALEN) + { + GUC_check_errdetail("%s is too long (maximum %d characters).", + "default_sequence_access_method", NAMEDATALEN - 1); + return false; + } + + /* + * If we aren't inside a transaction, or not connected to a database, we + * cannot do the catalog access necessary to verify the method. Must + * accept the value on faith. + */ + if (IsTransactionState() && MyDatabaseId != InvalidOid) + { + if (!OidIsValid(get_sequence_am_oid(*newval, true))) + { + /* + * When source == PGC_S_TEST, don't throw a hard error for a + * nonexistent sequence access method, only a NOTICE. See comments + * in guc.h. + */ + if (source == PGC_S_TEST) + { + ereport(NOTICE, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("sequence access method \"%s\" does not exist", + *newval))); + } + else + { + GUC_check_errdetail("sequence access method \"%s\" does not exist.", + *newval); + return false; + } + } + } + + return true; +} diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index bd3554c0bfdc..14780b4f825c 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -1480,9 +1480,13 @@ heap_create_with_catalog(const char *relname, * No need to add an explicit dependency for the toast table, as the * main table depends on it. Partitioned tables may not have an * access method set. + * + * Sequences and tables are created with their access method ID + * given by the caller of this function. */ if ((RELKIND_HAS_TABLE_AM(relkind) && relkind != RELKIND_TOASTVALUE) || - (relkind == RELKIND_PARTITIONED_TABLE && OidIsValid(accessmtd))) + (relkind == RELKIND_PARTITIONED_TABLE && OidIsValid(accessmtd)) || + RELKIND_HAS_SEQUENCE_AM(relkind)) { ObjectAddressSet(referenced, AccessMethodRelationId, accessmtd); add_exact_object_address(&referenced, addrs); diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c index 58ed9d216cc0..e44633d13b60 100644 --- a/src/backend/commands/amcmds.c +++ b/src/backend/commands/amcmds.c @@ -15,6 +15,7 @@ #include "access/htup_details.h" #include "access/table.h" +#include "access/sequenceam.h" #include "catalog/catalog.h" #include "catalog/dependency.h" #include "catalog/indexing.h" @@ -175,6 +176,16 @@ get_table_am_oid(const char *amname, bool missing_ok) return get_am_type_oid(amname, AMTYPE_TABLE, missing_ok); } +/* + * get_sequence_am_oid - given an access method name, look up its OID + * and verify it corresponds to an sequence AM. + */ +Oid +get_sequence_am_oid(const char *amname, bool missing_ok) +{ + return get_am_type_oid(amname, AMTYPE_SEQUENCE, missing_ok); +} + /* * get_am_oid - given an access method name, look up its OID. * The type is not checked. @@ -215,6 +226,8 @@ get_am_type_string(char amtype) { case AMTYPE_INDEX: return "INDEX"; + case AMTYPE_SEQUENCE: + return "SEQUENCE"; case AMTYPE_TABLE: return "TABLE"; default: @@ -251,6 +264,9 @@ lookup_am_handler_func(List *handler_name, char amtype) case AMTYPE_INDEX: expectedType = INDEX_AM_HANDLEROID; break; + case AMTYPE_SEQUENCE: + expectedType = SEQUENCE_AM_HANDLEROID; + break; case AMTYPE_TABLE: expectedType = TABLE_AM_HANDLEROID; break; diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index b7ed47d92801..e88ace9b4a4d 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -16,10 +16,10 @@ #include "access/bufmask.h" #include "access/htup_details.h" -#include "access/seqlocalam.h" #include "access/multixact.h" #include "access/relation.h" #include "access/sequence.h" +#include "access/sequenceam.h" #include "access/table.h" #include "access/transam.h" #include "access/xact.h" @@ -152,6 +152,7 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) stmt->inhRelations = NIL; stmt->constraints = NIL; stmt->options = NIL; + stmt->accessMethod = seq->accessMethod ? pstrdup(seq->accessMethod) : NULL; stmt->oncommit = ONCOMMIT_NOOP; stmt->tablespacename = NULL; stmt->if_not_exists = seq->if_not_exists; @@ -173,7 +174,7 @@ DefineSequence(ParseState *pstate, CreateSeqStmt *seq) rel = sequence_open(seqoid, AccessExclusiveLock); /* now initialize the sequence table structure and its data */ - seq_local_init(rel, last_value, is_called); + sequence_init(rel, last_value, is_called); /* process OWNED BY if given */ if (owned_by) @@ -241,7 +242,7 @@ ResetSequence(Oid seq_relid) ReleaseSysCache(pgstuple); /* Sequence state is forcibly reset here. */ - seq_local_reset(seq_rel, startv, false, true); + sequence_reset(seq_rel, startv, false, true); /* Clear local cache so that we don't think we have cached numbers */ /* Note that we do not change the currval() state */ @@ -297,7 +298,7 @@ AlterSequence(ParseState *pstate, AlterSeqStmt *stmt) seqform = (Form_pg_sequence) GETSTRUCT(seqtuple); /* Read sequence data */ - seq_local_get_state(seqrel, &last_value, &is_called); + sequence_get_state(seqrel, &last_value, &is_called); /* Check and set new values */ init_params(pstate, stmt->options, stmt->for_identity, false, @@ -310,7 +311,7 @@ AlterSequence(ParseState *pstate, AlterSeqStmt *stmt) if (RelationNeedsWAL(seqrel)) GetTopTransactionId(); - seq_local_reset(seqrel, last_value, is_called, reset_state); + sequence_reset(seqrel, last_value, is_called, reset_state); } /* Clear local cache so that we don't think we have cached numbers */ @@ -353,7 +354,7 @@ SequenceChangePersistence(Oid relid, char newrelpersistence) if (RelationNeedsWAL(seqrel)) GetTopTransactionId(); - seq_local_change_persistence(seqrel, newrelpersistence); + sequence_change_persistence(seqrel, newrelpersistence); sequence_close(seqrel, NoLock); } @@ -470,8 +471,8 @@ nextval_internal(Oid relid, bool check_permissions) ReleaseSysCache(pgstuple); /* retrieve next value from the access method */ - result = seq_local_nextval(seqrel, incby, maxv, minv, cache, cycle, - &last); + result = sequence_nextval(seqrel, incby, maxv, minv, cache, cycle, + &last); /* save info in local cache */ elm->increment = incby; @@ -625,7 +626,7 @@ do_setval(Oid relid, int64 next, bool iscalled) GetTopTransactionId(); /* Call the access method callback */ - seq_local_setval(seqrel, next, iscalled); + sequence_setval(seqrel, next, iscalled); sequence_close(seqrel, NoLock); } @@ -1353,7 +1354,7 @@ pg_get_sequence_data(PG_FUNCTION_ARGS) bool is_called; int64 last_value; - seq_local_get_state(seqrel, &last_value, &is_called); + sequence_get_state(seqrel, &last_value, &is_called); values[0] = Int64GetDatum(last_value); values[1] = BoolGetDatum(is_called); @@ -1400,7 +1401,7 @@ pg_sequence_last_value(PG_FUNCTION_ARGS) !RELATION_IS_OTHER_TEMP(seqrel) && (RelationIsPermanent(seqrel) || !RecoveryInProgress())) { - seq_local_get_state(seqrel, &result, &is_called); + sequence_get_state(seqrel, &result, &is_called); } sequence_close(seqrel, NoLock); diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 04ee8ed38be1..eb74bd1beafd 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -23,6 +23,7 @@ #include "access/reloptions.h" #include "access/relscan.h" #include "access/sysattr.h" +#include "access/sequenceam.h" #include "access/tableam.h" #include "access/toast_compression.h" #include "access/xact.h" @@ -1005,14 +1006,18 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, } /* - * For relations with table AM and partitioned tables, select access - * method to use: an explicitly indicated one, or (in the case of a + * For relations with table AM, partitioned tables or sequences, select + * access method to use: an explicitly indicated one, or (in the case of a * partitioned table) the parent's, if it has one. */ if (stmt->accessMethod != NULL) { - Assert(RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE); - accessMethodId = get_table_am_oid(stmt->accessMethod, false); + Assert(RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE || + RELKIND_HAS_SEQUENCE_AM(relkind)); + if (RELKIND_HAS_SEQUENCE_AM(relkind)) + accessMethodId = get_sequence_am_oid(stmt->accessMethod, false); + else + accessMethodId = get_table_am_oid(stmt->accessMethod, false); } else if (RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE) { @@ -1025,6 +1030,10 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, if (RELKIND_HAS_TABLE_AM(relkind) && !OidIsValid(accessMethodId)) accessMethodId = get_table_am_oid(default_table_access_method, false); } + else if (RELKIND_HAS_SEQUENCE_AM(relkind)) + { + accessMethodId = get_sequence_am_oid(default_sequence_access_method, false); + } /* * Create the relation. Inherited defaults and CHECK constraints are diff --git a/src/backend/nodes/Makefile b/src/backend/nodes/Makefile index 77ddb9ca53f1..64d4dccc936f 100644 --- a/src/backend/nodes/Makefile +++ b/src/backend/nodes/Makefile @@ -48,6 +48,7 @@ node_headers = \ access/amapi.h \ access/cmptype.h \ access/sdir.h \ + access/sequenceam.h \ access/tableam.h \ access/tsmapi.h \ commands/event_trigger.h \ diff --git a/src/backend/nodes/gen_node_support.pl b/src/backend/nodes/gen_node_support.pl index f6229089cd15..ee049b763ae2 100644 --- a/src/backend/nodes/gen_node_support.pl +++ b/src/backend/nodes/gen_node_support.pl @@ -60,6 +60,7 @@ my @all_input_files = qw( access/amapi.h access/cmptype.h access/sdir.h + access/sequenceam.h access/tableam.h access/tsmapi.h commands/event_trigger.h @@ -84,6 +85,7 @@ my @nodetag_only_files = qw( nodes/execnodes.h access/amapi.h access/sdir.h + access/sequenceam.h access/tableam.h access/tsmapi.h commands/event_trigger.h diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 0fc502a3a406..95c4fdfe956b 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -381,6 +381,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type copy_file_name access_method_clause attr_name table_access_method_clause name cursor_name file_name + sequence_access_method_clause cluster_index_specification %type func_name handler_name qual_Op qual_all_Op subquery_Op @@ -4937,23 +4938,26 @@ RefreshMatViewStmt: CreateSeqStmt: CREATE OptTemp SEQUENCE qualified_name OptSeqOptList + sequence_access_method_clause { CreateSeqStmt *n = makeNode(CreateSeqStmt); - $4->relpersistence = $2; n->sequence = $4; n->options = $5; + n->accessMethod = $6; n->ownerId = InvalidOid; n->if_not_exists = false; $$ = (Node *) n; } | CREATE OptTemp SEQUENCE IF_P NOT EXISTS qualified_name OptSeqOptList + sequence_access_method_clause { CreateSeqStmt *n = makeNode(CreateSeqStmt); $7->relpersistence = $2; n->sequence = $7; n->options = $8; + n->accessMethod = $9; n->ownerId = InvalidOid; n->if_not_exists = true; $$ = (Node *) n; @@ -4990,6 +4994,11 @@ OptParenthesizedSeqOptList: '(' SeqOptList ')' { $$ = $2; } | /*EMPTY*/ { $$ = NIL; } ; +sequence_access_method_clause: + USING name { $$ = $2; } + | /*EMPTY*/ { $$ = NULL; } + ; + SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; @@ -5993,6 +6002,7 @@ CreateAmStmt: CREATE ACCESS METHOD name TYPE_P am_type HANDLER handler_name am_type: INDEX { $$ = AMTYPE_INDEX; } + | SEQUENCE { $$ = AMTYPE_SEQUENCE; } | TABLE { $$ = AMTYPE_TABLE; } ; diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 896a7f2c59b4..8eafbe88cb34 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -26,6 +26,7 @@ #include "access/htup_details.h" #include "access/relation.h" #include "access/reloptions.h" +#include "access/sequenceam.h" #include "access/table.h" #include "access/toast_compression.h" #include "catalog/dependency.h" @@ -518,6 +519,7 @@ generateSerialExtraStmts(CreateStmtContext *cxt, ColumnDef *column, seqstmt->sequence = makeRangeVar(snamespace, sname, -1); seqstmt->sequence->relpersistence = seqpersistence; seqstmt->options = seqoptions; + seqstmt->accessMethod = NULL; /* * If a sequence data type was specified, add it to the options. Prepend diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c index 317a1f2b282f..68f160dda7d3 100644 --- a/src/backend/utils/adt/pseudotypes.c +++ b/src/backend/utils/adt/pseudotypes.c @@ -369,6 +369,7 @@ PSEUDOTYPE_DUMMY_IO_FUNCS(language_handler); PSEUDOTYPE_DUMMY_IO_FUNCS(fdw_handler); PSEUDOTYPE_DUMMY_IO_FUNCS(table_am_handler); PSEUDOTYPE_DUMMY_IO_FUNCS(index_am_handler); +PSEUDOTYPE_DUMMY_IO_FUNCS(sequence_am_handler); PSEUDOTYPE_DUMMY_IO_FUNCS(tsm_handler); PSEUDOTYPE_DUMMY_IO_FUNCS(internal); PSEUDOTYPE_DUMMY_IO_FUNCS(anyelement); diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 9f54a9e72b73..708d0f7a46b5 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -34,6 +34,7 @@ #include "access/multixact.h" #include "access/parallel.h" #include "access/reloptions.h" +#include "access/sequenceam.h" #include "access/sysattr.h" #include "access/table.h" #include "access/tableam.h" @@ -64,6 +65,7 @@ #include "catalog/pg_type.h" #include "catalog/schemapg.h" #include "catalog/storage.h" +#include "commands/defrem.h" #include "commands/policy.h" #include "commands/publicationcmds.h" #include "commands/trigger.h" @@ -302,6 +304,7 @@ static void RelationParseRelOptions(Relation relation, HeapTuple tuple); static void RelationBuildTupleDesc(Relation relation); static Relation RelationBuildDesc(Oid targetRelId, bool insertIt); static void RelationInitPhysicalAddr(Relation relation); +static void RelationInitSequenceAccessMethod(Relation relation); static void load_critical_index(Oid indexoid, Oid heapoid); static TupleDesc GetPgClassDescriptor(void); static TupleDesc GetPgIndexDescriptor(void); @@ -1199,8 +1202,7 @@ retry: if (relation->rd_rel->relkind == RELKIND_INDEX || relation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX) RelationInitIndexAccessInfo(relation); - else if (RELKIND_HAS_TABLE_AM(relation->rd_rel->relkind) || - relation->rd_rel->relkind == RELKIND_SEQUENCE) + else if (RELKIND_HAS_TABLE_AM(relation->rd_rel->relkind)) RelationInitTableAccessMethod(relation); else if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) { @@ -1209,6 +1211,8 @@ retry: * inherit. */ } + else if (RELKIND_HAS_SEQUENCE_AM(relation->rd_rel->relkind)) + RelationInitSequenceAccessMethod(relation); else Assert(relation->rd_rel->relam == InvalidOid); @@ -1805,17 +1809,9 @@ RelationInitTableAccessMethod(Relation relation) HeapTuple tuple; Form_pg_am aform; - if (relation->rd_rel->relkind == RELKIND_SEQUENCE) - { - /* - * Sequences are currently accessed like heap tables, but it doesn't - * seem prudent to show that in the catalog. So just overwrite it - * here. - */ - Assert(relation->rd_rel->relam == InvalidOid); - relation->rd_amhandler = F_HEAP_TABLEAM_HANDLER; - } - else if (IsCatalogRelation(relation)) + Assert(RELKIND_HAS_TABLE_AM(relation->rd_rel->relkind)); + + if (IsCatalogRelation(relation)) { /* * Avoid doing a syscache lookup for catalog tables. @@ -1846,6 +1842,49 @@ RelationInitTableAccessMethod(Relation relation) InitTableAmRoutine(relation); } +/* + * Initialize sequence-access-method support data for a sequence relation + */ +static void +RelationInitSequenceAccessMethod(Relation relation) +{ + HeapTuple tuple; + Form_pg_am aform; + const char *tableam_name; + Oid tableam_oid; + Oid tableam_handler; + + Assert(RELKIND_HAS_SEQUENCE_AM(relation->rd_rel->relkind)); + + /* + * Look up the sequence access method, save the OID of its handler + * function. + */ + Assert(relation->rd_rel->relam != InvalidOid); + relation->rd_amhandler = GetSequenceAmRoutineId(relation->rd_rel->relam); + + /* + * Now we can fetch the sequence AM's API struct. + */ + relation->rd_sequenceam = GetSequenceAmRoutine(relation->rd_amhandler); + + /* + * From the sequence AM, set its expected table access method. + */ + tableam_name = sequence_get_table_am(relation); + tableam_oid = get_table_am_oid(tableam_name, false); + + tuple = SearchSysCache1(AMOID, ObjectIdGetDatum(tableam_oid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for access method %u", + tableam_oid); + aform = (Form_pg_am) GETSTRUCT(tuple); + tableam_handler = aform->amhandler; + ReleaseSysCache(tuple); + + relation->rd_tableam = GetTableAmRoutine(tableam_handler); +} + /* * formrdesc * @@ -3663,14 +3702,17 @@ RelationBuildLocalRelation(const char *relname, rel->rd_rel->relam = accessmtd; /* - * RelationInitTableAccessMethod will do syscache lookups, so we mustn't - * run it in CacheMemoryContext. Fortunately, the remaining steps don't - * require a long-lived current context. + * RelationInitTableAccessMethod() and RelationInitSequenceAccessMethod() + * will do syscache lookups, so we mustn't run them in CacheMemoryContext. + * Fortunately, the remaining steps don't require a long-lived current + * context. */ MemoryContextSwitchTo(oldcxt); - if (RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_SEQUENCE) + if (RELKIND_HAS_TABLE_AM(relkind)) RelationInitTableAccessMethod(rel); + else if (relkind == RELKIND_SEQUENCE) + RelationInitSequenceAccessMethod(rel); /* * Leave index access method uninitialized, because the pg_index row has @@ -4295,13 +4337,21 @@ RelationCacheInitializePhase3(void) /* Reload tableam data if needed */ if (relation->rd_tableam == NULL && - (RELKIND_HAS_TABLE_AM(relation->rd_rel->relkind) || relation->rd_rel->relkind == RELKIND_SEQUENCE)) + (RELKIND_HAS_TABLE_AM(relation->rd_rel->relkind))) { RelationInitTableAccessMethod(relation); Assert(relation->rd_tableam != NULL); restart = true; } + else if (relation->rd_sequenceam == NULL && + relation->rd_rel->relkind == RELKIND_SEQUENCE) + { + RelationInitSequenceAccessMethod(relation); + Assert(relation->rd_sequenceam != NULL); + + restart = true; + } /* Release hold on the relation */ RelationDecrementReferenceCount(relation); @@ -6353,8 +6403,10 @@ load_relcache_init_file(bool shared) nailed_rels++; /* Load table AM data */ - if (RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind) || rel->rd_rel->relkind == RELKIND_SEQUENCE) + if (RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind)) RelationInitTableAccessMethod(rel); + else if (rel->rd_rel->relkind == RELKIND_SEQUENCE) + RelationInitSequenceAccessMethod(rel); Assert(rel->rd_index == NULL); Assert(rel->rd_indextuple == NULL); @@ -6366,6 +6418,7 @@ load_relcache_init_file(bool shared) Assert(rel->rd_supportinfo == NULL); Assert(rel->rd_indoption == NULL); Assert(rel->rd_indcollation == NULL); + Assert(rel->rd_sequenceam == NULL); Assert(rel->rd_opcoptions == NULL); } diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 76c7c6bb4b17..1380ae59d792 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -30,6 +30,7 @@ #include "access/gin.h" #include "access/slru.h" #include "access/toast_compression.h" +#include "access/sequenceam.h" #include "access/twophase.h" #include "access/xlog_internal.h" #include "access/xlogprefetcher.h" @@ -4329,6 +4330,17 @@ struct config_string ConfigureNamesString[] = check_default_table_access_method, NULL, NULL }, + { + {"default_sequence_access_method", PGC_USERSET, CLIENT_CONN_STATEMENT, + gettext_noop("Sets the default sequence access method for new sequences."), + NULL, + GUC_IS_NAME + }, + &default_sequence_access_method, + DEFAULT_SEQUENCE_ACCESS_METHOD, + check_default_sequence_access_method, NULL, NULL + }, + { {"default_tablespace", PGC_USERSET, CLIENT_CONN_STATEMENT, gettext_noop("Sets the default tablespace to create tables and indexes in."), diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index 7c12434efa2f..11df2d8a5bdd 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -748,6 +748,7 @@ autovacuum_worker_slots = 16 # autovacuum worker slots to allocate # error #search_path = '"$user", public' # schema names #row_security = on +#default_sequence_access_method = 'seqlocal' #default_table_access_method = 'heap' #default_tablespace = '' # a tablespace name, '' uses the default #default_toast_compression = 'pglz' # 'pglz' or 'lz4' diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index bf565afcc4ef..cce7c76fbbdf 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -168,10 +168,12 @@ describeAccessMethods(const char *pattern, bool verbose) "SELECT amname AS \"%s\",\n" " CASE amtype" " WHEN " CppAsString2(AMTYPE_INDEX) " THEN '%s'" + " WHEN " CppAsString2(AMTYPE_SEQUENCE) " THEN '%s'" " WHEN " CppAsString2(AMTYPE_TABLE) " THEN '%s'" " END AS \"%s\"", gettext_noop("Name"), gettext_noop("Index"), + gettext_noop("Sequence"), gettext_noop("Table"), gettext_noop("Type")); diff --git a/src/bin/psql/tab-complete.in.c b/src/bin/psql/tab-complete.in.c index 98951aef82ca..f122e64e6305 100644 --- a/src/bin/psql/tab-complete.in.c +++ b/src/bin/psql/tab-complete.in.c @@ -2562,7 +2562,7 @@ match_previous_words(int pattern_id, else if (Matches("ALTER", "SEQUENCE", MatchAny)) COMPLETE_WITH("AS", "INCREMENT", "MINVALUE", "MAXVALUE", "RESTART", "START", "NO", "CACHE", "CYCLE", "SET", "OWNED BY", - "OWNER TO", "RENAME TO"); + "OWNER TO", "RENAME TO", "USING"); /* ALTER SEQUENCE AS */ else if (TailMatches("ALTER", "SEQUENCE", MatchAny, "AS")) COMPLETE_WITH_CS("smallint", "integer", "bigint"); @@ -3605,7 +3605,7 @@ match_previous_words(int pattern_id, else if (TailMatches("CREATE", "SEQUENCE", MatchAny) || TailMatches("CREATE", "TEMP|TEMPORARY", "SEQUENCE", MatchAny)) COMPLETE_WITH("AS", "INCREMENT BY", "MINVALUE", "MAXVALUE", "NO", - "CACHE", "CYCLE", "OWNED BY", "START WITH"); + "CACHE", "CYCLE", "OWNED BY", "START WITH", "USING"); else if (TailMatches("CREATE", "SEQUENCE", MatchAny, "AS") || TailMatches("CREATE", "TEMP|TEMPORARY", "SEQUENCE", MatchAny, "AS")) COMPLETE_WITH_CS("smallint", "integer", "bigint"); diff --git a/src/test/regress/expected/create_am.out b/src/test/regress/expected/create_am.out index c1a951572512..784870e603d1 100644 --- a/src/test/regress/expected/create_am.out +++ b/src/test/regress/expected/create_am.out @@ -163,11 +163,6 @@ CREATE VIEW tableam_view_heap2 USING heap2 AS SELECT * FROM tableam_tbl_heap2; ERROR: syntax error at or near "USING" LINE 1: CREATE VIEW tableam_view_heap2 USING heap2 AS SELECT * FROM ... ^ --- CREATE SEQUENCE doesn't support USING -CREATE SEQUENCE tableam_seq_heap2 USING heap2; -ERROR: syntax error at or near "USING" -LINE 1: CREATE SEQUENCE tableam_seq_heap2 USING heap2; - ^ -- CREATE MATERIALIZED VIEW does support USING CREATE MATERIALIZED VIEW tableam_tblmv_heap2 USING heap2 AS SELECT * FROM tableam_tbl_heap2; SELECT f1 FROM tableam_tblmv_heap2 ORDER BY f1; @@ -514,9 +509,12 @@ CREATE TABLE tableam_parted_heapx (a text, b int) PARTITION BY list (a); CREATE TABLE tableam_parted_1_heapx PARTITION OF tableam_parted_heapx FOR VALUES IN ('a', 'b'); -- but an explicitly set AM overrides it CREATE TABLE tableam_parted_2_heapx PARTITION OF tableam_parted_heapx FOR VALUES IN ('c', 'd') USING heap; --- sequences, views and foreign servers shouldn't have an AM -CREATE VIEW tableam_view_heapx AS SELECT * FROM tableam_tbl_heapx; +-- sequences have an AM +SET LOCAL default_sequence_access_method = 'seqlocal'; CREATE SEQUENCE tableam_seq_heapx; +RESET default_sequence_access_method; +-- views and foreign servers shouldn't have an AM +CREATE VIEW tableam_view_heapx AS SELECT * FROM tableam_tbl_heapx; CREATE FOREIGN DATA WRAPPER fdw_heap2 VALIDATOR postgresql_fdw_validator; CREATE SERVER fs_heap2 FOREIGN DATA WRAPPER fdw_heap2 ; CREATE FOREIGN table tableam_fdw_heapx () SERVER fs_heap2; @@ -533,18 +531,18 @@ FROM pg_class AS pc LEFT JOIN pg_am AS pa ON (pa.oid = pc.relam) WHERE pc.relname LIKE 'tableam_%_heapx' ORDER BY 3, 1, 2; - relkind | amname | relname ----------+--------+----------------------------- - f | | tableam_fdw_heapx - r | heap2 | tableam_parted_1_heapx - r | heap | tableam_parted_2_heapx - p | | tableam_parted_heapx - S | | tableam_seq_heapx - r | heap2 | tableam_tbl_heapx - r | heap2 | tableam_tblas_heapx - m | heap2 | tableam_tblmv_heapx - r | heap2 | tableam_tblselectinto_heapx - v | | tableam_view_heapx + relkind | amname | relname +---------+----------+----------------------------- + f | | tableam_fdw_heapx + r | heap2 | tableam_parted_1_heapx + r | heap | tableam_parted_2_heapx + p | | tableam_parted_heapx + S | seqlocal | tableam_seq_heapx + r | heap2 | tableam_tbl_heapx + r | heap2 | tableam_tblas_heapx + m | heap2 | tableam_tblmv_heapx + r | heap2 | tableam_tblselectinto_heapx + v | | tableam_view_heapx (10 rows) -- don't want to keep those tables, nor the default @@ -574,3 +572,22 @@ table tableam_parted_b_heap2 depends on access method heap2 table tableam_parted_d_heap2 depends on access method heap2 HINT: Use DROP ... CASCADE to drop the dependent objects too. -- we intentionally leave the objects created above alive, to verify pg_dump support +-- Checks for sequence access methods +-- Create new sequence access method which uses standard local handler +CREATE ACCESS METHOD local2 TYPE SEQUENCE HANDLER seq_local_sequenceam_handler; +-- Create and use sequence +CREATE SEQUENCE test_seqam USING local2; +SELECT nextval('test_seqam'::regclass); + nextval +--------- + 1 +(1 row) + +-- Try to drop and fail on dependency +DROP ACCESS METHOD local2; +ERROR: cannot drop access method local2 because other objects depend on it +DETAIL: sequence test_seqam depends on access method local2 +HINT: Use DROP ... CASCADE to drop the dependent objects too. +-- And cleanup +DROP SEQUENCE test_seqam; +DROP ACCESS METHOD local2; diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index 20bf9ea9cdf7..20b3d8520315 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -1943,6 +1943,18 @@ WHERE p1.oid = a1.amhandler AND a1.amtype = 't' AND -----+--------+-----+--------- (0 rows) +-- check for sequence amhandler functions with the wrong signature +SELECT a1.oid, a1.amname, p1.oid, p1.proname +FROM pg_am AS a1, pg_proc AS p1 +WHERE p1.oid = a1.amhandler AND a1.amtype = 's' AND + (p1.prorettype != 'sequence_am_handler'::regtype + OR p1.proretset + OR p1.pronargs != 1 + OR p1.proargtypes[0] != 'internal'::regtype); + oid | amname | oid | proname +-----+--------+-----+--------- +(0 rows) + -- **************** pg_amop **************** -- Look for illegal values in pg_amop fields SELECT a1.amopfamily, a1.amopstrategy diff --git a/src/test/regress/expected/psql.out b/src/test/regress/expected/psql.out index b1d12585eaed..2f061931eb38 100644 --- a/src/test/regress/expected/psql.out +++ b/src/test/regress/expected/psql.out @@ -5127,31 +5127,33 @@ Indexes: -- check printing info about access methods \dA List of access methods - Name | Type ---------+------- - brin | Index - btree | Index - gin | Index - gist | Index - hash | Index - heap | Table - heap2 | Table - spgist | Index -(8 rows) + Name | Type +----------+---------- + brin | Index + btree | Index + gin | Index + gist | Index + hash | Index + heap | Table + heap2 | Table + seqlocal | Sequence + spgist | Index +(9 rows) \dA * List of access methods - Name | Type ---------+------- - brin | Index - btree | Index - gin | Index - gist | Index - hash | Index - heap | Table - heap2 | Table - spgist | Index -(8 rows) + Name | Type +----------+---------- + brin | Index + btree | Index + gin | Index + gist | Index + hash | Index + heap | Table + heap2 | Table + seqlocal | Sequence + spgist | Index +(9 rows) \dA h* List of access methods @@ -5176,32 +5178,34 @@ List of access methods \dA: extra argument "bar" ignored \dA+ - List of access methods - Name | Type | Handler | Description ---------+-------+----------------------+---------------------------------------- - brin | Index | brinhandler | block range index (BRIN) access method - btree | Index | bthandler | b-tree index access method - gin | Index | ginhandler | GIN index access method - gist | Index | gisthandler | GiST index access method - hash | Index | hashhandler | hash index access method - heap | Table | heap_tableam_handler | heap table access method - heap2 | Table | heap_tableam_handler | - spgist | Index | spghandler | SP-GiST index access method -(8 rows) + List of access methods + Name | Type | Handler | Description +----------+----------+------------------------------+---------------------------------------- + brin | Index | brinhandler | block range index (BRIN) access method + btree | Index | bthandler | b-tree index access method + gin | Index | ginhandler | GIN index access method + gist | Index | gisthandler | GiST index access method + hash | Index | hashhandler | hash index access method + heap | Table | heap_tableam_handler | heap table access method + heap2 | Table | heap_tableam_handler | + seqlocal | Sequence | seq_local_sequenceam_handler | local sequence access method + spgist | Index | spghandler | SP-GiST index access method +(9 rows) \dA+ * - List of access methods - Name | Type | Handler | Description ---------+-------+----------------------+---------------------------------------- - brin | Index | brinhandler | block range index (BRIN) access method - btree | Index | bthandler | b-tree index access method - gin | Index | ginhandler | GIN index access method - gist | Index | gisthandler | GiST index access method - hash | Index | hashhandler | hash index access method - heap | Table | heap_tableam_handler | heap table access method - heap2 | Table | heap_tableam_handler | - spgist | Index | spghandler | SP-GiST index access method -(8 rows) + List of access methods + Name | Type | Handler | Description +----------+----------+------------------------------+---------------------------------------- + brin | Index | brinhandler | block range index (BRIN) access method + btree | Index | bthandler | b-tree index access method + gin | Index | ginhandler | GIN index access method + gist | Index | gisthandler | GiST index access method + hash | Index | hashhandler | hash index access method + heap | Table | heap_tableam_handler | heap table access method + heap2 | Table | heap_tableam_handler | + seqlocal | Sequence | seq_local_sequenceam_handler | local sequence access method + spgist | Index | spghandler | SP-GiST index access method +(9 rows) \dA+ h* List of access methods diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out index 8eff3d10d279..622f360d5b3c 100644 --- a/src/test/regress/expected/type_sanity.out +++ b/src/test/regress/expected/type_sanity.out @@ -507,21 +507,21 @@ WHERE relkind NOT IN ('r', 'i', 'S', 't', 'v', 'm', 'c', 'f', 'p', 'I') OR -----+--------- (0 rows) --- All tables, indexes, partitioned indexes and matviews should have an --- access method. +-- All tables, indexes, partitioned indexes, matviews and sequences should have +-- an access method. SELECT c1.oid, c1.relname FROM pg_class as c1 -WHERE c1.relkind NOT IN ('S', 'v', 'f', 'c', 'p') and +WHERE c1.relkind NOT IN ('v', 'f', 'c', 'p') and c1.relam = 0; oid | relname -----+--------- (0 rows) --- Conversely, sequences, views, foreign tables, types and partitioned --- tables shouldn't have them. +-- Conversely, views, foreign tables, types and partitioned tables +-- shouldn't have them. SELECT c1.oid, c1.relname FROM pg_class as c1 -WHERE c1.relkind IN ('S', 'v', 'f', 'c', 'p') and +WHERE c1.relkind IN ('v', 'f', 'c', 'p') and c1.relam != 0; oid | relname -----+--------- diff --git a/src/test/regress/sql/create_am.sql b/src/test/regress/sql/create_am.sql index 754fe0c694bc..76a91cf8dd68 100644 --- a/src/test/regress/sql/create_am.sql +++ b/src/test/regress/sql/create_am.sql @@ -117,9 +117,6 @@ SELECT INTO tableam_tblselectinto_heap2 USING heap2 FROM tableam_tbl_heap2; -- CREATE VIEW doesn't support USING CREATE VIEW tableam_view_heap2 USING heap2 AS SELECT * FROM tableam_tbl_heap2; --- CREATE SEQUENCE doesn't support USING -CREATE SEQUENCE tableam_seq_heap2 USING heap2; - -- CREATE MATERIALIZED VIEW does support USING CREATE MATERIALIZED VIEW tableam_tblmv_heap2 USING heap2 AS SELECT * FROM tableam_tbl_heap2; SELECT f1 FROM tableam_tblmv_heap2 ORDER BY f1; @@ -327,9 +324,13 @@ CREATE TABLE tableam_parted_1_heapx PARTITION OF tableam_parted_heapx FOR VALUES -- but an explicitly set AM overrides it CREATE TABLE tableam_parted_2_heapx PARTITION OF tableam_parted_heapx FOR VALUES IN ('c', 'd') USING heap; --- sequences, views and foreign servers shouldn't have an AM -CREATE VIEW tableam_view_heapx AS SELECT * FROM tableam_tbl_heapx; +-- sequences have an AM +SET LOCAL default_sequence_access_method = 'seqlocal'; CREATE SEQUENCE tableam_seq_heapx; +RESET default_sequence_access_method; + +-- views and foreign servers shouldn't have an AM +CREATE VIEW tableam_view_heapx AS SELECT * FROM tableam_tbl_heapx; CREATE FOREIGN DATA WRAPPER fdw_heap2 VALIDATOR postgresql_fdw_validator; CREATE SERVER fs_heap2 FOREIGN DATA WRAPPER fdw_heap2 ; CREATE FOREIGN table tableam_fdw_heapx () SERVER fs_heap2; @@ -365,3 +366,16 @@ CREATE FOREIGN TABLE fp PARTITION OF tableam_parted_a_heap2 DEFAULT SERVER x; DROP ACCESS METHOD heap2; -- we intentionally leave the objects created above alive, to verify pg_dump support + +-- Checks for sequence access methods + +-- Create new sequence access method which uses standard local handler +CREATE ACCESS METHOD local2 TYPE SEQUENCE HANDLER seq_local_sequenceam_handler; +-- Create and use sequence +CREATE SEQUENCE test_seqam USING local2; +SELECT nextval('test_seqam'::regclass); +-- Try to drop and fail on dependency +DROP ACCESS METHOD local2; +-- And cleanup +DROP SEQUENCE test_seqam; +DROP ACCESS METHOD local2; diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql index 2fb3a8528781..e60eeab33c02 100644 --- a/src/test/regress/sql/opr_sanity.sql +++ b/src/test/regress/sql/opr_sanity.sql @@ -1236,6 +1236,16 @@ WHERE p1.oid = a1.amhandler AND a1.amtype = 't' AND OR p1.pronargs != 1 OR p1.proargtypes[0] != 'internal'::regtype); +-- check for sequence amhandler functions with the wrong signature + +SELECT a1.oid, a1.amname, p1.oid, p1.proname +FROM pg_am AS a1, pg_proc AS p1 +WHERE p1.oid = a1.amhandler AND a1.amtype = 's' AND + (p1.prorettype != 'sequence_am_handler'::regtype + OR p1.proretset + OR p1.pronargs != 1 + OR p1.proargtypes[0] != 'internal'::regtype); + -- **************** pg_amop **************** -- Look for illegal values in pg_amop fields diff --git a/src/test/regress/sql/type_sanity.sql b/src/test/regress/sql/type_sanity.sql index 303f90955d15..aade05c0e172 100644 --- a/src/test/regress/sql/type_sanity.sql +++ b/src/test/regress/sql/type_sanity.sql @@ -364,18 +364,18 @@ WHERE relkind NOT IN ('r', 'i', 'S', 't', 'v', 'm', 'c', 'f', 'p', 'I') OR relpersistence NOT IN ('p', 'u', 't') OR relreplident NOT IN ('d', 'n', 'f', 'i'); --- All tables, indexes, partitioned indexes and matviews should have an --- access method. +-- All tables, indexes, partitioned indexes, matviews and sequences should have +-- an access method. SELECT c1.oid, c1.relname FROM pg_class as c1 -WHERE c1.relkind NOT IN ('S', 'v', 'f', 'c', 'p') and +WHERE c1.relkind NOT IN ('v', 'f', 'c', 'p') and c1.relam = 0; --- Conversely, sequences, views, foreign tables, types and partitioned --- tables shouldn't have them. +-- Conversely, views, foreign tables, types and partitioned tables +-- shouldn't have them. SELECT c1.oid, c1.relname FROM pg_class as c1 -WHERE c1.relkind IN ('S', 'v', 'f', 'c', 'p') and +WHERE c1.relkind IN ('v', 'f', 'c', 'p') and c1.relam != 0; -- Indexes and partitioned indexes should have AMs of type 'i'. diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 1279b69422a5..0cc7872dd81c 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -2654,6 +2654,7 @@ SeqScanState SeqTable SeqTableData SeqType +SequenceAmRoutine SequenceItem SerCommitSeqNo SerialControl @@ -3696,6 +3697,7 @@ list_sort_comparator loc_chunk local_relopt local_relopts +local_sequence_magic local_source local_ts_iter local_ts_radix_tree @@ -3984,7 +3986,6 @@ scram_state_enum script_error_callback_arg security_class_t sem_t -sepgsql_context_info_t sequence_magic set_join_pathlist_hook_type set_rel_pathlist_hook_type @@ -4209,6 +4210,7 @@ xl_heap_visible xl_invalid_page xl_invalid_page_key xl_invalidations +xl_local_seq_rec xl_logical_message xl_multi_insert_tuple xl_multixact_create @@ -4220,7 +4222,6 @@ xl_replorigin_drop xl_replorigin_set xl_restore_point xl_running_xacts -xl_seq_rec xl_smgr_create xl_smgr_truncate xl_standby_lock -- 2.49.0